summaryrefslogtreecommitdiffstats
path: root/tools/layoutlib
diff options
context:
space:
mode:
Diffstat (limited to 'tools/layoutlib')
-rw-r--r--tools/layoutlib/.gitignore4
-rw-r--r--tools/layoutlib/.idea/.name1
-rw-r--r--tools/layoutlib/.idea/codeStyleSettings.xml75
-rw-r--r--tools/layoutlib/.idea/compiler.xml27
-rw-r--r--tools/layoutlib/.idea/copyright/Android.xml9
-rw-r--r--tools/layoutlib/.idea/copyright/profiles_settings.xml3
-rw-r--r--tools/layoutlib/.idea/encodings.xml5
-rw-r--r--tools/layoutlib/.idea/inspectionProfiles/Project_Default.xml11
-rw-r--r--tools/layoutlib/.idea/inspectionProfiles/profiles_settings.xml7
-rw-r--r--tools/layoutlib/.idea/libraries/asm_4_0.xml11
-rw-r--r--tools/layoutlib/.idea/libraries/framework_jar.xml13
-rw-r--r--tools/layoutlib/.idea/libraries/guava.xml11
-rw-r--r--tools/layoutlib/.idea/libraries/icu4j.xml11
-rw-r--r--tools/layoutlib/.idea/libraries/kxml2_2_3_0.xml11
-rw-r--r--tools/layoutlib/.idea/libraries/layoutlib_api_prebuilt.xml11
-rw-r--r--tools/layoutlib/.idea/libraries/ninepatch_prebuilt.xml11
-rw-r--r--tools/layoutlib/.idea/libraries/tools_common_prebuilt.xml14
-rw-r--r--tools/layoutlib/.idea/misc.xml16
-rw-r--r--tools/layoutlib/.idea/modules.xml10
-rw-r--r--tools/layoutlib/.idea/runConfigurations/All_in_bridge.xml31
-rw-r--r--tools/layoutlib/.idea/runConfigurations/All_in_create.xml31
-rw-r--r--tools/layoutlib/.idea/runConfigurations/Create.xml25
-rw-r--r--tools/layoutlib/.idea/scopes/scope_settings.xml5
-rw-r--r--tools/layoutlib/.idea/uiDesigner.xml125
-rw-r--r--tools/layoutlib/.idea/vcs.xml7
-rw-r--r--tools/layoutlib/Android.mk9
-rw-r--r--tools/layoutlib/bridge/.classpath6
-rw-r--r--tools/layoutlib/bridge/Android.mk1
-rw-r--r--tools/layoutlib/bridge/bridge.iml48
-rw-r--r--tools/layoutlib/bridge/resources/bars/README8
-rw-r--r--tools/layoutlib/bridge/resources/bars/action_bar.xml7
-rw-r--r--tools/layoutlib/bridge/resources/bars/hdpi/stat_sys_battery_100.pngbin0 -> 19810 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/hdpi/stat_sys_battery_charge_anim100.pngbin1040 -> 0 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/hdpi/stat_sys_wifi_signal_4_fully.pngbin2436 -> 0 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/hdpi/status_bar_background.9.pngbin3233 -> 0 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_battery_100.pngbin0 -> 19396 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_battery_charge_anim100.pngbin762 -> 0 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_wifi_signal_4_fully.pngbin1430 -> 0 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/mdpi/status_bar_background.9.pngbin204 -> 0 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/status_bar.xml38
-rw-r--r--tools/layoutlib/bridge/resources/bars/v18/hdpi/stat_sys_battery_100.pngbin0 -> 604 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v18/hdpi/stat_sys_wifi_signal_4_fully.pngbin0 -> 1541 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v18/mdpi/stat_sys_battery_100.pngbin0 -> 515 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v18/mdpi/stat_sys_wifi_signal_4_fully.pngbin0 -> 1333 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v18/xhdpi/stat_sys_battery_100.pngbin0 -> 728 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v18/xhdpi/stat_sys_wifi_signal_4_fully.pngbin0 -> 1750 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v19/hdpi/ic_sysbar_back.png (renamed from tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_back.png)bin1053 -> 1053 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v19/hdpi/ic_sysbar_home.png (renamed from tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_home.png)bin1064 -> 1064 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v19/hdpi/ic_sysbar_recent.png (renamed from tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_recent.png)bin711 -> 711 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v19/hdpi/stat_sys_wifi_signal_4_fully.pngbin0 -> 1541 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v19/ldrtl-hdpi/ic_sysbar_back.png (renamed from tools/layoutlib/bridge/resources/bars/ldrtl-hdpi/ic_sysbar_back.png)bin904 -> 904 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v19/ldrtl-hdpi/ic_sysbar_recent.png (renamed from tools/layoutlib/bridge/resources/bars/ldrtl-hdpi/ic_sysbar_recent.png)bin533 -> 533 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v19/ldrtl-mdpi/ic_sysbar_back.png (renamed from tools/layoutlib/bridge/resources/bars/ldrtl-mdpi/ic_sysbar_back.png)bin617 -> 617 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v19/ldrtl-mdpi/ic_sysbar_recent.png (renamed from tools/layoutlib/bridge/resources/bars/ldrtl-mdpi/ic_sysbar_recent.png)bin423 -> 423 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v19/ldrtl-xhdpi/ic_sysbar_back.png (renamed from tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi/ic_sysbar_back.png)bin1250 -> 1250 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v19/ldrtl-xhdpi/ic_sysbar_recent.png (renamed from tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi/ic_sysbar_recent.png)bin552 -> 552 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v19/mdpi/ic_sysbar_back.png (renamed from tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back.png)bin774 -> 774 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v19/mdpi/ic_sysbar_home.png (renamed from tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home.png)bin836 -> 836 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v19/mdpi/ic_sysbar_recent.png (renamed from tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent.png)bin591 -> 591 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v19/mdpi/stat_sys_wifi_signal_4_fully.pngbin0 -> 1333 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v19/xhdpi/ic_sysbar_back.png (renamed from tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_back.png)bin1421 -> 1421 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v19/xhdpi/ic_sysbar_home.png (renamed from tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_home.png)bin1421 -> 1421 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v19/xhdpi/ic_sysbar_recent.png (renamed from tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_recent.png)bin749 -> 749 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v19/xhdpi/stat_sys_wifi_signal_4_fully.pngbin0 -> 1750 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v19/xxhdpi/ic_sysbar_back.pngbin0 -> 2811 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v19/xxhdpi/ic_sysbar_home.pngbin0 -> 2229 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v19/xxhdpi/ic_sysbar_recent.pngbin0 -> 1517 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v19/xxhdpi/stat_sys_wifi_signal_4_fully.pngbin0 -> 2202 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v21/hdpi/ic_sysbar_back.pngbin0 -> 2980 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v21/hdpi/ic_sysbar_home.pngbin0 -> 3653 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v21/hdpi/ic_sysbar_recent.pngbin0 -> 1396 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v21/hdpi/stat_sys_battery_100.pngbin0 -> 19810 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v21/ldrtl-hdpi/ic_sysbar_back.pngbin0 -> 3026 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v21/ldrtl-mdpi/ic_sysbar_back.pngbin0 -> 1803 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v21/ldrtl-xhdpi/ic_sysbar_back.pngbin0 -> 4375 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v21/ldrtl-xxhdpi/ic_sysbar_back.pngbin0 -> 2062 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v21/mdpi/ic_sysbar_back.pngbin0 -> 1845 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v21/mdpi/ic_sysbar_home.pngbin0 -> 2372 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v21/mdpi/ic_sysbar_recent.pngbin0 -> 1148 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v21/mdpi/stat_sys_battery_100.pngbin0 -> 19396 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v21/xhdpi/ic_sysbar_back.pngbin0 -> 4218 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v21/xhdpi/ic_sysbar_home.pngbin0 -> 5365 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v21/xhdpi/ic_sysbar_recent.pngbin0 -> 1754 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v21/xhdpi/stat_sys_battery_100.pngbin0 -> 19839 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v21/xhdpi/stat_sys_wifi_signal_4_fully.xml9
-rw-r--r--tools/layoutlib/bridge/resources/bars/v21/xxhdpi/ic_sysbar_back.pngbin0 -> 7195 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v21/xxhdpi/ic_sysbar_home.pngbin0 -> 8635 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v21/xxhdpi/ic_sysbar_recent.pngbin0 -> 2369 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v21/xxhdpi/stat_sys_battery_100.pngbin0 -> 19388 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v9/hdpi/stat_sys_battery_100.pngbin0 -> 788 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v9/hdpi/stat_sys_wifi_signal_4_fully.pngbin0 -> 5146 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v9/ldpi/stat_sys_battery_100.pngbin0 -> 604 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v9/mdpi/stat_sys_battery_100.pngbin0 -> 715 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/v9/mdpi/stat_sys_wifi_signal_4_fully.pngbin0 -> 3922 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/xhdpi/stat_sys_battery_100.pngbin0 -> 19839 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/xhdpi/stat_sys_battery_charge_anim100.pngbin1332 -> 0 bytes
-rw-r--r--tools/layoutlib/bridge/resources/bars/xhdpi/stat_sys_wifi_signal_4_fully.pngbin3485 -> 0 bytes
-rw-r--r--tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java50
-rw-r--r--tools/layoutlib/bridge/src/android/content/res/AssetManager_Delegate.java45
-rw-r--r--tools/layoutlib/bridge/src/android/content/res/BridgeAssetManager.java2
-rw-r--r--tools/layoutlib/bridge/src/android/content/res/BridgeResources.java6
-rw-r--r--tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java398
-rw-r--r--tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java86
-rw-r--r--tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java5
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java146
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java19
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java7
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java37
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java559
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java14
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java11
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java13
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java375
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java11
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java16
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java193
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java6
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java243
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java65
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java83
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java75
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java14
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java7
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java5
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java14
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java214
-rw-r--r--tools/layoutlib/bridge/src/android/os/SystemProperties_Delegate.java105
-rw-r--r--tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java11
-rw-r--r--tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java55
-rw-r--r--tools/layoutlib/bridge/src/android/text/format/Time_Delegate.java85
-rw-r--r--tools/layoutlib/bridge/src/android/util/BridgeXmlPullAttributes.java4
-rw-r--r--tools/layoutlib/bridge/src/android/util/Xml_Delegate.java (renamed from tools/layoutlib/bridge/src/android/os/Build_Delegate.java)33
-rw-r--r--tools/layoutlib/bridge/src/android/view/BridgeInflater.java99
-rw-r--r--tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java76
-rw-r--r--tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java22
-rw-r--r--tools/layoutlib/bridge/src/android/view/MenuInflater_Delegate.java74
-rw-r--r--tools/layoutlib/bridge/src/android/view/SurfaceView.java4
-rw-r--r--tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java172
-rw-r--r--tools/layoutlib/bridge/src/com/android/internal/view/menu/BridgeMenuItemImpl.java68
-rw-r--r--tools/layoutlib/bridge/src/com/android/internal/view/menu/MenuBuilder_Delegate.java38
-rw-r--r--tools/layoutlib/bridge/src/com/android/internal/widget/ActionBarAccessor.java32
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java24
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java5
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java6
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java59
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java1
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java100
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java16
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java23
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java9
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java14
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java355
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java117
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java148
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java48
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/IconLoader.java125
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java9
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/OverflowMenuAdapter.java99
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java67
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java9
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java398
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java185
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java107
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java455
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java20
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/SystemViewInfo.java51
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/SparseWeakArray.java64
-rw-r--r--tools/layoutlib/bridge/src/dalvik/system/VMRuntime_Delegate.java78
-rw-r--r--tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java21
-rw-r--r--tools/layoutlib/bridge/tests/Android.mk13
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/.gitignore14
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/build.gradle43
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/BuildConfig.classbin0 -> 775 bytes
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.classbin0 -> 1157 bytes
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$attr.classbin0 -> 406 bytes
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.classbin0 -> 527 bytes
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$drawable.classbin0 -> 473 bytes
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.classbin0 -> 488 bytes
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.classbin0 -> 485 bytes
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.classbin0 -> 452 bytes
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.classbin0 -> 538 bytes
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.classbin0 -> 461 bytes
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.classbin0 -> 897 bytes
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradle.properties18
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradle/wrapper/gradle-wrapper.jarbin0 -> 49896 bytes
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradle/wrapper/gradle-wrapper.properties6
-rwxr-xr-xtools/layoutlib/bridge/tests/res/testApp/MyApplication/gradlew164
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradlew.bat90
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/proguard-rules.pro17
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/androidTest/java/com/android/layoulib/test/myapplication/ApplicationTest.java13
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/AndroidManifest.xml21
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/MyActivity.java35
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/ic_launcher.xml9
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/activity.xml21
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml11
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/menu/my.xml8
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/dimens.xml5
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/strings.xml8
-rw-r--r--tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml8
-rw-r--r--tools/layoutlib/bridge/tests/src/android/graphics/Matrix_DelegateTest.java10
-rw-r--r--tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java79
-rw-r--r--tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java11
-rw-r--r--tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java374
-rw-r--r--tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ConfigGenerator.java294
-rw-r--r--tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java183
-rw-r--r--tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java111
-rw-r--r--tools/layoutlib/create/.classpath2
-rw-r--r--tools/layoutlib/create/Android.mk3
-rw-r--r--tools/layoutlib/create/README.txt275
-rw-r--r--tools/layoutlib/create/create.iml18
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/AbstractClassAdapter.java13
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java62
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java54
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java35
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java37
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java (renamed from tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter2.java)10
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/DependencyFinder.java7
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java4
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java28
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/OverrideMethod.java1
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/RenameClassAdapter.java2
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java173
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java4
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java34
-rw-r--r--tools/layoutlib/create/tests/Android.mk35
-rw-r--r--tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java8
-rw-r--r--tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java86
-rw-r--r--tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java88
-rw-r--r--tools/layoutlib/create/tests/data/mock_android.jarbin14872 -> 10198 bytes
-rw-r--r--tools/layoutlib/create/tests/mock_data/mock_android/util/EmptyArray.java24
-rw-r--r--tools/layoutlib/create/tests/mock_data/mock_android/widget/LinearLayout.java4
-rw-r--r--tools/layoutlib/rename_font/README9
-rw-r--r--tools/layoutlib/rename_font/Roboto-Regular.ttfbin0 -> 114976 bytes
-rwxr-xr-xtools/layoutlib/rename_font/build_font.py224
-rwxr-xr-xtools/layoutlib/rename_font/build_font_single.py208
-rwxr-xr-xtools/layoutlib/rename_font/test.py45
236 files changed, 7149 insertions, 2731 deletions
diff --git a/tools/layoutlib/.gitignore b/tools/layoutlib/.gitignore
index c5e82d7..eb52b64 100644
--- a/tools/layoutlib/.gitignore
+++ b/tools/layoutlib/.gitignore
@@ -1 +1,3 @@
-bin \ No newline at end of file
+bin
+/.idea/workspace.xml
+/out
diff --git a/tools/layoutlib/.idea/.name b/tools/layoutlib/.idea/.name
new file mode 100644
index 0000000..10eb5c1
--- /dev/null
+++ b/tools/layoutlib/.idea/.name
@@ -0,0 +1 @@
+layoutlib \ No newline at end of file
diff --git a/tools/layoutlib/.idea/codeStyleSettings.xml b/tools/layoutlib/.idea/codeStyleSettings.xml
new file mode 100644
index 0000000..b324213
--- /dev/null
+++ b/tools/layoutlib/.idea/codeStyleSettings.xml
@@ -0,0 +1,75 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectCodeStyleSettingsManager">
+ <option name="PER_PROJECT_SETTINGS">
+ <value>
+ <option name="FIELD_NAME_PREFIX" value="m" />
+ <option name="STATIC_FIELD_NAME_PREFIX" value="s" />
+ <option name="USE_FQ_CLASS_NAMES_IN_JAVADOC" value="false" />
+ <option name="INSERT_INNER_CLASS_IMPORTS" value="true" />
+ <option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999" />
+ <option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="999" />
+ <option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
+ <value />
+ </option>
+ <option name="IMPORT_LAYOUT_TABLE">
+ <value>
+ <package name="com.android" withSubpackages="true" static="false" />
+ <emptyLine />
+ <package name="org" withSubpackages="true" static="false" />
+ <emptyLine />
+ <package name="android" withSubpackages="true" static="false" />
+ <emptyLine />
+ <package name="java" withSubpackages="true" static="false" />
+ <emptyLine />
+ <package name="" withSubpackages="true" static="false" />
+ <emptyLine />
+ <package name="" withSubpackages="true" static="true" />
+ </value>
+ </option>
+ <option name="RIGHT_MARGIN" value="100" />
+ <option name="WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN" value="true" />
+ <option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
+ <option name="JD_ADD_BLANK_AFTER_PARM_COMMENTS" value="true" />
+ <option name="JD_ADD_BLANK_AFTER_RETURN" value="true" />
+ <option name="JD_DO_NOT_WRAP_ONE_LINE_COMMENTS" value="true" />
+ <option name="WRAP_COMMENTS" value="true" />
+ <XML>
+ <option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
+ </XML>
+ <codeStyleSettings language="JAVA">
+ <option name="INDENT_CASE_FROM_SWITCH" value="false" />
+ <option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
+ <option name="CALL_PARAMETERS_WRAP" value="1" />
+ <option name="METHOD_PARAMETERS_WRAP" value="1" />
+ <option name="THROWS_LIST_WRAP" value="1" />
+ <option name="EXTENDS_KEYWORD_WRAP" value="1" />
+ <option name="THROWS_KEYWORD_WRAP" value="1" />
+ <option name="BINARY_OPERATION_WRAP" value="1" />
+ <option name="TERNARY_OPERATION_WRAP" value="1" />
+ <option name="ARRAY_INITIALIZER_WRAP" value="1" />
+ <option name="ASSIGNMENT_WRAP" value="1" />
+ <option name="ASSERT_STATEMENT_WRAP" value="1" />
+ <option name="IF_BRACE_FORCE" value="3" />
+ <option name="DOWHILE_BRACE_FORCE" value="3" />
+ <option name="WHILE_BRACE_FORCE" value="3" />
+ <option name="FOR_BRACE_FORCE" value="3" />
+ <arrangement>
+ <groups>
+ <group>
+ <type>GETTERS_AND_SETTERS</type>
+ <order>KEEP</order>
+ </group>
+ <group>
+ <type>OVERRIDDEN_METHODS</type>
+ <order>KEEP</order>
+ </group>
+ </groups>
+ </arrangement>
+ </codeStyleSettings>
+ </value>
+ </option>
+ <option name="USE_PER_PROJECT_SETTINGS" value="true" />
+ </component>
+</project>
+
diff --git a/tools/layoutlib/.idea/compiler.xml b/tools/layoutlib/.idea/compiler.xml
new file mode 100644
index 0000000..5aaaf18
--- /dev/null
+++ b/tools/layoutlib/.idea/compiler.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="CompilerConfiguration">
+ <option name="DEFAULT_COMPILER" value="Javac" />
+ <excludeFromCompile>
+ <directory url="file://$PROJECT_DIR$/create/tests/mock_data" includeSubdirectories="true" />
+ </excludeFromCompile>
+ <resourceExtensions />
+ <wildcardResourcePatterns>
+ <entry name="!?*.java" />
+ <entry name="!?*.form" />
+ <entry name="!?*.class" />
+ <entry name="!?*.groovy" />
+ <entry name="!?*.scala" />
+ <entry name="!?*.flex" />
+ <entry name="!?*.kt" />
+ <entry name="!?*.clj" />
+ </wildcardResourcePatterns>
+ <annotationProcessing>
+ <profile default="true" name="Default" enabled="false">
+ <processorPath useClasspath="true" />
+ </profile>
+ </annotationProcessing>
+ <bytecodeTargetLevel target="1.6" />
+ </component>
+</project>
+
diff --git a/tools/layoutlib/.idea/copyright/Android.xml b/tools/layoutlib/.idea/copyright/Android.xml
new file mode 100644
index 0000000..d81d75d
--- /dev/null
+++ b/tools/layoutlib/.idea/copyright/Android.xml
@@ -0,0 +1,9 @@
+<component name="CopyrightManager">
+ <copyright>
+ <option name="notice" value="Copyright (C) &amp;#36;today.year The Android Open Source Project&#10;&#10;Licensed under the Apache License, Version 2.0 (the &quot;License&quot;);&#10;you may not use this file except in compliance with the License.&#10;You may obtain a copy of the License at&#10;&#10; http://www.apache.org/licenses/LICENSE-2.0&#10;&#10;Unless required by applicable law or agreed to in writing, software&#10;distributed under the License is distributed on an &quot;AS IS&quot; BASIS,&#10;WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.&#10;See the License for the specific language governing permissions and&#10;limitations under the License." />
+ <option name="keyword" value="Copyright" />
+ <option name="allowReplaceKeyword" value="" />
+ <option name="myName" value="Android" />
+ <option name="myLocal" value="true" />
+ </copyright>
+</component> \ No newline at end of file
diff --git a/tools/layoutlib/.idea/copyright/profiles_settings.xml b/tools/layoutlib/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..20145de
--- /dev/null
+++ b/tools/layoutlib/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+<component name="CopyrightManager">
+ <settings default="Android" />
+</component> \ No newline at end of file
diff --git a/tools/layoutlib/.idea/encodings.xml b/tools/layoutlib/.idea/encodings.xml
new file mode 100644
index 0000000..e206d70
--- /dev/null
+++ b/tools/layoutlib/.idea/encodings.xml
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" />
+</project>
+
diff --git a/tools/layoutlib/.idea/inspectionProfiles/Project_Default.xml b/tools/layoutlib/.idea/inspectionProfiles/Project_Default.xml
new file mode 100644
index 0000000..0ac7a44
--- /dev/null
+++ b/tools/layoutlib/.idea/inspectionProfiles/Project_Default.xml
@@ -0,0 +1,11 @@
+<component name="InspectionProjectProfileManager">
+ <profile version="1.0" is_locked="false">
+ <option name="myName" value="Project Default" />
+ <option name="myLocal" value="false" />
+ <inspection_tool class="DefaultFileTemplate" enabled="false" level="WARNING" enabled_by_default="false">
+ <option name="CHECK_FILE_HEADER" value="true" />
+ <option name="CHECK_TRY_CATCH_SECTION" value="true" />
+ <option name="CHECK_METHOD_BODY" value="true" />
+ </inspection_tool>
+ </profile>
+</component> \ No newline at end of file
diff --git a/tools/layoutlib/.idea/inspectionProfiles/profiles_settings.xml b/tools/layoutlib/.idea/inspectionProfiles/profiles_settings.xml
new file mode 100644
index 0000000..3b31283
--- /dev/null
+++ b/tools/layoutlib/.idea/inspectionProfiles/profiles_settings.xml
@@ -0,0 +1,7 @@
+<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/tools/layoutlib/.idea/libraries/asm_4_0.xml b/tools/layoutlib/.idea/libraries/asm_4_0.xml
new file mode 100644
index 0000000..7df287f
--- /dev/null
+++ b/tools/layoutlib/.idea/libraries/asm_4_0.xml
@@ -0,0 +1,11 @@
+<component name="libraryTable">
+ <library name="asm-4.0">
+ <CLASSES>
+ <root url="jar://$PROJECT_DIR$/../../../../prebuilts/misc/common/asm/asm-4.0.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES>
+ <root url="jar://$PROJECT_DIR$/../../../../prebuilts/misc/common/asm/src.zip!/" />
+ </SOURCES>
+ </library>
+</component> \ No newline at end of file
diff --git a/tools/layoutlib/.idea/libraries/framework_jar.xml b/tools/layoutlib/.idea/libraries/framework_jar.xml
new file mode 100644
index 0000000..6695a36
--- /dev/null
+++ b/tools/layoutlib/.idea/libraries/framework_jar.xml
@@ -0,0 +1,13 @@
+<component name="libraryTable">
+ <library name="framework.jar">
+ <CLASSES>
+ <root url="jar://$PROJECT_DIR$/../../../../out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES>
+ <root url="file://$PROJECT_DIR$/../../core/java" />
+ <root url="file://$PROJECT_DIR$/../../graphics/java" />
+ <root url="file://$PROJECT_DIR$/../../../../libcore/luni/src/main/java" />
+ </SOURCES>
+ </library>
+</component> \ No newline at end of file
diff --git a/tools/layoutlib/.idea/libraries/guava.xml b/tools/layoutlib/.idea/libraries/guava.xml
new file mode 100644
index 0000000..d47fc06
--- /dev/null
+++ b/tools/layoutlib/.idea/libraries/guava.xml
@@ -0,0 +1,11 @@
+<component name="libraryTable">
+ <library name="guava">
+ <CLASSES>
+ <root url="jar://$PROJECT_DIR$/../../../../out/host/common/obj/JAVA_LIBRARIES/guavalib_intermediates/javalib.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES>
+ <root url="file://$PROJECT_DIR$/../../../../external/guava/guava/src" />
+ </SOURCES>
+ </library>
+</component> \ No newline at end of file
diff --git a/tools/layoutlib/.idea/libraries/icu4j.xml b/tools/layoutlib/.idea/libraries/icu4j.xml
new file mode 100644
index 0000000..dbe0bd7
--- /dev/null
+++ b/tools/layoutlib/.idea/libraries/icu4j.xml
@@ -0,0 +1,11 @@
+<component name="libraryTable">
+ <library name="icu4j">
+ <CLASSES>
+ <root url="jar://$PROJECT_DIR$/../../../../prebuilts/misc/common/icu4j/icu4j.jar!/" />
+ </CLASSES>
+ <JAVADOC>
+ <root url="http://icu-project.org/apiref/icu4j50rc/" />
+ </JAVADOC>
+ <SOURCES />
+ </library>
+</component> \ No newline at end of file
diff --git a/tools/layoutlib/.idea/libraries/kxml2_2_3_0.xml b/tools/layoutlib/.idea/libraries/kxml2_2_3_0.xml
new file mode 100644
index 0000000..2a65050
--- /dev/null
+++ b/tools/layoutlib/.idea/libraries/kxml2_2_3_0.xml
@@ -0,0 +1,11 @@
+<component name="libraryTable">
+ <library name="kxml2-2.3.0">
+ <CLASSES>
+ <root url="jar://$PROJECT_DIR$/../../../../prebuilts/misc/common/kxml2/kxml2-2.3.0.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES>
+ <root url="file://$PROJECT_DIR$/../../../../libcore/xml/src/main/java" />
+ </SOURCES>
+ </library>
+</component> \ No newline at end of file
diff --git a/tools/layoutlib/.idea/libraries/layoutlib_api_prebuilt.xml b/tools/layoutlib/.idea/libraries/layoutlib_api_prebuilt.xml
new file mode 100644
index 0000000..5952002
--- /dev/null
+++ b/tools/layoutlib/.idea/libraries/layoutlib_api_prebuilt.xml
@@ -0,0 +1,11 @@
+<component name="libraryTable">
+ <library name="layoutlib_api-prebuilt">
+ <CLASSES>
+ <root url="jar://$PROJECT_DIR$/../../../../prebuilts/misc/common/layoutlib_api/layoutlib_api-prebuilt.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES>
+ <root url="file://$ANDROID_SRC$/tools/base/layoutlib-api/src/main/java" />
+ </SOURCES>
+ </library>
+</component> \ No newline at end of file
diff --git a/tools/layoutlib/.idea/libraries/ninepatch_prebuilt.xml b/tools/layoutlib/.idea/libraries/ninepatch_prebuilt.xml
new file mode 100644
index 0000000..f34f7dd
--- /dev/null
+++ b/tools/layoutlib/.idea/libraries/ninepatch_prebuilt.xml
@@ -0,0 +1,11 @@
+<component name="libraryTable">
+ <library name="ninepatch-prebuilt">
+ <CLASSES>
+ <root url="jar://$PROJECT_DIR$/../../../../prebuilts/misc/common/ninepatch/ninepatch-prebuilt.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES>
+ <root url="file://$ANDROID_SRC$/tools/base/ninepatch/src/main/java" />
+ </SOURCES>
+ </library>
+</component> \ No newline at end of file
diff --git a/tools/layoutlib/.idea/libraries/tools_common_prebuilt.xml b/tools/layoutlib/.idea/libraries/tools_common_prebuilt.xml
new file mode 100644
index 0000000..b325ad4
--- /dev/null
+++ b/tools/layoutlib/.idea/libraries/tools_common_prebuilt.xml
@@ -0,0 +1,14 @@
+<component name="libraryTable">
+ <library name="tools-common-prebuilt">
+ <ANNOTATIONS>
+ <root url="file://$PROJECT_DIR$" />
+ </ANNOTATIONS>
+ <CLASSES>
+ <root url="jar://$PROJECT_DIR$/../../../../prebuilts/misc/common/tools-common/tools-common-prebuilt.jar!/" />
+ </CLASSES>
+ <JAVADOC />
+ <SOURCES>
+ <root url="file://$ANDROID_SRC$/tools/base/common/src/main/java" />
+ </SOURCES>
+ </library>
+</component> \ No newline at end of file
diff --git a/tools/layoutlib/.idea/misc.xml b/tools/layoutlib/.idea/misc.xml
new file mode 100644
index 0000000..fd63e6c
--- /dev/null
+++ b/tools/layoutlib/.idea/misc.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="EntryPointsManager">
+ <entry_points version="2.0" />
+ <list size="1">
+ <item index="0" class="java.lang.String" itemvalue="com.android.tools.layoutlib.annotations.LayoutlibDelegate" />
+ </list>
+ </component>
+ <component name="FrameworkDetectionExcludesConfiguration">
+ <type id="android" />
+ </component>
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_1_6" assert-keyword="true" jdk-15="true" project-jdk-name="1.7" project-jdk-type="JavaSDK">
+ <output url="file://$PROJECT_DIR$/out" />
+ </component>
+</project>
+
diff --git a/tools/layoutlib/.idea/modules.xml b/tools/layoutlib/.idea/modules.xml
new file mode 100644
index 0000000..684f4fd
--- /dev/null
+++ b/tools/layoutlib/.idea/modules.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectModuleManager">
+ <modules>
+ <module fileurl="file://$PROJECT_DIR$/bridge/bridge.iml" filepath="$PROJECT_DIR$/bridge/bridge.iml" />
+ <module fileurl="file://$PROJECT_DIR$/create/create.iml" filepath="$PROJECT_DIR$/create/create.iml" />
+ </modules>
+ </component>
+</project>
+
diff --git a/tools/layoutlib/.idea/runConfigurations/All_in_bridge.xml b/tools/layoutlib/.idea/runConfigurations/All_in_bridge.xml
new file mode 100644
index 0000000..f965ba7
--- /dev/null
+++ b/tools/layoutlib/.idea/runConfigurations/All_in_bridge.xml
@@ -0,0 +1,31 @@
+<component name="ProjectRunConfigurationManager">
+ <configuration default="false" name="All in bridge" type="JUnit" factoryName="JUnit" singleton="true" nameIsGenerated="true">
+ <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
+ <module name="bridge" />
+ <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 -Dtest_res.dir=&quot;$PROJECT_DIR$/bridge/tests/res&quot;" />
+ <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 />
+ <RunnerSettings RunnerId="Debug">
+ <option name="DEBUG_PORT" value="" />
+ <option name="TRANSPORT" value="0" />
+ <option name="LOCAL" value="true" />
+ </RunnerSettings>
+ <RunnerSettings RunnerId="Run" />
+ <ConfigurationWrapper RunnerId="Debug" />
+ <ConfigurationWrapper RunnerId="Run" />
+ <method />
+ </configuration>
+</component> \ No newline at end of file
diff --git a/tools/layoutlib/.idea/runConfigurations/All_in_create.xml b/tools/layoutlib/.idea/runConfigurations/All_in_create.xml
new file mode 100644
index 0000000..b9cd419
--- /dev/null
+++ b/tools/layoutlib/.idea/runConfigurations/All_in_create.xml
@@ -0,0 +1,31 @@
+<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 />
+ <RunnerSettings RunnerId="Debug">
+ <option name="DEBUG_PORT" value="" />
+ <option name="TRANSPORT" value="0" />
+ <option name="LOCAL" value="true" />
+ </RunnerSettings>
+ <RunnerSettings RunnerId="Run" />
+ <ConfigurationWrapper RunnerId="Debug" />
+ <ConfigurationWrapper RunnerId="Run" />
+ <method />
+ </configuration>
+</component> \ No newline at end of file
diff --git a/tools/layoutlib/.idea/runConfigurations/Create.xml b/tools/layoutlib/.idea/runConfigurations/Create.xml
new file mode 100644
index 0000000..fb0b866
--- /dev/null
+++ b/tools/layoutlib/.idea/runConfigurations/Create.xml
@@ -0,0 +1,25 @@
+<component name="ProjectRunConfigurationManager">
+ <configuration default="false" name="Create" type="Application" factoryName="Application" singleton="true">
+ <extension name="coverage" enabled="false" merge="false" sample_coverage="true" runner="idea" />
+ <option name="MAIN_CLASS_NAME" value="com.android.tools.layoutlib.create.Main" />
+ <option name="VM_PARAMETERS" value="" />
+ <option name="PROGRAM_PARAMETERS" value="out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.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/target/common/obj/JAVA_LIBRARIES/ext_intermediates/javalib.jar" />
+ <option name="WORKING_DIRECTORY" value="file://$PROJECT_DIR$/../../../../" />
+ <option name="ALTERNATIVE_JRE_PATH_ENABLED" value="true" />
+ <option name="ALTERNATIVE_JRE_PATH" value="1.6" />
+ <option name="ENABLE_SWING_INSPECTOR" value="false" />
+ <option name="ENV_VARIABLES" />
+ <option name="PASS_PARENT_ENVS" value="true" />
+ <module name="create" />
+ <envs />
+ <RunnerSettings RunnerId="Debug">
+ <option name="DEBUG_PORT" value="" />
+ <option name="TRANSPORT" value="0" />
+ <option name="LOCAL" value="true" />
+ </RunnerSettings>
+ <RunnerSettings RunnerId="Run" />
+ <ConfigurationWrapper RunnerId="Debug" />
+ <ConfigurationWrapper RunnerId="Run" />
+ <method />
+ </configuration>
+</component> \ No newline at end of file
diff --git a/tools/layoutlib/.idea/scopes/scope_settings.xml b/tools/layoutlib/.idea/scopes/scope_settings.xml
new file mode 100644
index 0000000..922003b
--- /dev/null
+++ b/tools/layoutlib/.idea/scopes/scope_settings.xml
@@ -0,0 +1,5 @@
+<component name="DependencyValidationManager">
+ <state>
+ <option name="SKIP_IMPORT_STATEMENTS" value="false" />
+ </state>
+</component> \ No newline at end of file
diff --git a/tools/layoutlib/.idea/uiDesigner.xml b/tools/layoutlib/.idea/uiDesigner.xml
new file mode 100644
index 0000000..3b00020
--- /dev/null
+++ b/tools/layoutlib/.idea/uiDesigner.xml
@@ -0,0 +1,125 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="Palette2">
+ <group name="Swing">
+ <item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
+ </item>
+ <item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
+ </item>
+ <item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
+ </item>
+ <item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.png" removable="false" auto-create-binding="false" can-attach-label="true">
+ <default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
+ </item>
+ <item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
+ <initial-values>
+ <property name="text" value="Button" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+ <initial-values>
+ <property name="text" value="RadioButton" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
+ <initial-values>
+ <property name="text" value="CheckBox" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
+ <initial-values>
+ <property name="text" value="Label" />
+ </initial-values>
+ </item>
+ <item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+ <preferred-size width="150" height="-1" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+ <preferred-size width="150" height="-1" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
+ <preferred-size width="150" height="-1" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
+ </item>
+ <item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
+ <preferred-size width="150" height="50" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+ <preferred-size width="200" height="200" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
+ <preferred-size width="200" height="200" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.png" removable="false" auto-create-binding="true" can-attach-label="true">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+ </item>
+ <item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
+ </item>
+ <item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
+ </item>
+ <item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
+ </item>
+ <item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
+ <preferred-size width="-1" height="20" />
+ </default-constraints>
+ </item>
+ <item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.png" removable="false" auto-create-binding="false" can-attach-label="false">
+ <default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
+ </item>
+ <item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.png" removable="false" auto-create-binding="true" can-attach-label="false">
+ <default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
+ </item>
+ </group>
+ </component>
+</project>
+
diff --git a/tools/layoutlib/.idea/vcs.xml b/tools/layoutlib/.idea/vcs.xml
new file mode 100644
index 0000000..9ab281a
--- /dev/null
+++ b/tools/layoutlib/.idea/vcs.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="VcsDirectoryMappings">
+ <mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
+ </component>
+</project>
+
diff --git a/tools/layoutlib/Android.mk b/tools/layoutlib/Android.mk
index 5d03842..9300401 100644
--- a/tools/layoutlib/Android.mk
+++ b/tools/layoutlib/Android.mk
@@ -16,6 +16,8 @@
LOCAL_PATH := $(my-dir)
include $(CLEAR_VARS)
+LOCAL_JAVACFLAGS := -source 6 -target 6
+
#
# Define rules to build temp_layoutlib.jar, which contains a subset of
# the classes in framework.jar. The layoutlib_create tool is used to
@@ -25,8 +27,8 @@ include $(CLEAR_VARS)
# We need to process the framework classes.jar file, but we can't
# depend directly on it (private vars won't be inherited correctly).
# So, we depend on framework's BUILT file.
-built_framework_dep := $(call java-lib-deps,framework-base)
-built_framework_classes := $(call java-lib-files,framework-base)
+built_framework_dep := $(call java-lib-deps,framework)
+built_framework_classes := $(call java-lib-files,framework)
built_core_dep := $(call java-lib-deps,core-libart)
built_core_classes := $(call java-lib-files,core-libart)
@@ -53,12 +55,13 @@ include $(BUILD_SYSTEM)/base_rules.mk
$(LOCAL_BUILT_MODULE): $(built_core_dep) \
$(built_framework_dep) \
$(built_ext_dep) \
+ $(built_ext_data) \
$(built_layoutlib_create_jar)
$(hide) echo "host layoutlib_create: $@"
$(hide) mkdir -p $(dir $@)
$(hide) rm -f $@
$(hide) ls -l $(built_framework_classes)
- $(hide) java -jar $(built_layoutlib_create_jar) \
+ $(hide) java -ea -jar $(built_layoutlib_create_jar) \
$@ \
$(built_core_classes) \
$(built_framework_classes) \
diff --git a/tools/layoutlib/bridge/.classpath b/tools/layoutlib/bridge/.classpath
index 2e4274d..9c4160c 100644
--- a/tools/layoutlib/bridge/.classpath
+++ b/tools/layoutlib/bridge/.classpath
@@ -1,12 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry excluding="org/kxml2/io/" kind="src" path="src"/>
+ <classpathentry kind="src" path="tests/src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
- <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/layoutlib_api/layoutlib_api-prebuilt.jar"/>
+ <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/layoutlib_api/layoutlib_api-prebuilt.jar" sourcepath="/ANDROID_SRC/tools/base/layoutlib-api/src/main"/>
<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_PLAT_SRC/dalvik/libcore/xml/src/main/java"/>
<classpathentry kind="var" path="ANDROID_PLAT_SRC/out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar" sourcepath="/ANDROID_PLAT_SRC/frameworks/base"/>
<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/ninepatch/ninepatch-prebuilt.jar"/>
<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/tools-common/tools-common-prebuilt.jar"/>
<classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/icu4j/icu4j.jar"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
+ <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/sdk-common/sdk-common.jar"/>
+ <classpathentry kind="var" path="ANDROID_PLAT_SRC/out/host/common/obj/JAVA_LIBRARIES/guavalib_intermediates/javalib.jar"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/tools/layoutlib/bridge/Android.mk b/tools/layoutlib/bridge/Android.mk
index e3d48fc..cfd597e 100644
--- a/tools/layoutlib/bridge/Android.mk
+++ b/tools/layoutlib/bridge/Android.mk
@@ -18,6 +18,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-java-files-under,src)
LOCAL_JAVA_RESOURCE_DIRS := resources
+LOCAL_JAVACFLAGS := -source 6 -target 6
LOCAL_JAVA_LIBRARIES := \
diff --git a/tools/layoutlib/bridge/bridge.iml b/tools/layoutlib/bridge/bridge.iml
new file mode 100644
index 0000000..0f96916
--- /dev/null
+++ b/tools/layoutlib/bridge/bridge.iml
@@ -0,0 +1,48 @@
+<?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$/resources" type="java-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/tests/res" type="java-test-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/tests/src" isTestSource="true" />
+ <excludeFolder url="file://$MODULE_DIR$/.settings" />
+ <excludeFolder url="file://$MODULE_DIR$/bin" />
+ <excludeFolder url="file://$MODULE_DIR$/tests/res/testApp/MyApplication/.gradle" />
+ <excludeFolder url="file://$MODULE_DIR$/tests/res/testApp/MyApplication/.idea" />
+ <excludeFolder url="file://$MODULE_DIR$/tests/res/testApp/MyApplication/build/generated" />
+ <excludeFolder url="file://$MODULE_DIR$/tests/res/testApp/MyApplication/build/intermediates/assets" />
+ <excludeFolder url="file://$MODULE_DIR$/tests/res/testApp/MyApplication/build/intermediates/dependency-cache" />
+ <excludeFolder url="file://$MODULE_DIR$/tests/res/testApp/MyApplication/build/intermediates/incremental" />
+ <excludeFolder url="file://$MODULE_DIR$/tests/res/testApp/MyApplication/build/intermediates/libs" />
+ <excludeFolder url="file://$MODULE_DIR$/tests/res/testApp/MyApplication/build/intermediates/manifests" />
+ <excludeFolder url="file://$MODULE_DIR$/tests/res/testApp/MyApplication/build/intermediates/res" />
+ <excludeFolder url="file://$MODULE_DIR$/tests/res/testApp/MyApplication/build/intermediates/rs" />
+ <excludeFolder url="file://$MODULE_DIR$/tests/res/testApp/MyApplication/build/intermediates/symbols" />
+ <excludeFolder url="file://$MODULE_DIR$/tests/res/testApp/MyApplication/gradle" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="icu4j" level="project" />
+ <orderEntry type="library" name="kxml2-2.3.0" level="project" />
+ <orderEntry type="library" name="layoutlib_api-prebuilt" level="project" />
+ <orderEntry type="library" name="ninepatch-prebuilt" level="project" />
+ <orderEntry type="library" name="tools-common-prebuilt" level="project" />
+ <orderEntry type="library" name="framework.jar" level="project" />
+ <orderEntry type="library" scope="TEST" name="guava" level="project" />
+ <orderEntry type="module-library" scope="TEST">
+ <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>
+ <orderEntry type="library" scope="TEST" name="JUnit4" level="application" />
+ </component>
+</module>
+
diff --git a/tools/layoutlib/bridge/resources/bars/README b/tools/layoutlib/bridge/resources/bars/README
new file mode 100644
index 0000000..c84ef80
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/README
@@ -0,0 +1,8 @@
+The directory contains the resources for StatusBar and Navigation Bar.
+
+The resources are not arranged as per the standard resources configuration.
+They are stored per API. However, to prevent duplication of resources, each API
+resource directory is used as a backup for all earlier API levels.
+
+For example, for the back icon for ICS, we search first in v18, where we don't
+find it, and then in v19.
diff --git a/tools/layoutlib/bridge/resources/bars/action_bar.xml b/tools/layoutlib/bridge/resources/bars/action_bar.xml
deleted file mode 100644
index 7adc5af..0000000
--- a/tools/layoutlib/bridge/resources/bars/action_bar.xml
+++ /dev/null
@@ -1,7 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <include layout="@android:layout/action_bar_home" />
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"/>
-</merge>
diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/stat_sys_battery_100.png b/tools/layoutlib/bridge/resources/bars/hdpi/stat_sys_battery_100.png
new file mode 100644
index 0000000..f17189a
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/hdpi/stat_sys_battery_100.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/stat_sys_battery_charge_anim100.png b/tools/layoutlib/bridge/resources/bars/hdpi/stat_sys_battery_charge_anim100.png
deleted file mode 100644
index 829378e..0000000
--- a/tools/layoutlib/bridge/resources/bars/hdpi/stat_sys_battery_charge_anim100.png
+++ /dev/null
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/stat_sys_wifi_signal_4_fully.png b/tools/layoutlib/bridge/resources/bars/hdpi/stat_sys_wifi_signal_4_fully.png
deleted file mode 100644
index 931daed..0000000
--- a/tools/layoutlib/bridge/resources/bars/hdpi/stat_sys_wifi_signal_4_fully.png
+++ /dev/null
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/status_bar_background.9.png b/tools/layoutlib/bridge/resources/bars/hdpi/status_bar_background.9.png
deleted file mode 100644
index a4be298..0000000
--- a/tools/layoutlib/bridge/resources/bars/hdpi/status_bar_background.9.png
+++ /dev/null
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_battery_100.png b/tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_battery_100.png
new file mode 100644
index 0000000..2a9757d
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_battery_100.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_battery_charge_anim100.png b/tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_battery_charge_anim100.png
deleted file mode 100644
index 2773a70..0000000
--- a/tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_battery_charge_anim100.png
+++ /dev/null
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_wifi_signal_4_fully.png b/tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_wifi_signal_4_fully.png
deleted file mode 100644
index 6e1ac91..0000000
--- a/tools/layoutlib/bridge/resources/bars/mdpi/stat_sys_wifi_signal_4_fully.png
+++ /dev/null
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/status_bar_background.9.png b/tools/layoutlib/bridge/resources/bars/mdpi/status_bar_background.9.png
deleted file mode 100644
index eb7c1a4..0000000
--- a/tools/layoutlib/bridge/resources/bars/mdpi/status_bar_background.9.png
+++ /dev/null
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/status_bar.xml b/tools/layoutlib/bridge/resources/bars/status_bar.xml
index 51b474d..04571e1 100644
--- a/tools/layoutlib/bridge/resources/bars/status_bar.xml
+++ b/tools/layoutlib/bridge/resources/bars/status_bar.xml
@@ -1,17 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
- <TextView
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"/>
- <ImageView
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_marginTop="1dp"/>
- <ImageView
- android:layout_height="wrap_content"
- android:layout_width="wrap_content"
- android:layout_marginLeft="3dp"
- android:layout_marginRight="5dp"
- android:layout_marginTop="1dp"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_weight="1"/>
+ <!-- The exact size of the wifi icon is specified in order to scale it properly.
+ Without scaling, it appeared huge. This is currently, 70% of the actual size. -->
+ <ImageView
+ android:layout_height="22.4dp"
+ android:layout_width="20.65dp"
+ android:layout_marginTop="1dp"/>
+ <ImageView
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content"
+ android:layout_marginLeft="3dp"
+ android:layout_marginRight="5dp"
+ android:layout_marginTop="4dp"/>
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="4dp"
+ android:layout_marginRight="5dp"
+ android:gravity="center_vertical"
+ android:textSize="16dp"
+ android:fontFamily="sans-serif-medium"/>
</merge>
diff --git a/tools/layoutlib/bridge/resources/bars/v18/hdpi/stat_sys_battery_100.png b/tools/layoutlib/bridge/resources/bars/v18/hdpi/stat_sys_battery_100.png
new file mode 100644
index 0000000..c920ec4
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v18/hdpi/stat_sys_battery_100.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v18/hdpi/stat_sys_wifi_signal_4_fully.png b/tools/layoutlib/bridge/resources/bars/v18/hdpi/stat_sys_wifi_signal_4_fully.png
new file mode 100644
index 0000000..6248cfd
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v18/hdpi/stat_sys_wifi_signal_4_fully.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v18/mdpi/stat_sys_battery_100.png b/tools/layoutlib/bridge/resources/bars/v18/mdpi/stat_sys_battery_100.png
new file mode 100644
index 0000000..943332e
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v18/mdpi/stat_sys_battery_100.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v18/mdpi/stat_sys_wifi_signal_4_fully.png b/tools/layoutlib/bridge/resources/bars/v18/mdpi/stat_sys_wifi_signal_4_fully.png
new file mode 100644
index 0000000..441de0c
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v18/mdpi/stat_sys_wifi_signal_4_fully.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v18/xhdpi/stat_sys_battery_100.png b/tools/layoutlib/bridge/resources/bars/v18/xhdpi/stat_sys_battery_100.png
new file mode 100644
index 0000000..36c61e1
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v18/xhdpi/stat_sys_battery_100.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v18/xhdpi/stat_sys_wifi_signal_4_fully.png b/tools/layoutlib/bridge/resources/bars/v18/xhdpi/stat_sys_wifi_signal_4_fully.png
new file mode 100644
index 0000000..459a1a2
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v18/xhdpi/stat_sys_wifi_signal_4_fully.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/v19/hdpi/ic_sysbar_back.png
index 84e6bc8..84e6bc8 100644
--- a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_back.png
+++ b/tools/layoutlib/bridge/resources/bars/v19/hdpi/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_home.png b/tools/layoutlib/bridge/resources/bars/v19/hdpi/ic_sysbar_home.png
index 38e4f45..38e4f45 100644
--- a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_home.png
+++ b/tools/layoutlib/bridge/resources/bars/v19/hdpi/ic_sysbar_home.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/v19/hdpi/ic_sysbar_recent.png
index bf9f300..bf9f300 100644
--- a/tools/layoutlib/bridge/resources/bars/hdpi/ic_sysbar_recent.png
+++ b/tools/layoutlib/bridge/resources/bars/v19/hdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v19/hdpi/stat_sys_wifi_signal_4_fully.png b/tools/layoutlib/bridge/resources/bars/v19/hdpi/stat_sys_wifi_signal_4_fully.png
new file mode 100644
index 0000000..6248cfd
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v19/hdpi/stat_sys_wifi_signal_4_fully.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/ldrtl-hdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/v19/ldrtl-hdpi/ic_sysbar_back.png
index 782ebfe..782ebfe 100644
--- a/tools/layoutlib/bridge/resources/bars/ldrtl-hdpi/ic_sysbar_back.png
+++ b/tools/layoutlib/bridge/resources/bars/v19/ldrtl-hdpi/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/ldrtl-hdpi/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/v19/ldrtl-hdpi/ic_sysbar_recent.png
index 677b471..677b471 100644
--- a/tools/layoutlib/bridge/resources/bars/ldrtl-hdpi/ic_sysbar_recent.png
+++ b/tools/layoutlib/bridge/resources/bars/v19/ldrtl-hdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/ldrtl-mdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/v19/ldrtl-mdpi/ic_sysbar_back.png
index a1b8062..a1b8062 100644
--- a/tools/layoutlib/bridge/resources/bars/ldrtl-mdpi/ic_sysbar_back.png
+++ b/tools/layoutlib/bridge/resources/bars/v19/ldrtl-mdpi/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/ldrtl-mdpi/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/v19/ldrtl-mdpi/ic_sysbar_recent.png
index fcdbefe..fcdbefe 100644
--- a/tools/layoutlib/bridge/resources/bars/ldrtl-mdpi/ic_sysbar_recent.png
+++ b/tools/layoutlib/bridge/resources/bars/v19/ldrtl-mdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/v19/ldrtl-xhdpi/ic_sysbar_back.png
index 633d864..633d864 100644
--- a/tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi/ic_sysbar_back.png
+++ b/tools/layoutlib/bridge/resources/bars/v19/ldrtl-xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/v19/ldrtl-xhdpi/ic_sysbar_recent.png
index 4665e2a..4665e2a 100644
--- a/tools/layoutlib/bridge/resources/bars/ldrtl-xhdpi/ic_sysbar_recent.png
+++ b/tools/layoutlib/bridge/resources/bars/v19/ldrtl-xhdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/v19/mdpi/ic_sysbar_back.png
index a00bc5b..a00bc5b 100644
--- a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_back.png
+++ b/tools/layoutlib/bridge/resources/bars/v19/mdpi/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home.png b/tools/layoutlib/bridge/resources/bars/v19/mdpi/ic_sysbar_home.png
index dc3183b..dc3183b 100644
--- a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_home.png
+++ b/tools/layoutlib/bridge/resources/bars/v19/mdpi/ic_sysbar_home.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/v19/mdpi/ic_sysbar_recent.png
index b07f611..b07f611 100644
--- a/tools/layoutlib/bridge/resources/bars/mdpi/ic_sysbar_recent.png
+++ b/tools/layoutlib/bridge/resources/bars/v19/mdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v19/mdpi/stat_sys_wifi_signal_4_fully.png b/tools/layoutlib/bridge/resources/bars/v19/mdpi/stat_sys_wifi_signal_4_fully.png
new file mode 100644
index 0000000..441de0c
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v19/mdpi/stat_sys_wifi_signal_4_fully.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/v19/xhdpi/ic_sysbar_back.png
index bd60cd6..bd60cd6 100644
--- a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_back.png
+++ b/tools/layoutlib/bridge/resources/bars/v19/xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_home.png b/tools/layoutlib/bridge/resources/bars/v19/xhdpi/ic_sysbar_home.png
index c5bc5c9..c5bc5c9 100644
--- a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_home.png
+++ b/tools/layoutlib/bridge/resources/bars/v19/xhdpi/ic_sysbar_home.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/v19/xhdpi/ic_sysbar_recent.png
index f621d9c..f621d9c 100644
--- a/tools/layoutlib/bridge/resources/bars/xhdpi/ic_sysbar_recent.png
+++ b/tools/layoutlib/bridge/resources/bars/v19/xhdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v19/xhdpi/stat_sys_wifi_signal_4_fully.png b/tools/layoutlib/bridge/resources/bars/v19/xhdpi/stat_sys_wifi_signal_4_fully.png
new file mode 100644
index 0000000..459a1a2
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v19/xhdpi/stat_sys_wifi_signal_4_fully.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v19/xxhdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/v19/xxhdpi/ic_sysbar_back.png
new file mode 100644
index 0000000..79cfcee
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v19/xxhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v19/xxhdpi/ic_sysbar_home.png b/tools/layoutlib/bridge/resources/bars/v19/xxhdpi/ic_sysbar_home.png
new file mode 100644
index 0000000..64f6a22
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v19/xxhdpi/ic_sysbar_home.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v19/xxhdpi/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/v19/xxhdpi/ic_sysbar_recent.png
new file mode 100644
index 0000000..6e0b071
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v19/xxhdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v19/xxhdpi/stat_sys_wifi_signal_4_fully.png b/tools/layoutlib/bridge/resources/bars/v19/xxhdpi/stat_sys_wifi_signal_4_fully.png
new file mode 100644
index 0000000..494b005
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v19/xxhdpi/stat_sys_wifi_signal_4_fully.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v21/hdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/v21/hdpi/ic_sysbar_back.png
new file mode 100644
index 0000000..b28624f
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v21/hdpi/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v21/hdpi/ic_sysbar_home.png b/tools/layoutlib/bridge/resources/bars/v21/hdpi/ic_sysbar_home.png
new file mode 100644
index 0000000..3f3e288
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v21/hdpi/ic_sysbar_home.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v21/hdpi/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/v21/hdpi/ic_sysbar_recent.png
new file mode 100644
index 0000000..06dcd20
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v21/hdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v21/hdpi/stat_sys_battery_100.png b/tools/layoutlib/bridge/resources/bars/v21/hdpi/stat_sys_battery_100.png
new file mode 100644
index 0000000..f17189a
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v21/hdpi/stat_sys_battery_100.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v21/ldrtl-hdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/v21/ldrtl-hdpi/ic_sysbar_back.png
new file mode 100644
index 0000000..e464347
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v21/ldrtl-hdpi/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v21/ldrtl-mdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/v21/ldrtl-mdpi/ic_sysbar_back.png
new file mode 100644
index 0000000..1b578a6
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v21/ldrtl-mdpi/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v21/ldrtl-xhdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/v21/ldrtl-xhdpi/ic_sysbar_back.png
new file mode 100644
index 0000000..373e84a
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v21/ldrtl-xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v21/ldrtl-xxhdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/v21/ldrtl-xxhdpi/ic_sysbar_back.png
new file mode 100644
index 0000000..6b19593
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v21/ldrtl-xxhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v21/mdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/v21/mdpi/ic_sysbar_back.png
new file mode 100644
index 0000000..f878093
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v21/mdpi/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v21/mdpi/ic_sysbar_home.png b/tools/layoutlib/bridge/resources/bars/v21/mdpi/ic_sysbar_home.png
new file mode 100644
index 0000000..8e9583b
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v21/mdpi/ic_sysbar_home.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v21/mdpi/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/v21/mdpi/ic_sysbar_recent.png
new file mode 100644
index 0000000..e2a89c3
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v21/mdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v21/mdpi/stat_sys_battery_100.png b/tools/layoutlib/bridge/resources/bars/v21/mdpi/stat_sys_battery_100.png
new file mode 100644
index 0000000..2a9757d
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v21/mdpi/stat_sys_battery_100.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v21/xhdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/v21/xhdpi/ic_sysbar_back.png
new file mode 100644
index 0000000..ec2951d
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v21/xhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v21/xhdpi/ic_sysbar_home.png b/tools/layoutlib/bridge/resources/bars/v21/xhdpi/ic_sysbar_home.png
new file mode 100644
index 0000000..254f757
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v21/xhdpi/ic_sysbar_home.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v21/xhdpi/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/v21/xhdpi/ic_sysbar_recent.png
new file mode 100644
index 0000000..8a8e941
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v21/xhdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v21/xhdpi/stat_sys_battery_100.png b/tools/layoutlib/bridge/resources/bars/v21/xhdpi/stat_sys_battery_100.png
new file mode 100644
index 0000000..555bcd9
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v21/xhdpi/stat_sys_battery_100.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v21/xhdpi/stat_sys_wifi_signal_4_fully.xml b/tools/layoutlib/bridge/resources/bars/v21/xhdpi/stat_sys_wifi_signal_4_fully.xml
new file mode 100644
index 0000000..0498b6c
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v21/xhdpi/stat_sys_wifi_signal_4_fully.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="32.0dp"
+ android:height="29.5dp"
+ android:viewportWidth="26.0"
+ android:viewportHeight="24.0">
+ <path
+ android:fillColor="#FFFFFFFF"
+ android:pathData="M13.000000,22.000000L25.600000,6.500000C25.100000,6.100000 20.299999,2.100000 13.000000,2.100000S0.900000,6.100000 0.400000,6.500000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000L13.000000,22.000000z"/>
+</vector>
diff --git a/tools/layoutlib/bridge/resources/bars/v21/xxhdpi/ic_sysbar_back.png b/tools/layoutlib/bridge/resources/bars/v21/xxhdpi/ic_sysbar_back.png
new file mode 100644
index 0000000..77969b8
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v21/xxhdpi/ic_sysbar_back.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v21/xxhdpi/ic_sysbar_home.png b/tools/layoutlib/bridge/resources/bars/v21/xxhdpi/ic_sysbar_home.png
new file mode 100644
index 0000000..d60229f
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v21/xxhdpi/ic_sysbar_home.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v21/xxhdpi/ic_sysbar_recent.png b/tools/layoutlib/bridge/resources/bars/v21/xxhdpi/ic_sysbar_recent.png
new file mode 100644
index 0000000..a261f85
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v21/xxhdpi/ic_sysbar_recent.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v21/xxhdpi/stat_sys_battery_100.png b/tools/layoutlib/bridge/resources/bars/v21/xxhdpi/stat_sys_battery_100.png
new file mode 100644
index 0000000..6474aad
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v21/xxhdpi/stat_sys_battery_100.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v9/hdpi/stat_sys_battery_100.png b/tools/layoutlib/bridge/resources/bars/v9/hdpi/stat_sys_battery_100.png
new file mode 100644
index 0000000..754cdf6
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v9/hdpi/stat_sys_battery_100.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v9/hdpi/stat_sys_wifi_signal_4_fully.png b/tools/layoutlib/bridge/resources/bars/v9/hdpi/stat_sys_wifi_signal_4_fully.png
new file mode 100644
index 0000000..b5326d2
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v9/hdpi/stat_sys_wifi_signal_4_fully.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v9/ldpi/stat_sys_battery_100.png b/tools/layoutlib/bridge/resources/bars/v9/ldpi/stat_sys_battery_100.png
new file mode 100644
index 0000000..7023ea7
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v9/ldpi/stat_sys_battery_100.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v9/mdpi/stat_sys_battery_100.png b/tools/layoutlib/bridge/resources/bars/v9/mdpi/stat_sys_battery_100.png
new file mode 100644
index 0000000..17a955d
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v9/mdpi/stat_sys_battery_100.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/v9/mdpi/stat_sys_wifi_signal_4_fully.png b/tools/layoutlib/bridge/resources/bars/v9/mdpi/stat_sys_wifi_signal_4_fully.png
new file mode 100644
index 0000000..19165ab
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/v9/mdpi/stat_sys_wifi_signal_4_fully.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi/stat_sys_battery_100.png b/tools/layoutlib/bridge/resources/bars/xhdpi/stat_sys_battery_100.png
new file mode 100644
index 0000000..555bcd9
--- /dev/null
+++ b/tools/layoutlib/bridge/resources/bars/xhdpi/stat_sys_battery_100.png
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi/stat_sys_battery_charge_anim100.png b/tools/layoutlib/bridge/resources/bars/xhdpi/stat_sys_battery_charge_anim100.png
deleted file mode 100644
index c7fd719..0000000
--- a/tools/layoutlib/bridge/resources/bars/xhdpi/stat_sys_battery_charge_anim100.png
+++ /dev/null
Binary files differ
diff --git a/tools/layoutlib/bridge/resources/bars/xhdpi/stat_sys_wifi_signal_4_fully.png b/tools/layoutlib/bridge/resources/bars/xhdpi/stat_sys_wifi_signal_4_fully.png
deleted file mode 100644
index 625c61d..0000000
--- a/tools/layoutlib/bridge/resources/bars/xhdpi/stat_sys_wifi_signal_4_fully.png
+++ /dev/null
Binary files differ
diff --git a/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java b/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
index 224eac6..4603b63 100644
--- a/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java
@@ -48,6 +48,20 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
}
@LayoutlibDelegate
+ /*package*/ static long nGetMultipleIntMethod(Class<?> targetClass, String methodName,
+ int numParams) {
+ // TODO: return the right thing.
+ return 0;
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static long nGetMultipleFloatMethod(Class<?> targetClass, String methodName,
+ int numParams) {
+ // TODO: return the right thing.
+ return 0;
+ }
+
+ @LayoutlibDelegate
/*package*/ static void nCallIntMethod(Object target, long methodID, int arg) {
// do nothing
}
@@ -56,4 +70,40 @@ import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
/*package*/ static void nCallFloatMethod(Object target, long methodID, float arg) {
// do nothing
}
+
+ @LayoutlibDelegate
+ /*package*/ static void nCallTwoIntMethod(Object target, long methodID, int arg1,
+ int arg2) {
+ // do nothing
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nCallFourIntMethod(Object target, long methodID, int arg1,
+ int arg2, int arg3, int arg4) {
+ // do nothing
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nCallMultipleIntMethod(Object target, long methodID,
+ int[] args) {
+ // do nothing
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nCallTwoFloatMethod(Object target, long methodID, float arg1,
+ float arg2) {
+ // do nothing
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nCallFourFloatMethod(Object target, long methodID, float arg1,
+ float arg2, float arg3, float arg4) {
+ // do nothing
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void nCallMultipleFloatMethod(Object target, long methodID,
+ float[] args) {
+ // do nothing
+ }
}
diff --git a/tools/layoutlib/bridge/src/android/content/res/AssetManager_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/AssetManager_Delegate.java
new file mode 100644
index 0000000..914a359
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/content/res/AssetManager_Delegate.java
@@ -0,0 +1,45 @@
+/*
+ * 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.content.res;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate used to provide implementation of a select few native methods of {@link AssetManager}
+ * <p/>
+ * Through the layoutlib_create tool, the original native methods of AssetManager have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ */
+public class AssetManager_Delegate {
+
+ @LayoutlibDelegate
+ /*package*/ static long newTheme(AssetManager manager) {
+ return Resources_Theme_Delegate.getDelegateManager()
+ .addNewDelegate(new Resources_Theme_Delegate());
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void deleteTheme(AssetManager manager, long theme) {
+ Resources_Theme_Delegate.getDelegateManager().removeJavaReferenceFor(theme);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void applyThemeStyle(long theme, int styleRes, boolean force) {
+ Resources_Theme_Delegate.getDelegateManager().getDelegate(theme).force = force;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeAssetManager.java b/tools/layoutlib/bridge/src/android/content/res/BridgeAssetManager.java
index a953918..93814b2 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeAssetManager.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeAssetManager.java
@@ -35,7 +35,7 @@ public class BridgeAssetManager extends AssetManager {
// Note that AssetManager() creates a system AssetManager and we override it
// with our BridgeAssetManager.
AssetManager.sSystem = new BridgeAssetManager();
- AssetManager.sSystem.makeStringBlocks(false);
+ AssetManager.sSystem.makeStringBlocks(null);
}
return AssetManager.sSystem;
}
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
index 8794452..dd573be 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
@@ -51,6 +51,7 @@ public final class BridgeResources extends Resources {
private BridgeContext mContext;
private IProjectCallback mProjectCallback;
private boolean[] mPlatformResourceFlag = new boolean[1];
+ private TypedValue mTmpValue = new TypedValue();
/**
* Simpler wrapper around FileInputStream. This is used when the input stream represent
@@ -154,6 +155,11 @@ public final class BridgeResources extends Resources {
@Override
public Drawable getDrawable(int id) throws NotFoundException {
+ return getDrawable(id, null);
+ }
+
+ @Override
+ public Drawable getDrawable(int id, Theme theme) {
Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
if (value != null) {
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
index 446d139..28a109d 100644
--- a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -23,7 +23,6 @@ import com.android.ide.common.rendering.api.ResourceValue;
import com.android.ide.common.rendering.api.StyleResourceValue;
import com.android.internal.util.XmlUtils;
import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.BridgeConstants;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
import com.android.layoutlib.bridge.impl.ParserFactory;
@@ -52,13 +51,13 @@ public final class BridgeTypedArray extends TypedArray {
private final BridgeContext mContext;
private final boolean mPlatformFile;
- private ResourceValue[] mResourceData;
- private String[] mNames;
- private boolean[] mIsFramework;
+ private final ResourceValue[] mResourceData;
+ private final String[] mNames;
+ private final boolean[] mIsFramework;
public BridgeTypedArray(BridgeResources resources, BridgeContext context, int len,
boolean platformFile) {
- super(null, null, null, 0);
+ super(resources, null, null, 0);
mBridgeResources = resources;
mContext = context;
mPlatformFile = platformFile;
@@ -81,8 +80,8 @@ public final class BridgeTypedArray extends TypedArray {
}
/**
- * Seals the array after all calls to {@link #bridgeSetValue(int, String, ResourceValue)} have
- * been done.
+ * Seals the array after all calls to
+ * {@link #bridgeSetValue(int, String, boolean, ResourceValue)} have been done.
* <p/>This allows to compute the list of non default values, permitting
* {@link #getIndexCount()} to return the proper value.
*/
@@ -90,9 +89,16 @@ public final class BridgeTypedArray extends TypedArray {
// fills TypedArray.mIndices which is used to implement getIndexCount/getIndexAt
// first count the array size
int count = 0;
- for (ResourceValue data : mResourceData) {
+ for (int i = 0; i < mResourceData.length; i++) {
+ ResourceValue data = mResourceData[i];
if (data != null) {
- count++;
+ if (RenderResources.REFERENCE_NULL.equals(data.getValue())) {
+ // No need to store this resource value. This saves needless checking for
+ // "@null" every time an attribute is requested.
+ mResourceData[i] = null;
+ } else {
+ count++;
+ }
}
}
@@ -135,16 +141,8 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public CharSequence getText(int index) {
- if (index < 0 || index >= mResourceData.length) {
- return null;
- }
-
- if (mResourceData[index] != null) {
- // FIXME: handle styled strings!
- return mResourceData[index].getValue();
- }
-
- return null;
+ // FIXME: handle styled strings!
+ return getString(index);
}
/**
@@ -157,15 +155,14 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public String getString(int index) {
- if (index < 0 || index >= mResourceData.length) {
+ if (!hasValue(index)) {
return null;
}
-
- if (mResourceData[index] != null) {
- return mResourceData[index].getValue();
- }
-
- return null;
+ // As unfortunate as it is, it's possible to use enums with all attribute formats,
+ // not just integers/enums. So, we need to search the enums always. In case
+ // enums are used, the returned value is an integer.
+ Integer v = resolveEnumAttribute(index);
+ return v == null ? mResourceData[index].getValue() : String.valueOf((int) v);
}
/**
@@ -178,20 +175,9 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public boolean getBoolean(int index, boolean defValue) {
- if (index < 0 || index >= mResourceData.length) {
- return defValue;
- }
-
- if (mResourceData[index] == null) {
- return defValue;
- }
+ String s = getString(index);
+ return s == null ? defValue : XmlUtils.convertValueToBoolean(s, defValue);
- String s = mResourceData[index].getValue();
- if (s != null) {
- return XmlUtils.convertValueToBoolean(s, defValue);
- }
-
- return defValue;
}
/**
@@ -204,65 +190,18 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public int getInt(int index, int defValue) {
- if (index < 0 || index >= mResourceData.length) {
- return defValue;
- }
-
- if (mResourceData[index] == null) {
- return defValue;
- }
-
- String s = mResourceData[index].getValue();
-
- if (RenderResources.REFERENCE_NULL.equals(s)) {
- return defValue;
- }
-
- if (s == null || s.length() == 0) {
- return defValue;
- }
-
+ String s = getString(index);
try {
- return XmlUtils.convertValueToInt(s, defValue);
- } catch (NumberFormatException e) {
- // pass
- }
-
- // Field is not null and is not an integer.
- // Check for possible constants and try to find them.
- // Get the map of attribute-constant -> IntegerValue
- Map<String, Integer> map = null;
- if (mIsFramework[index]) {
- map = Bridge.getEnumValues(mNames[index]);
- } else {
- // get the styleable matching the resolved name
- RenderResources res = mContext.getRenderResources();
- ResourceValue attr = res.getProjectResource(ResourceType.ATTR, mNames[index]);
- if (attr instanceof AttrResourceValue) {
- map = ((AttrResourceValue) attr).getAttributeValues();
+ if (s != null) {
+ return XmlUtils.convertValueToInt(s, defValue);
}
+ } catch (NumberFormatException e) {
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+ String.format("\"%1$s\" in attribute \"%2$s\" is not a valid integer",
+ s, mNames[index]),
+ null);
+ return defValue;
}
-
- if (map != null) {
- // accumulator to store the value of the 1+ constants.
- int result = 0;
-
- // split the value in case this is a mix of several flags.
- String[] keywords = s.split("\\|");
- for (String keyword : keywords) {
- Integer i = map.get(keyword.trim());
- if (i != null) {
- result |= i.intValue();
- } else {
- Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
- String.format(
- "\"%s\" in attribute \"%2$s\" is not a valid value",
- keyword, mNames[index]), null /*data*/);
- }
- }
- return result;
- }
-
return defValue;
}
@@ -275,27 +214,16 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public float getFloat(int index, float defValue) {
- if (index < 0 || index >= mResourceData.length) {
- return defValue;
- }
-
- if (mResourceData[index] == null) {
- return defValue;
- }
-
- String s = mResourceData[index].getValue();
-
- if (s != null) {
- try {
- return Float.parseFloat(s);
- } catch (NumberFormatException e) {
- Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
- String.format(
- "\"%s\" in attribute \"%2$s\" cannot be converted to float.",
- s, mNames[index]), null /*data*/);
-
- // we'll return the default value below.
+ String s = getString(index);
+ try {
+ if (s != null) {
+ return Float.parseFloat(s);
}
+ } catch (NumberFormatException e) {
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+ String.format("\"%1$s\" in attribute \"%2$s\" cannot be converted to float.",
+ s, mNames[index]),
+ null);
}
return defValue;
}
@@ -342,11 +270,7 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public ColorStateList getColorStateList(int index) {
- if (index < 0 || index >= mResourceData.length) {
- return null;
- }
-
- if (mResourceData[index] == null) {
+ if (!hasValue(index)) {
return null;
}
@@ -357,10 +281,6 @@ public final class BridgeTypedArray extends TypedArray {
return null;
}
- if (RenderResources.REFERENCE_NULL.equals(value)) {
- return null;
- }
-
// let the framework inflate the ColorStateList from the XML file.
File f = new File(value);
if (f.isFile()) {
@@ -376,13 +296,13 @@ public final class BridgeTypedArray extends TypedArray {
}
} catch (XmlPullParserException e) {
Bridge.getLog().error(LayoutLog.TAG_BROKEN,
- "Failed to configure parser for " + value, e, null /*data*/);
+ "Failed to configure parser for " + value, e, null);
return null;
} catch (Exception e) {
// this is an error and not warning since the file existence is checked before
// attempting to parse it.
Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
- "Failed to parse file " + value, e, null /*data*/);
+ "Failed to parse file " + value, e, null);
return null;
}
@@ -392,7 +312,7 @@ public final class BridgeTypedArray extends TypedArray {
int color = ResourceHelper.getColor(value);
return ColorStateList.valueOf(color);
} catch (NumberFormatException e) {
- Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, e.getMessage(), e, null /*data*/);
+ Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, e.getMessage(), e, null);
}
return null;
@@ -430,37 +350,24 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public float getDimension(int index, float defValue) {
- if (index < 0 || index >= mResourceData.length) {
- return defValue;
- }
-
- if (mResourceData[index] == null) {
- return defValue;
- }
-
- String s = mResourceData[index].getValue();
-
+ String s = getString(index);
if (s == null) {
return defValue;
- } else if (s.equals(BridgeConstants.MATCH_PARENT) ||
- s.equals(BridgeConstants.FILL_PARENT)) {
- return LayoutParams.MATCH_PARENT;
- } else if (s.equals(BridgeConstants.WRAP_CONTENT)) {
- return LayoutParams.WRAP_CONTENT;
- } else if (RenderResources.REFERENCE_NULL.equals(s)) {
- return defValue;
+ }
+ // Check if the value is a magic constant that doesn't require a unit.
+ try {
+ int i = Integer.parseInt(s);
+ if (i == LayoutParams.MATCH_PARENT || i == LayoutParams.WRAP_CONTENT) {
+ return i;
+ }
+ } catch (NumberFormatException ignored) {
+ // pass
}
- if (ResourceHelper.parseFloatAttribute(mNames[index], s, mValue, true /*requireUnit*/)) {
+ if (ResourceHelper.parseFloatAttribute(mNames[index], s, mValue, true)) {
return mValue.getDimension(mBridgeResources.getDisplayMetrics());
}
- // looks like we were unable to resolve the dimension value
- Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
- String.format(
- "\"%1$s\" in attribute \"%2$s\" is not a valid format.",
- s, mNames[index]), null /*data*/);
-
return defValue;
}
@@ -509,16 +416,13 @@ public final class BridgeTypedArray extends TypedArray {
try {
return getDimension(index);
} catch (RuntimeException e) {
- if (mResourceData[index] != null) {
- String s = mResourceData[index].getValue();
-
- if (s != null) {
- // looks like we were unable to resolve the dimension value
- Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
- String.format(
- "\"%1$s\" in attribute \"%2$s\" is not a valid format.",
- s, mNames[index]), null /*data*/);
- }
+ String s = getString(index);
+
+ if (s != null) {
+ // looks like we were unable to resolve the dimension value
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+ String.format("\"%1$s\" in attribute \"%2$s\" is not a valid format.",
+ s, mNames[index]), null);
}
return defValue;
@@ -561,24 +465,20 @@ public final class BridgeTypedArray extends TypedArray {
}
private int getDimension(int index) {
- if (mResourceData[index] == null) {
- throw new RuntimeException();
- }
-
- String s = mResourceData[index].getValue();
-
+ String s = getString(index);
if (s == null) {
throw new RuntimeException();
- } else if (s.equals(BridgeConstants.MATCH_PARENT) ||
- s.equals(BridgeConstants.FILL_PARENT)) {
- return LayoutParams.MATCH_PARENT;
- } else if (s.equals(BridgeConstants.WRAP_CONTENT)) {
- return LayoutParams.WRAP_CONTENT;
- } else if (RenderResources.REFERENCE_NULL.equals(s)) {
- throw new RuntimeException();
}
-
- if (ResourceHelper.parseFloatAttribute(mNames[index], s, mValue, true /*requireUnit*/)) {
+ // Check if the value is a magic constant that doesn't require a unit.
+ try {
+ int i = Integer.parseInt(s);
+ if (i == LayoutParams.MATCH_PARENT || i == LayoutParams.WRAP_CONTENT) {
+ return i;
+ }
+ } catch (NumberFormatException ignored) {
+ // pass
+ }
+ if (ResourceHelper.parseFloatAttribute(mNames[index], s, mValue, true)) {
float f = mValue.getDimension(mBridgeResources.getDisplayMetrics());
final int res = (int)(f+0.5f);
@@ -607,29 +507,20 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public float getFraction(int index, int base, int pbase, float defValue) {
- if (index < 0 || index >= mResourceData.length) {
- return defValue;
- }
-
- if (mResourceData[index] == null) {
- return defValue;
- }
-
- String value = mResourceData[index].getValue();
+ String value = getString(index);
if (value == null) {
return defValue;
}
- if (ResourceHelper.parseFloatAttribute(mNames[index], value, mValue,
- false /*requireUnit*/)) {
+ if (ResourceHelper.parseFloatAttribute(mNames[index], value, mValue, false)) {
return mValue.getFraction(base, pbase);
}
// looks like we were unable to resolve the fraction value
Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
String.format(
- "\"%1$s\" in attribute \"%2$s\" cannot be converted to a fraction.",
- value, mNames[index]), null /*data*/);
+ "\"%1$s\" in attribute \"%2$s\" cannot be converted to a fraction.",
+ value, mNames[index]), null);
return defValue;
}
@@ -668,10 +559,6 @@ public final class BridgeTypedArray extends TypedArray {
return mContext.getDynamicIdByStyle((StyleResourceValue)resValue);
}
- if (RenderResources.REFERENCE_NULL.equals(resValue.getValue())) {
- return defValue;
- }
-
// if the attribute was a reference to a resource, and not a declaration of an id (@+id),
// then the xml attribute value was "resolved" which leads us to a ResourceValue with a
// valid getType() and getName() returning a resource name.
@@ -731,7 +618,7 @@ public final class BridgeTypedArray extends TypedArray {
}
// not a direct id valid reference? resolve it
- Integer idValue = null;
+ Integer idValue;
if (resValue.isFramework()) {
idValue = Bridge.getResourceId(resValue.getResourceType(),
@@ -742,7 +629,7 @@ public final class BridgeTypedArray extends TypedArray {
}
if (idValue != null) {
- return idValue.intValue();
+ return idValue;
}
Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE,
@@ -753,6 +640,12 @@ public final class BridgeTypedArray extends TypedArray {
return defValue;
}
+ @Override
+ public int getThemeAttributeId(int index, int defValue) {
+ // TODO: Get the right Theme Attribute ID to enable caching of the drawables.
+ return defValue;
+ }
+
/**
* Retrieve the Drawable for the attribute at <var>index</var>. This
* gets the resource ID of the selected attribute, and uses
@@ -765,20 +658,11 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public Drawable getDrawable(int index) {
- if (index < 0 || index >= mResourceData.length) {
- return null;
- }
-
- if (mResourceData[index] == null) {
+ if (!hasValue(index)) {
return null;
}
ResourceValue value = mResourceData[index];
- String stringValue = value.getValue();
- if (stringValue == null || RenderResources.REFERENCE_NULL.equals(stringValue)) {
- return null;
- }
-
return ResourceHelper.getDrawable(value, mContext);
}
@@ -795,31 +679,28 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public CharSequence[] getTextArray(int index) {
- if (index < 0 || index >= mResourceData.length) {
- return null;
- }
-
- if (mResourceData[index] == null) {
- return null;
- }
-
- String value = mResourceData[index].getValue();
+ String value = getString(index);
if (value != null) {
- if (RenderResources.REFERENCE_NULL.equals(value)) {
- return null;
- }
-
return new CharSequence[] { value };
}
- Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
- String.format(
- String.format("Unknown value for getTextArray(%d) => %s", //DEBUG
- index, mResourceData[index].getName())), null /*data*/);
+ return null;
+ }
+ @Override
+ public int[] extractThemeAttrs() {
+ // The drawables are always inflated with a Theme and we don't care about caching. So,
+ // just return.
return null;
}
+ @Override
+ public int getChangingConfigurations() {
+ // We don't care about caching. Any change in configuration is a fresh render. So,
+ // just return.
+ return 0;
+ }
+
/**
* Retrieve the raw TypedValue for the attribute at <var>index</var>.
*
@@ -831,18 +712,8 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public boolean getValue(int index, TypedValue outValue) {
- if (index < 0 || index >= mResourceData.length) {
- return false;
- }
-
- if (mResourceData[index] == null) {
- return false;
- }
-
- String s = mResourceData[index].getValue();
-
- return ResourceHelper.parseFloatAttribute(mNames[index], s, outValue,
- false /*requireUnit*/);
+ String s = getString(index);
+ return s != null && ResourceHelper.parseFloatAttribute(mNames[index], s, outValue, false);
}
/**
@@ -854,11 +725,7 @@ public final class BridgeTypedArray extends TypedArray {
*/
@Override
public boolean hasValue(int index) {
- if (index < 0 || index >= mResourceData.length) {
- return false;
- }
-
- return mResourceData[index] != null;
+ return index >= 0 && index < mResourceData.length && mResourceData[index] != null;
}
/**
@@ -905,4 +772,55 @@ public final class BridgeTypedArray extends TypedArray {
public String toString() {
return Arrays.toString(mResourceData);
}
- }
+
+ /**
+ * Searches for the string in the attributes (flag or enums) and returns the integer.
+ * If found, it will return an integer matching the value.
+ *
+ * @param index Index of attribute to retrieve.
+ *
+ * @return Attribute int value, or null if not defined.
+ */
+ private Integer resolveEnumAttribute(int index) {
+ // Get the map of attribute-constant -> IntegerValue
+ Map<String, Integer> map = null;
+ if (mIsFramework[index]) {
+ map = Bridge.getEnumValues(mNames[index]);
+ } else {
+ // get the styleable matching the resolved name
+ RenderResources res = mContext.getRenderResources();
+ ResourceValue attr = res.getProjectResource(ResourceType.ATTR, mNames[index]);
+ if (attr instanceof AttrResourceValue) {
+ map = ((AttrResourceValue) attr).getAttributeValues();
+ }
+ }
+
+ if (map != null) {
+ // accumulator to store the value of the 1+ constants.
+ int result = 0;
+ boolean found = false;
+
+ // split the value in case this is a mix of several flags.
+ String[] keywords = mResourceData[index].getValue().split("\\|");
+ for (String keyword : keywords) {
+ Integer i = map.get(keyword.trim());
+ if (i != null) {
+ result |= i;
+ found = true;
+ }
+ // TODO: We should act smartly and log a warning for incorrect keywords. However,
+ // this method is currently called even if the resourceValue is not an enum.
+ }
+ if (found) {
+ return result;
+ }
+ }
+
+ return null;
+ }
+
+ static TypedArray obtain(Resources res, int len) {
+ return res instanceof BridgeResources ?
+ new BridgeTypedArray(((BridgeResources) res), null, len, true) : null;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java
index c9d615c..f4a9f52 100644
--- a/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/content/res/Resources_Theme_Delegate.java
@@ -16,7 +16,13 @@
package android.content.res;
+import com.android.annotations.Nullable;
+import com.android.ide.common.rendering.api.ResourceReference;
+import com.android.ide.common.rendering.api.StyleResourceValue;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.layoutlib.bridge.impl.RenderSessionImpl;
+import com.android.resources.ResourceType;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import android.content.res.Resources.NotFoundException;
@@ -25,7 +31,7 @@ import android.util.AttributeSet;
import android.util.TypedValue;
/**
- * Delegate used to provide new implementation of a select few methods of {@link Resources$Theme}
+ * Delegate used to provide new implementation of a select few methods of {@link Resources.Theme}
*
* Through the layoutlib_create tool, the original methods of Theme have been replaced
* by calls to methods of the same name in this delegate class.
@@ -33,11 +39,28 @@ import android.util.TypedValue;
*/
public class Resources_Theme_Delegate {
+ // Whether to use the Theme.mThemeResId as primary theme.
+ boolean force;
+
+ // ---- delegate manager ----
+
+ private static final DelegateManager<Resources_Theme_Delegate> sManager =
+ new DelegateManager<Resources_Theme_Delegate>(Resources_Theme_Delegate.class);
+
+ public static DelegateManager<Resources_Theme_Delegate> getDelegateManager() {
+ return sManager;
+ }
+
+ // ---- delegate methods. ----
+
@LayoutlibDelegate
/*package*/ static TypedArray obtainStyledAttributes(
Resources thisResources, Theme thisTheme,
int[] attrs) {
- return RenderSessionImpl.getCurrentContext().obtainStyledAttributes(attrs);
+ boolean changed = setupResources(thisTheme);
+ TypedArray ta = RenderSessionImpl.getCurrentContext().obtainStyledAttributes(attrs);
+ restoreResources(changed);
+ return ta;
}
@LayoutlibDelegate
@@ -45,15 +68,21 @@ public class Resources_Theme_Delegate {
Resources thisResources, Theme thisTheme,
int resid, int[] attrs)
throws NotFoundException {
- return RenderSessionImpl.getCurrentContext().obtainStyledAttributes(resid, attrs);
+ boolean changed = setupResources(thisTheme);
+ TypedArray ta = RenderSessionImpl.getCurrentContext().obtainStyledAttributes(resid, attrs);
+ restoreResources(changed);
+ return ta;
}
@LayoutlibDelegate
/*package*/ static TypedArray obtainStyledAttributes(
Resources thisResources, Theme thisTheme,
AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) {
- return RenderSessionImpl.getCurrentContext().obtainStyledAttributes(
- set, attrs, defStyleAttr, defStyleRes);
+ boolean changed = setupResources(thisTheme);
+ TypedArray ta = RenderSessionImpl.getCurrentContext().obtainStyledAttributes(set, attrs,
+ defStyleAttr, defStyleRes);
+ restoreResources(changed);
+ return ta;
}
@LayoutlibDelegate
@@ -61,7 +90,52 @@ public class Resources_Theme_Delegate {
Resources thisResources, Theme thisTheme,
int resid, TypedValue outValue,
boolean resolveRefs) {
- return RenderSessionImpl.getCurrentContext().resolveThemeAttribute(
+ boolean changed = setupResources(thisTheme);
+ boolean found = RenderSessionImpl.getCurrentContext().resolveThemeAttribute(
resid, outValue, resolveRefs);
+ restoreResources(changed);
+ return found;
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static TypedArray resolveAttributes(Resources thisResources, Theme thisTheme,
+ int[] values, int[] attrs) {
+ // FIXME
+ return null;
+ }
+
+ // ---- private helper methods ----
+
+ private static boolean setupResources(Theme thisTheme) {
+ Resources_Theme_Delegate themeDelegate = sManager.getDelegate(thisTheme.getNativeTheme());
+ StyleResourceValue style = resolveStyle(thisTheme.getAppliedStyleResId());
+ if (style != null) {
+ RenderSessionImpl.getCurrentContext().getRenderResources()
+ .applyStyle(style, themeDelegate.force);
+ return true;
+ }
+ return false;
+ }
+
+ private static void restoreResources(boolean changed) {
+ if (changed) {
+ RenderSessionImpl.getCurrentContext().getRenderResources().clearStyles();
+ }
+ }
+
+ @Nullable
+ private static StyleResourceValue resolveStyle(int nativeResid) {
+ if (nativeResid == 0) {
+ return null;
+ }
+ BridgeContext context = RenderSessionImpl.getCurrentContext();
+ ResourceReference theme = context.resolveId(nativeResid);
+ if (theme.isFramework()) {
+ return (StyleResourceValue) context.getRenderResources()
+ .getFrameworkResource(ResourceType.STYLE, theme.getName());
+ } else {
+ return (StyleResourceValue) context.getRenderResources()
+ .getProjectResource(ResourceType.STYLE, theme.getName());
+ }
}
}
diff --git a/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java b/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java
index 0a7899a..faa8852 100644
--- a/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/content/res/TypedArray_Delegate.java
@@ -27,4 +27,9 @@ public class TypedArray_Delegate {
// pass
return false;
}
+
+ @LayoutlibDelegate
+ /*package*/ static TypedArray obtain(Resources res, int len) {
+ return BridgeTypedArray.obtain(res, len);
+ }
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
index 802cf1c..a4a3b7d 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BidiRenderer.java
@@ -16,16 +16,23 @@
package android.graphics;
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+
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.Rectangle2D;
+import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import com.ibm.icu.lang.UScript;
import com.ibm.icu.lang.UScriptRun;
+import com.ibm.icu.text.Bidi;
+import com.ibm.icu.text.BidiRun;
import android.graphics.Paint_Delegate.FontInfo;
@@ -36,12 +43,12 @@ import android.graphics.Paint_Delegate.FontInfo;
@SuppressWarnings("deprecation")
public class BidiRenderer {
- /* package */ static class ScriptRun {
+ private static class ScriptRun {
int start;
int limit;
boolean isRtl;
int scriptCode;
- FontInfo font;
+ Font font;
public ScriptRun(int start, int limit, boolean isRtl) {
this.start = start;
@@ -51,9 +58,12 @@ public class BidiRenderer {
}
}
- private Graphics2D mGraphics;
- private Paint_Delegate mPaint;
+ private final Graphics2D mGraphics;
+ private final Paint_Delegate mPaint;
private char[] mText;
+ // This List can contain nulls. A null font implies that the we weren't able to load the font
+ // properly. So, if we encounter a situation where we try to use that font, log a warning.
+ private List<Font> mFonts;
// Bounds of the text drawn so far.
private RectF mBounds;
private float mBaseline;
@@ -63,18 +73,58 @@ public class BidiRenderer {
* @param paint The Paint to use to get the fonts. Should not be null.
* @param text Unidirectional text. Should not be null.
*/
- /* package */ BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) {
+ public BidiRenderer(Graphics2D graphics, Paint_Delegate paint, char[] text) {
assert (paint != null);
mGraphics = graphics;
mPaint = paint;
mText = text;
+ mFonts = new ArrayList<Font>(paint.getFonts().size());
+ for (FontInfo fontInfo : paint.getFonts()) {
+ if (fontInfo == null) {
+ mFonts.add(null);
+ continue;
+ }
+ mFonts.add(fontInfo.mFont);
+ }
+ mBounds = new RectF();
}
/**
- * Render unidirectional text.
*
- * This method can also be used to measure the width of the text without actually drawing it.
+ * @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 = new RectF(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) {
+ Bidi bidi = new Bidi(mText, start, null, 0, limit - start, getIcuFlags(bidiFlags));
+ for (int i = 0; i < bidi.countRuns(); i++) {
+ BidiRun visualRun = bidi.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
@@ -83,18 +133,13 @@ public class BidiRenderer {
* @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
- * @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.
* @return A rectangle specifying the bounds of the text drawn.
*/
- /* package */ RectF renderText(int start, int limit, boolean isRtl, float[] advances,
- int advancesIndex, boolean draw, float x, float y) {
+ 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.
- mBounds = new RectF(x, y, x, y);
- mBaseline = y;
- for (ScriptRun run : getScriptRuns(mText, start, limit, isRtl, mPaint.getFonts())) {
+ for (ScriptRun run : getScriptRuns(mText, start, limit, isRtl, mFonts)) {
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);
@@ -108,16 +153,15 @@ public class BidiRenderer {
* 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, FontInfo preferredFont, int flag,
+ private void renderScript(int start, int limit, Font preferredFont, int flag,
float[] advances, int advancesIndex, boolean draw) {
- List<FontInfo> fonts = mPaint.getFonts();
- if (fonts == null || preferredFont == null) {
+ if (mFonts.size() == 0 || preferredFont == null) {
return;
}
while (start < limit) {
boolean foundFont = false;
- int canDisplayUpTo = preferredFont.mFont.canDisplayUpTo(mText, 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);
@@ -133,8 +177,12 @@ public class BidiRenderer {
// The current character cannot be drawn with the preferred font. Cycle through all the
// fonts to check which one can draw it.
int charCount = Character.isHighSurrogate(mText[start]) ? 2 : 1;
- for (FontInfo font : fonts) {
- canDisplayUpTo = font.mFont.canDisplayUpTo(mText, start, start + charCount);
+ for (Font font : mFonts) {
+ if (font == null) {
+ logFontWarning();
+ continue;
+ }
+ canDisplayUpTo = font.canDisplayUpTo(mText, start, start + charCount);
if (canDisplayUpTo == -1) {
render(start, start+charCount, font, flag, advances, advancesIndex, draw);
start += charCount;
@@ -156,19 +204,29 @@ public class BidiRenderer {
}
}
+ private static void logFontWarning() {
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
+ "Some fonts could not be loaded. The rendering may not be perfect. " +
+ "Try running the IDE with JRE 7.", 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, FontInfo font, int flag, float[] advances,
+ private void render(int start, int limit, Font font, int flag, float[] advances,
int advancesIndex, boolean draw) {
- // Since the metrics don't have anti-aliasing set, we create a new FontRenderContext with
- // the anti-aliasing set.
- FontRenderContext f = font.mMetrics.getFontRenderContext();
- FontRenderContext frc = new FontRenderContext(f.getTransform(), mPaint.isAntiAliased(),
- f.usesFractionalMetrics());
- GlyphVector gv = font.mFont.layoutGlyphVector(frc, mText, start, limit, flag);
+ FontRenderContext frc;
+ if (mGraphics != null) {
+ frc = mGraphics.getFontRenderContext();
+ } else {
+ frc = Toolkit.getDefaultToolkit().getFontMetrics(font).getFontRenderContext();
+ // Metrics obtained this way don't have anti-aliasing set. So,
+ // we create a new FontRenderContext with anti-aliasing set.
+ frc = new FontRenderContext(font.getTransform(), mPaint.isAntiAliased(), 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) {
@@ -206,7 +264,7 @@ public class BidiRenderer {
}
/* package */ static List<ScriptRun> getScriptRuns(char[] text, int start, int limit,
- boolean isRtl, List<FontInfo> fonts) {
+ boolean isRtl, List<Font> fonts) {
LinkedList<ScriptRun> scriptRuns = new LinkedList<ScriptRun>();
int count = limit - start;
@@ -225,13 +283,35 @@ public class BidiRenderer {
// TODO: Replace this method with one which returns the font based on the scriptCode.
private static void setScriptFont(char[] text, ScriptRun run,
- List<FontInfo> fonts) {
- for (FontInfo fontInfo : fonts) {
- if (fontInfo.mFont.canDisplayUpTo(text, run.start, run.limit) == -1) {
- run.font = fontInfo;
+ List<Font> fonts) {
+ for (Font font : fonts) {
+ if (font == null) {
+ logFontWarning();
+ continue;
+ }
+ if (font.canDisplayUpTo(text, run.start, run.limit) == -1) {
+ run.font = font;
return;
}
}
run.font = fonts.get(0);
}
+
+ 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/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
index 06673c1..9cf777d 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
@@ -16,6 +16,7 @@
package android.graphics;
+import com.android.annotations.Nullable;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.ninepatch.NinePatchChunk;
@@ -48,7 +49,7 @@ import java.util.Set;
@LayoutlibDelegate
/*package*/ static Bitmap nativeDecodeStream(InputStream is, byte[] storage,
- Rect padding, Options opts) {
+ @Nullable Rect padding, @Nullable Options opts) {
Bitmap bm = null;
Density density = Density.MEDIUM;
@@ -77,18 +78,20 @@ import java.util.Set;
// put the chunk in the bitmap
bm.setNinePatchChunk(NinePatch_Delegate.serialize(chunk));
- // read the padding
- int[] paddingarray = chunk.getPadding();
- padding.left = paddingarray[0];
- padding.top = paddingarray[1];
- padding.right = paddingarray[2];
- padding.bottom = paddingarray[3];
+ 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);
+ Bridge.getLog().error(null, "Failed to load image", e, null);
}
return bm;
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java
index cdbbe46..610c867 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapShader_Delegate.java
@@ -79,13 +79,6 @@ public class BitmapShader_Delegate extends Shader_Delegate {
return sManager.addNewDelegate(newDelegate);
}
- @LayoutlibDelegate
- /*package*/ static long nativePostCreate(long native_shader, long native_bitmap,
- int shaderTileModeX, int shaderTileModeY) {
- // pass, not needed.
- return 0;
- }
-
// ---- Private delegate/helper methods ----
private BitmapShader_Delegate(java.awt.image.BufferedImage image,
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
index 89d7e23..f4282ad 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java
@@ -53,6 +53,7 @@ import javax.imageio.ImageIO;
*/
public final class Bitmap_Delegate {
+
public enum BitmapCreateFlags {
PREMULTIPLIED, MUTABLE
}
@@ -68,6 +69,7 @@ public final class Bitmap_Delegate {
private BufferedImage mImage;
private boolean mHasAlpha = true;
private boolean mHasMipMap = false; // TODO: check the default.
+ private boolean mIsPremultiplied = true;
private int mGenerationId = 0;
@@ -315,7 +317,7 @@ public final class Bitmap_Delegate {
@LayoutlibDelegate
/*package*/ static void nativeReconfigure(long nativeBitmap, int width, int height,
- int config, int allocSize) {
+ int config, int allocSize, boolean isPremultiplied) {
Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
"Bitmap.reconfigure() is not supported", null /*data*/);
}
@@ -393,21 +395,19 @@ public final class Bitmap_Delegate {
}
@LayoutlibDelegate
- /*package*/ static int nativeGetPixel(long nativeBitmap, int x, int y,
- boolean isPremultiplied) {
+ /*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;
}
- // TODO: Support isPremultiplied.
return delegate.mImage.getRGB(x, y);
}
@LayoutlibDelegate
/*package*/ static void nativeGetPixels(long nativeBitmap, int[] pixels, int offset,
- int stride, int x, int y, int width, int height, boolean isPremultiplied) {
+ int stride, int x, int y, int width, int height) {
Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
if (delegate == null) {
return;
@@ -418,8 +418,7 @@ public final class Bitmap_Delegate {
@LayoutlibDelegate
- /*package*/ static void nativeSetPixel(long nativeBitmap, int x, int y, int color,
- boolean isPremultiplied) {
+ /*package*/ static void nativeSetPixel(long nativeBitmap, int x, int y, int color) {
Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
if (delegate == null) {
return;
@@ -430,7 +429,7 @@ public final class Bitmap_Delegate {
@LayoutlibDelegate
/*package*/ static void nativeSetPixels(long nativeBitmap, int[] colors, int offset,
- int stride, int x, int y, int width, int height, boolean isPremultiplied) {
+ int stride, int x, int y, int width, int height) {
Bitmap_Delegate delegate = sManager.getDelegate(nativeBitmap);
if (delegate == null) {
return;
@@ -518,7 +517,27 @@ public final class Bitmap_Delegate {
}
@LayoutlibDelegate
- /*package*/ static void nativeSetHasAlpha(long nativeBitmap, boolean hasAlpha) {
+ /*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) {
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
index 73d274c..be75dde 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java
@@ -20,6 +20,7 @@ import com.android.ide.common.rendering.api.LayoutLog;
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.tools.layoutlib.annotations.LayoutlibDelegate;
import android.graphics.Bitmap.Config;
@@ -55,23 +56,26 @@ public final class Canvas_Delegate {
private static final DelegateManager<Canvas_Delegate> sManager =
new DelegateManager<Canvas_Delegate>(Canvas_Delegate.class);
+
// ---- delegate helper data ----
private final static boolean[] sBoolOut = new boolean[1];
+
// ---- delegate data ----
private Bitmap_Delegate mBitmap;
private GcSnapshot mSnapshot;
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 sManager.getDelegate(canvas.mNativeCanvas);
+ return sManager.getDelegate(canvas.getNativeCanvasWrapper());
}
/**
@@ -100,134 +104,82 @@ public final class Canvas_Delegate {
// ---- native methods ----
@LayoutlibDelegate
- /*package*/ static boolean isOpaque(Canvas thisCanvas) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
- if (canvasDelegate == null) {
- return false;
- }
-
- return canvasDelegate.mBitmap.getConfig() == Config.RGB_565;
+ /*package*/ static void freeCaches() {
+ // nothing to be done here.
}
@LayoutlibDelegate
- /*package*/ static int getWidth(Canvas thisCanvas) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
- if (canvasDelegate == null) {
- return 0;
- }
-
- return canvasDelegate.mBitmap.getImage().getWidth();
+ /*package*/ static void freeTextLayoutCaches() {
+ // nothing to be done here yet.
}
@LayoutlibDelegate
- /*package*/ static int getHeight(Canvas thisCanvas) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
- if (canvasDelegate == null) {
- return 0;
- }
+ /*package*/ static long initRaster(long nativeBitmapOrZero) {
+ if (nativeBitmapOrZero > 0) {
+ // get the Bitmap from the int
+ Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmapOrZero);
- return canvasDelegate.mBitmap.getImage().getHeight();
- }
+ // create a new Canvas_Delegate with the given bitmap and return its new native int.
+ Canvas_Delegate newDelegate = new Canvas_Delegate(bitmapDelegate);
- @LayoutlibDelegate
- /*package*/ static void translate(Canvas thisCanvas, float dx, float dy) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
- if (canvasDelegate == null) {
- return;
+ return sManager.addNewDelegate(newDelegate);
}
- canvasDelegate.getSnapshot().translate(dx, dy);
+ // create a new Canvas_Delegate and return its new native int.
+ Canvas_Delegate newDelegate = new Canvas_Delegate();
+
+ return sManager.addNewDelegate(newDelegate);
}
@LayoutlibDelegate
- /*package*/ static void rotate(Canvas thisCanvas, float degrees) {
- // get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
- if (canvasDelegate == null) {
+ /*package*/
+ static void native_setBitmap(long canvas, long bitmap, boolean copyState) {
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(canvas);
+ Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
+ if (canvasDelegate == null || bitmapDelegate==null) {
return;
}
-
- canvasDelegate.getSnapshot().rotate(Math.toRadians(degrees));
+ canvasDelegate.mBitmap = bitmapDelegate;
+ canvasDelegate.mSnapshot = GcSnapshot.createDefaultSnapshot(bitmapDelegate);
}
@LayoutlibDelegate
- /*package*/ static void scale(Canvas thisCanvas, float sx, float sy) {
+ /*package*/ static boolean native_isOpaque(long nativeCanvas) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
- return;
+ return false;
}
- canvasDelegate.getSnapshot().scale(sx, sy);
+ return canvasDelegate.mBitmap.getConfig() == Config.RGB_565;
}
@LayoutlibDelegate
- /*package*/ static void skew(Canvas thisCanvas, float kx, float ky) {
+ /*package*/ static int native_getWidth(long nativeCanvas) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
- return;
+ return 0;
}
- // 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
- /*package*/ static boolean clipRect(Canvas thisCanvas, RectF rect) {
- return clipRect(thisCanvas, rect.left, rect.top, rect.right, rect.bottom);
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean clipRect(Canvas thisCanvas, Rect rect) {
- return clipRect(thisCanvas, (float) rect.left, (float) rect.top,
- (float) rect.right, (float) rect.bottom);
+ return canvasDelegate.mBitmap.getImage().getWidth();
}
@LayoutlibDelegate
- /*package*/ static boolean clipRect(Canvas thisCanvas, float left, float top, float right,
- float bottom) {
+ /*package*/ static int native_getHeight(long nativeCanvas) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
- return false;
+ return 0;
}
- return canvasDelegate.clipRect(left, top, right, bottom, Region.Op.INTERSECT.nativeInt);
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean clipRect(Canvas thisCanvas, int left, int top, int right,
- int bottom) {
-
- return clipRect(thisCanvas, (float) left, (float) top, (float) right, (float) bottom);
- }
-
- @LayoutlibDelegate
- /*package*/ static int save(Canvas thisCanvas) {
- return save(thisCanvas, Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG);
+ return canvasDelegate.mBitmap.getImage().getHeight();
}
@LayoutlibDelegate
- /*package*/ static int save(Canvas thisCanvas, int saveFlags) {
+ /*package*/ static int native_save(long nativeCanvas, int saveFlags) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
return 0;
}
@@ -236,175 +188,126 @@ public final class Canvas_Delegate {
}
@LayoutlibDelegate
- /*package*/ static void restore(Canvas thisCanvas) {
+ /*package*/ static int native_saveLayer(long nativeCanvas, float l,
+ float t, float r, float b,
+ long paint, int layerFlags) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
- return;
+ return 0;
}
- canvasDelegate.restore();
+ Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
+ if (paintDelegate == null) {
+ return 0;
+ }
+
+ return canvasDelegate.saveLayer(new RectF(l, t, r, b),
+ paintDelegate, layerFlags);
}
@LayoutlibDelegate
- /*package*/ static int getSaveCount(Canvas thisCanvas) {
+ /*package*/ static int native_saveLayerAlpha(long nativeCanvas, float l,
+ float t, float r, float b,
+ int alpha, int layerFlags) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
return 0;
}
- return canvasDelegate.getSnapshot().size();
+ return canvasDelegate.saveLayerAlpha(new RectF(l, t, r, b), alpha, layerFlags);
}
@LayoutlibDelegate
- /*package*/ static void restoreToCount(Canvas thisCanvas, int saveCount) {
+ /*package*/ static void native_restore(long nativeCanvas) {
// get the delegate from the native int.
- Canvas_Delegate canvasDelegate = sManager.getDelegate(thisCanvas.mNativeCanvas);
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
return;
}
- canvasDelegate.restoreTo(saveCount);
- }
-
- @LayoutlibDelegate
- /*package*/ static void drawPoints(Canvas thisCanvas, float[] pts, int offset, int count,
- Paint paint) {
- // FIXME
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Canvas.drawPoint is not supported.", null, null /*data*/);
- }
-
- @LayoutlibDelegate
- /*package*/ static void drawPoint(Canvas thisCanvas, float x, float y, Paint paint) {
- // FIXME
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Canvas.drawPoint is not supported.", null, null /*data*/);
- }
-
- @LayoutlibDelegate
- /*package*/ static void drawLines(Canvas thisCanvas,
- final float[] pts, final int offset, final int count,
- Paint paint) {
- draw(thisCanvas.mNativeCanvas, paint.mNativePaint, false /*compositeOnly*/,
- false /*forceSrcMode*/, new GcSnapshot.Drawable() {
- @Override
- public void draw(Graphics2D graphics, Paint_Delegate 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 freeCaches() {
- // nothing to be done here.
- }
-
- @LayoutlibDelegate
- /*package*/ static void freeTextLayoutCaches() {
- // nothing to be done here yet.
- }
-
- @LayoutlibDelegate
- /*package*/ static long initRaster(long nativeBitmapOrZero) {
- if (nativeBitmapOrZero > 0) {
- // get the Bitmap from the int
- Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(nativeBitmapOrZero);
-
- // 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);
+ canvasDelegate.restore();
}
@LayoutlibDelegate
- /*package*/ static void copyNativeCanvasState(long srcCanvas, long dstCanvas) {
+ /*package*/ static void native_restoreToCount(long nativeCanvas, int saveCount) {
// get the delegate from the native int.
- Canvas_Delegate srcCanvasDelegate = sManager.getDelegate(srcCanvas);
- if (srcCanvasDelegate == null) {
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ if (canvasDelegate == null) {
return;
}
- // get the delegate from the native int.
- Canvas_Delegate dstCanvasDelegate = sManager.getDelegate(dstCanvas);
- if (dstCanvasDelegate == null) {
- return;
- }
- // TODO: actually copy the canvas state.
+ canvasDelegate.restoreTo(saveCount);
}
@LayoutlibDelegate
- /*package*/ static long native_saveLayer(long nativeCanvas, RectF bounds,
- long paint, int layerFlags) {
+ /*package*/ static int native_getSaveCount(long nativeCanvas) {
// get the delegate from the native int.
Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
return 0;
}
- Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
- if (paintDelegate == null) {
- return 0;
- }
-
- return canvasDelegate.saveLayer(bounds, paintDelegate, layerFlags);
+ return canvasDelegate.getSnapshot().size();
}
@LayoutlibDelegate
- /*package*/ static long native_saveLayer(long nativeCanvas, float l,
- float t, float r, float b,
- long paint, int layerFlags) {
+ /*package*/ static void native_translate(long nativeCanvas, float dx, float dy) {
// get the delegate from the native int.
Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
- return 0;
- }
-
- Paint_Delegate paintDelegate = Paint_Delegate.getDelegate(paint);
- if (paintDelegate == null) {
- return 0;
+ return;
}
- return canvasDelegate.saveLayer(new RectF(l, t, r, b),
- paintDelegate, layerFlags);
+ canvasDelegate.getSnapshot().translate(dx, dy);
}
@LayoutlibDelegate
- /*package*/ static long native_saveLayerAlpha(long nativeCanvas,
- RectF bounds, int alpha,
- int layerFlags) {
+ /*package*/ static void native_scale(long nativeCanvas, float sx, float sy) {
+ // get the delegate from the native int.
+ Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
+ if (canvasDelegate == null) {
+ return;
+ }
+
+ canvasDelegate.getSnapshot().scale(sx, sy);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void native_rotate(long nativeCanvas, float degrees) {
// get the delegate from the native int.
Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
- return 0;
+ return;
}
- return canvasDelegate.saveLayerAlpha(bounds, alpha, layerFlags);
+ canvasDelegate.getSnapshot().rotate(Math.toRadians(degrees));
}
@LayoutlibDelegate
- /*package*/ static long native_saveLayerAlpha(long nativeCanvas, float l,
- float t, float r, float b,
- int alpha, int layerFlags) {
+ /*package*/ static void native_skew(long nativeCanvas, float kx, float ky) {
// get the delegate from the native int.
Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
if (canvasDelegate == null) {
- return 0;
+ return;
}
- return canvasDelegate.saveLayerAlpha(new RectF(l, t, r, b), alpha, layerFlags);
- }
+ // 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
/*package*/ static void native_concat(long nCanvas, long nMatrix) {
@@ -469,7 +372,6 @@ public final class Canvas_Delegate {
float left, float top,
float right, float bottom,
int regionOp) {
-
// get the delegate from the native int.
Canvas_Delegate canvasDelegate = sManager.getDelegate(nCanvas);
if (canvasDelegate == null) {
@@ -568,15 +470,7 @@ public final class Canvas_Delegate {
}
@LayoutlibDelegate
- /*package*/ static boolean native_quickReject(long nativeCanvas,
- RectF rect) {
- // FIXME properly implement quickReject
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean native_quickReject(long nativeCanvas,
- long path) {
+ /*package*/ static boolean native_quickReject(long nativeCanvas, long path) {
// FIXME properly implement quickReject
return false;
}
@@ -590,24 +484,6 @@ public final class Canvas_Delegate {
}
@LayoutlibDelegate
- /*package*/ static void native_drawRGB(long nativeCanvas, int r, int g, int b) {
- native_drawColor(nativeCanvas, 0xFF000000 | r << 16 | (g&0xFF) << 8 | (b&0xFF),
- PorterDuff.Mode.SRC_OVER.nativeInt);
-
- }
-
- @LayoutlibDelegate
- /*package*/ static void native_drawARGB(long nativeCanvas, int a, int r, int g, int b) {
- native_drawColor(nativeCanvas, a << 24 | (r&0xFF) << 16 | (g&0xFF) << 8 | (b&0xFF),
- PorterDuff.Mode.SRC_OVER.nativeInt);
- }
-
- @LayoutlibDelegate
- /*package*/ static void native_drawColor(long nativeCanvas, int color) {
- native_drawColor(nativeCanvas, color, PorterDuff.Mode.SRC_OVER.nativeInt);
- }
-
- @LayoutlibDelegate
/*package*/ static void native_drawColor(long nativeCanvas, final int color, final int mode) {
// get the delegate from the native int.
Canvas_Delegate canvasDelegate = sManager.getDelegate(nativeCanvas);
@@ -627,8 +503,8 @@ public final class Canvas_Delegate {
// set the color
graphics.setColor(new Color(color, true /*alpha*/));
- Composite composite = PorterDuffXfermode_Delegate.getComposite(
- PorterDuffXfermode_Delegate.getPorterDuffMode(mode), 0xFF);
+ Composite composite = PorterDuffUtility.getComposite(
+ PorterDuffUtility.getPorterDuffMode(mode), 0xFF);
if (composite != null) {
graphics.setComposite(composite);
}
@@ -646,10 +522,25 @@ public final class Canvas_Delegate {
}
@LayoutlibDelegate
+ /*package*/ static void native_drawPoint(long nativeCanvas, float x, float y,
+ long nativePaint) {
+ // FIXME
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "Canvas.drawPoint is not supported.", null, null /*data*/);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void native_drawPoints(long nativeCanvas, float[] pts, int offset, int count,
+ long nativePaint) {
+ // FIXME
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "Canvas.drawPoint is not supported.", null, null /*data*/);
+ }
+
+ @LayoutlibDelegate
/*package*/ static void native_drawLine(long nativeCanvas,
final float startX, final float startY, final float stopX, final float stopY,
long paint) {
-
draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
new GcSnapshot.Drawable() {
@Override
@@ -660,8 +551,19 @@ public final class Canvas_Delegate {
}
@LayoutlibDelegate
- /*package*/ static void native_drawRect(long nativeCanvas, RectF rect, long paint) {
- native_drawRect(nativeCanvas, rect.left, rect.top, rect.right, rect.bottom, paint);
+ /*package*/ static void native_drawLines(long nativeCanvas,
+ final float[] pts, final int offset, final int count,
+ long nativePaint) {
+ draw(nativeCanvas, nativePaint, false /*compositeOnly*/,
+ false /*forceSrcMode*/, new GcSnapshot.Drawable() {
+ @Override
+ public void draw(Graphics2D graphics, Paint_Delegate 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
@@ -691,8 +593,9 @@ public final class Canvas_Delegate {
}
@LayoutlibDelegate
- /*package*/ static void native_drawOval(long nativeCanvas, final RectF oval, long paint) {
- if (oval.right > oval.left && oval.bottom > oval.top) {
+ /*package*/ static void native_drawOval(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*/,
new GcSnapshot.Drawable() {
@Override
@@ -702,14 +605,14 @@ public final class Canvas_Delegate {
// draw
if (style == Paint.Style.FILL.nativeInt ||
style == Paint.Style.FILL_AND_STROKE.nativeInt) {
- graphics.fillOval((int)oval.left, (int)oval.top,
- (int)oval.width(), (int)oval.height());
+ 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)oval.left, (int)oval.top,
- (int)oval.width(), (int)oval.height());
+ graphics.drawOval((int)left, (int)top,
+ (int)(right - left), (int)(bottom - top));
}
}
});
@@ -720,15 +623,16 @@ public final class Canvas_Delegate {
/*package*/ static void native_drawCircle(long nativeCanvas,
float cx, float cy, float radius, long paint) {
native_drawOval(nativeCanvas,
- new RectF(cx - radius, cy - radius, cx + radius, cy + radius),
+ cx - radius, cy - radius, cx + radius, cy + radius,
paint);
}
@LayoutlibDelegate
/*package*/ static void native_drawArc(long nativeCanvas,
- final RectF oval, final float startAngle, final float sweep,
+ final float left, final float top, final float right, final float bottom,
+ final float startAngle, final float sweep,
final boolean useCenter, long paint) {
- if (oval.right > oval.left && oval.bottom > oval.top) {
+ if (right > left && bottom > top) {
draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
new GcSnapshot.Drawable() {
@Override
@@ -736,7 +640,7 @@ public final class Canvas_Delegate {
int style = paintDelegate.getStyle();
Arc2D.Float arc = new Arc2D.Float(
- oval.left, oval.top, oval.width(), oval.height(),
+ left, top, right - left, bottom - top,
-startAngle, -sweep,
useCenter ? Arc2D.PIE : Arc2D.OPEN);
@@ -757,8 +661,8 @@ public final class Canvas_Delegate {
@LayoutlibDelegate
/*package*/ static void native_drawRoundRect(long nativeCanvas,
- final RectF rect, final float rx, final float ry, long paint) {
-
+ 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*/,
new GcSnapshot.Drawable() {
@Override
@@ -769,16 +673,16 @@ public final class Canvas_Delegate {
if (style == Paint.Style.FILL.nativeInt ||
style == Paint.Style.FILL_AND_STROKE.nativeInt) {
graphics.fillRoundRect(
- (int)rect.left, (int)rect.top,
- (int)rect.width(), (int)rect.height(),
+ (int)left, (int)top,
+ (int)(right - left), (int)(bottom - top),
(int)rx, (int)ry);
}
if (style == Paint.Style.STROKE.nativeInt ||
style == Paint.Style.FILL_AND_STROKE.nativeInt) {
graphics.drawRoundRect(
- (int)rect.left, (int)rect.top,
- (int)rect.width(), (int)rect.height(),
+ (int)left, (int)top,
+ (int)(right - left), (int)(bottom - top),
(int)rx, (int)ry);
}
}
@@ -836,52 +740,18 @@ public final class Canvas_Delegate {
@LayoutlibDelegate
/*package*/ static void native_drawBitmap(Canvas thisCanvas, long nativeCanvas, long bitmap,
- Rect src, RectF dst,
- long nativePaintOrZero,
- int screenDensity,
- int bitmapDensity) {
+ 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(bitmap);
if (bitmapDelegate == null) {
return;
}
- BufferedImage image = bitmapDelegate.getImage();
-
- if (src == null) {
- drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
- 0, 0, image.getWidth(), image.getHeight(),
- (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
- } else {
- drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
- src.left, src.top, src.width(), src.height(),
- (int)dst.left, (int)dst.top, (int)dst.right, (int)dst.bottom);
- }
- }
-
- @LayoutlibDelegate
- /*package*/ static void native_drawBitmap(long nativeCanvas, long bitmap,
- Rect src, Rect dst,
- long nativePaintOrZero,
- int screenDensity,
- int bitmapDensity) {
- // get the delegate from the native int.
- Bitmap_Delegate bitmapDelegate = Bitmap_Delegate.getDelegate(bitmap);
- if (bitmapDelegate == null) {
- return;
- }
-
- BufferedImage image = bitmapDelegate.getImage();
-
- if (src == null) {
- drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
- 0, 0, image.getWidth(), image.getHeight(),
- dst.left, dst.top, dst.right, dst.bottom);
- } else {
- drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
- src.left, src.top, src.width(), src.height(),
- dst.left, dst.top, dst.right, dst.bottom);
- }
+ drawBitmap(nativeCanvas, bitmapDelegate, nativePaintOrZero,
+ (int)srcLeft, (int)srcTop, (int)srcRight, (int)srcBottom,
+ (int)dstLeft, (int)dstTop, (int)dstRight, (int)dstBottom);
}
@LayoutlibDelegate
@@ -890,7 +760,6 @@ public final class Canvas_Delegate {
final float y, int width, int height,
boolean hasAlpha,
long nativePaintOrZero) {
-
// create a temp BufferedImage containing the content.
final BufferedImage image = new BufferedImage(width, height,
hasAlpha ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
@@ -973,82 +842,39 @@ public final class Canvas_Delegate {
}
@LayoutlibDelegate
- /*package*/ static void native_drawText(long nativeCanvas,
- final char[] text, final int index, final int count,
- final float startX, final float startY, final int flags, long paint) {
-
- draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
- new GcSnapshot.Drawable() {
- @Override
- public void draw(Graphics2D graphics, Paint_Delegate 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;
- boolean isRtl = flags == Canvas.DIRECTION_RTL;
- if (paintDelegate.getTextAlign() != Paint.Align.LEFT.nativeInt) {
- RectF bounds = paintDelegate.measureText(text, index, count, isRtl);
- 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).renderText(
- index, limit, isRtl, null, 0, true, x, startY);
- }
- });
+ /*package*/ static void native_drawText(long nativeCanvas, char[] text, int index, int count,
+ float startX, float startY, int flags, long paint, long typeface) {
+ drawText(nativeCanvas, text, index, count, startX, startY, flags == Canvas.DIRECTION_RTL,
+ paint, typeface);
}
@LayoutlibDelegate
/*package*/ static void native_drawText(long nativeCanvas, String text,
- int start, int end, float x, float y, final int flags, long paint) {
+ int start, int end, float x, float y, final int flags, long paint,
+ long typeface) {
int count = end - start;
char[] buffer = TemporaryBuffer.obtain(count);
TextUtils.getChars(text, start, end, buffer, 0);
- native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint);
+ native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint, typeface);
}
@LayoutlibDelegate
/*package*/ static void native_drawTextRun(long nativeCanvas, String text,
int start, int end, int contextStart, int contextEnd,
- float x, float y, int flags, long paint) {
+ float x, float y, boolean isRtl, long paint, long typeface) {
int count = end - start;
char[] buffer = TemporaryBuffer.obtain(count);
TextUtils.getChars(text, start, end, buffer, 0);
- native_drawText(nativeCanvas, buffer, 0, count, x, y, flags, paint);
+ drawText(nativeCanvas, buffer, 0, count, x, y, isRtl, paint, typeface);
}
@LayoutlibDelegate
/*package*/ static void native_drawTextRun(long nativeCanvas, char[] text,
int start, int count, int contextStart, int contextCount,
- float x, float y, int flags, long paint) {
- native_drawText(nativeCanvas, text, start, count, x, y, flags, paint);
- }
-
- @LayoutlibDelegate
- /*package*/ static void native_drawPosText(long nativeCanvas,
- char[] text, int index,
- int count, float[] pos,
- long paint) {
- // FIXME
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Canvas.drawPosText is not supported.", null, null /*data*/);
- }
-
- @LayoutlibDelegate
- /*package*/ static void native_drawPosText(long nativeCanvas,
- String text, float[] pos,
- long paint) {
- // FIXME
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Canvas.drawPosText is not supported.", null, null /*data*/);
+ float x, float y, boolean isRtl, long paint, long typeface) {
+ drawText(nativeCanvas, text, start, count, x, y, isRtl, paint, typeface);
}
@LayoutlibDelegate
@@ -1057,7 +883,7 @@ public final class Canvas_Delegate {
int count, long path,
float hOffset,
float vOffset, int bidiFlags,
- long paint) {
+ long paint, long typeface) {
// FIXME
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
"Canvas.drawTextOnPath is not supported.", null, null /*data*/);
@@ -1068,7 +894,8 @@ public final class Canvas_Delegate {
String text, long path,
float hOffset,
float vOffset,
- int flags, long paint) {
+ int bidiFlags, long paint,
+ long typeface) {
// FIXME
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
"Canvas.drawTextOnPath is not supported.", null, null /*data*/);
@@ -1088,12 +915,13 @@ public final class Canvas_Delegate {
sManager.removeJavaReferenceFor(nativeCanvas);
}
+
// ---- 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, int, int)}.
+ * layers involved (see {@link #saveLayer(RectF, Paint_Delegate, int)}.
*/
private static void draw(long nCanvas, long nPaint, boolean compositeOnly, boolean forceSrcMode,
GcSnapshot.Drawable drawable) {
@@ -1113,7 +941,7 @@ public final class Canvas_Delegate {
* 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, int, int)}.
+ * 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.
@@ -1125,6 +953,41 @@ public final class Canvas_Delegate {
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 boolean isRtl,
+ long paint, final long typeface) {
+
+ draw(nativeCanvas, paint, false /*compositeOnly*/, false /*forceSrcMode*/,
+ new GcSnapshot.Drawable() {
+ @Override
+ public void draw(Graphics2D graphics, Paint_Delegate paintDelegate) {
+ // WARNING: the logic in this method is similar to Paint_Delegate.measureText.
+ // Any change to this method should be reflected in Paint.measureText
+
+ // assert that the typeface passed is actually the one stored in paint.
+ assert (typeface == paintDelegate.mNativeTypeface);
+
+ // 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,
+ isRtl);
+ 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, isRtl, null, 0, true);
+ }
+ });
+ }
+
private Canvas_Delegate(Bitmap_Delegate bitmap) {
mSnapshot = GcSnapshot.createDefaultSnapshot(mBitmap = bitmap);
}
@@ -1186,12 +1049,6 @@ public final class Canvas_Delegate {
return mSnapshot.clipRect(left, top, right, bottom, regionOp);
}
- private void setBitmap(Bitmap_Delegate bitmap) {
- mBitmap = bitmap;
- assert mSnapshot.size() == 1;
- mSnapshot.setBitmap(mBitmap);
- }
-
private static void drawBitmap(
long nativeCanvas,
Bitmap_Delegate bitmap,
diff --git a/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java
index d6b3da1..bd934d0 100644
--- a/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/ColorFilter_Delegate.java
@@ -19,6 +19,8 @@ package android.graphics;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import java.awt.Graphics2D;
+
/**
* Delegate implementing the native methods of android.graphics.ColorFilter
*
@@ -50,13 +52,21 @@ public abstract class ColorFilter_Delegate {
return sManager.getDelegate(nativeShader);
}
- public abstract boolean isSupported();
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 void finalizer(long native_instance, long nativeColorFilter) {
+ /*package*/ static void destroyFilter(long native_instance) {
sManager.removeJavaReferenceFor(native_instance);
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java
index ca8f450..6739484 100644
--- a/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/ColorMatrixColorFilter_Delegate.java
@@ -43,11 +43,6 @@ public class ColorMatrixColorFilter_Delegate extends ColorFilter_Delegate {
// ---- Public Helper methods ----
@Override
- public boolean isSupported() {
- return false;
- }
-
- @Override
public String getSupportMessage() {
return "ColorMatrix Color Filters are not supported.";
}
@@ -60,11 +55,5 @@ public class ColorMatrixColorFilter_Delegate extends ColorFilter_Delegate {
return sManager.addNewDelegate(newDelegate);
}
- @LayoutlibDelegate
- /*package*/ static long nColorMatrixFilter(long nativeFilter, float[] array) {
- // pass
- return 0;
- }
-
// ---- Private delegate/helper methods ----
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java
index fae8aef..59ddcc6 100644
--- a/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/ComposeShader_Delegate.java
@@ -78,19 +78,6 @@ public class ComposeShader_Delegate extends Shader_Delegate {
return sManager.addNewDelegate(newDelegate);
}
- @LayoutlibDelegate
- /*package*/ static long nativePostCreate1(long native_shader, long native_skiaShaderA,
- long native_skiaShaderB, long native_mode) {
- // pass, not needed.
- return 0;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nativePostCreate2(long native_shader, long native_skiaShaderA,
- long native_skiaShaderB, int porterDuffMode) {
- // pass, not needed.
- return 0;
- }
// ---- Private delegate/helper methods ----
diff --git a/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
new file mode 100644
index 0000000..bef5181
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/FontFamily_Delegate.java
@@ -0,0 +1,375 @@
+/*
+ * 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.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.content.res.AssetManager;
+
+import java.awt.Font;
+import java.awt.FontFormatException;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Scanner;
+import java.util.Set;
+
+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;
+
+ // FONT_SUFFIX_ITALIC will always match FONT_SUFFIX_BOLDITALIC and hence it must be checked
+ // separately.
+ private static final String FONT_SUFFIX_ITALIC = "Italic.ttf";
+ private static final String FN_ALL_FONTS_LIST = "fontsInSdk.txt";
+
+ /**
+ * A class associating {@link Font} with its metadata.
+ */
+ private static final class FontInfo {
+ @Nullable
+ Font mFont;
+ int mWeight;
+ boolean mIsItalic;
+ }
+
+ // ---- delegate manager ----
+ private static final DelegateManager<FontFamily_Delegate> sManager =
+ new DelegateManager<FontFamily_Delegate>(FontFamily_Delegate.class);
+
+ // ---- 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 ----
+ private List<FontInfo> mFonts = new ArrayList<FontInfo>();
+
+ /**
+ * 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<String>(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(LayoutLog.TAG_BROKEN,
+ "Unable to load the list of fonts. Try re-installing the SDK Platform from the SDK Manager.",
+ e, 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;
+ FontInfo bestFont = null;
+ int bestMatch = Integer.MAX_VALUE;
+ for (FontInfo font : mFonts) {
+ int match = computeMatch(font, desiredStyle);
+ if (match < bestMatch) {
+ bestMatch = match;
+ bestFont = font;
+ }
+ }
+ if (bestFont == null) {
+ return null;
+ }
+ if (bestMatch == 0) {
+ return bestFont.mFont;
+ }
+ // 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;
+ }
+
+ /*package*/ 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(".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(LayoutLog.TAG_BROKEN,
+ String.format("Unable to load font %1$s", relativePath),
+ e, null);
+ }
+ } else {
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "Only platform fonts located in " + SYSTEM_FONTS + "can be loaded.",
+ null, null);
+ }
+
+ return null;
+ }
+
+
+ // ---- native methods ----
+
+ @LayoutlibDelegate
+ /*package*/ static long nCreateFamily(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 void nUnrefFamily(long nativePtr) {
+ // Removing the java reference for the object doesn't mean that it's freed for garbage
+ // collection. Typeface_Delegate may still hold a reference for it.
+ sManager.removeJavaReferenceFor(nativePtr);
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static boolean nAddFont(long nativeFamily, final String path) {
+ final FontFamily_Delegate delegate = getDelegate(nativeFamily);
+ if (delegate != null) {
+ if (sFontLocation == null) {
+ delegate.mPostInitRunnables.add(new Runnable() {
+ @Override
+ public void run() {
+ delegate.addFont(path);
+ }
+ });
+ return true;
+ }
+ return delegate.addFont(path);
+ }
+ return false;
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static boolean nAddFontWeightStyle(long nativeFamily, final String path,
+ final int weight, final boolean isItalic) {
+ final FontFamily_Delegate delegate = getDelegate(nativeFamily);
+ if (delegate != null) {
+ if (sFontLocation == null) {
+ delegate.mPostInitRunnables.add(new Runnable() {
+ @Override
+ public void run() {
+ delegate.addFont(path, weight, isItalic);
+ }
+ });
+ return true;
+ }
+ return delegate.addFont(path, weight, isItalic);
+ }
+ return false;
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static boolean nAddFontFromAsset(long nativeFamily, AssetManager mgr, String path) {
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "FontFamily.addFontFromAsset is not supported.", null, null);
+ return false;
+ }
+
+
+ // ---- private helper methods ----
+
+ private void init() {
+ for (Runnable postInitRunnable : mPostInitRunnables) {
+ postInitRunnable.run();
+ }
+ mPostInitRunnables = null;
+ }
+
+ private boolean addFont(@NonNull String path) {
+ return addFont(path, DEFAULT_FONT_WEIGHT, path.endsWith(FONT_SUFFIX_ITALIC));
+ }
+
+ private boolean addFont(@NonNull String path, int weight, boolean isItalic) {
+ if (path.startsWith(SYSTEM_FONTS) &&
+ !SDK_FONTS.contains(path.substring(SYSTEM_FONTS.length()))) {
+ 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 = isItalic;
+ addFont(fontInfo);
+ return true;
+ }
+
+ private boolean addFont(@NonNull FontInfo fontInfo) {
+ int weight = fontInfo.mWeight;
+ boolean isItalic = fontInfo.mIsItalic;
+ // The list is usually just two fonts big. So iterating over all isn't as bad as it looks.
+ // It's biggest for roboto where the size is 12.
+ for (FontInfo font : mFonts) {
+ if (font.mWeight == weight && font.mIsItalic == isItalic) {
+ return false;
+ }
+ }
+ mFonts.add(fontInfo);
+ return true;
+ }
+
+ /**
+ * Compute matching metric between two styles - 0 is an exact match.
+ */
+ private static int computeMatch(@NonNull FontInfo font1, @NonNull FontInfo font2) {
+ int score = Math.abs(font1.mWeight - font2.mWeight);
+ if (font1.mIsItalic != font2.mIsItalic) {
+ score += 200;
+ }
+ 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
+ * @return outFont
+ */
+ @NonNull
+ private FontInfo deriveFont(@NonNull FontInfo srcFont, @NonNull FontInfo outFont) {
+ int desiredWeight = outFont.mWeight;
+ int srcWeight = srcFont.mWeight;
+ Font derivedFont = srcFont.mFont;
+ // Embolden the font if required.
+ if (desiredWeight >= BOLD_FONT_WEIGHT && desiredWeight - srcWeight > BOLD_FONT_WEIGHT_DELTA / 2) {
+ derivedFont = derivedFont.deriveFont(Font.BOLD);
+ srcWeight += BOLD_FONT_WEIGHT_DELTA;
+ }
+ // Italicize the font if required.
+ if (outFont.mIsItalic && !srcFont.mIsItalic) {
+ derivedFont = derivedFont.deriveFont(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;
+ }
+ outFont.mFont = derivedFont;
+ outFont.mWeight = srcWeight;
+ // No need to update mIsItalics, as it's already been handled above.
+ return outFont;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java
index defaac3..0dd9703 100644
--- a/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/LightingColorFilter_Delegate.java
@@ -43,11 +43,6 @@ public class LightingColorFilter_Delegate extends ColorFilter_Delegate {
// ---- Public Helper methods ----
@Override
- public boolean isSupported() {
- return false;
- }
-
- @Override
public String getSupportMessage() {
return "Lighting Color Filters are not supported.";
}
@@ -60,11 +55,5 @@ public class LightingColorFilter_Delegate extends ColorFilter_Delegate {
return sManager.addNewDelegate(newDelegate);
}
- @LayoutlibDelegate
- /*package*/ static int nCreateLightingFilter(long nativeFilter, int mul, int add) {
- // pass
- return 0;
- }
-
// ---- Private delegate/helper methods ----
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
index ac77377..55c4b98 100644
--- a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java
@@ -71,22 +71,6 @@ public final class LinearGradient_Delegate extends Gradient_Delegate {
tileMode);
}
- @LayoutlibDelegate
- /*package*/ static long nativePostCreate1(LinearGradient thisGradient,
- long native_shader, float x0, float y0, float x1, float y1,
- int colors[], float positions[], int tileMode) {
- // nothing to be done here.
- return 0;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nativePostCreate2(LinearGradient thisGradient,
- long native_shader, float x0, float y0, float x1, float y1,
- int color0, int color1, int tileMode) {
- // nothing to be done here.
- return 0;
- }
-
// ---- Private delegate/helper methods ----
/**
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
index e9b5211..1105c7b 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java
@@ -203,6 +203,16 @@ public final class Matrix_Delegate {
}
@LayoutlibDelegate
+ /*package*/ static boolean native_isAffine(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 native_rectStaysRect(long native_object) {
Matrix_Delegate d = sManager.getDelegate(native_object);
if (d == null) {
@@ -355,227 +365,162 @@ public final class Matrix_Delegate {
}
@LayoutlibDelegate
- /*package*/ static boolean native_setConcat(long native_object, long a, long b) {
+ /*package*/ static void native_setConcat(long native_object, long a, long b) {
if (a == native_object) {
- return native_preConcat(native_object, b);
+ native_preConcat(native_object, b);
+ return;
} else if (b == native_object) {
- return native_postConcat(native_object, a);
+ native_postConcat(native_object, a);
+ return;
}
Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return false;
- }
-
Matrix_Delegate a_mtx = sManager.getDelegate(a);
- if (a_mtx == null) {
- return false;
- }
-
Matrix_Delegate b_mtx = sManager.getDelegate(b);
- if (b_mtx == null) {
- return false;
+ if (d != null && a_mtx != null && b_mtx != null) {
+ multiply(d.mValues, a_mtx.mValues, b_mtx.mValues);
}
-
- multiply(d.mValues, a_mtx.mValues, b_mtx.mValues);
-
- return true;
}
@LayoutlibDelegate
- /*package*/ static boolean native_preTranslate(long native_object, float dx, float dy) {
+ /*package*/ static void native_preTranslate(long native_object, float dx, float dy) {
Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return false;
+ if (d != null) {
+ d.preTransform(getTranslate(dx, dy));
}
-
- d.preTransform(getTranslate(dx, dy));
- return true;
}
@LayoutlibDelegate
- /*package*/ static boolean native_preScale(long native_object, float sx, float sy,
+ /*package*/ static void native_preScale(long native_object, float sx, float sy,
float px, float py) {
Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return false;
+ if (d != null) {
+ d.preTransform(getScale(sx, sy, px, py));
}
-
- d.preTransform(getScale(sx, sy, px, py));
- return true;
}
@LayoutlibDelegate
- /*package*/ static boolean native_preScale(long native_object, float sx, float sy) {
+ /*package*/ static void native_preScale(long native_object, float sx, float sy) {
Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return false;
+ if (d != null) {
+ d.preTransform(getScale(sx, sy));
}
-
- d.preTransform(getScale(sx, sy));
- return true;
}
@LayoutlibDelegate
- /*package*/ static boolean native_preRotate(long native_object, float degrees,
+ /*package*/ static void native_preRotate(long native_object, float degrees,
float px, float py) {
Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return false;
+ if (d != null) {
+ d.preTransform(getRotate(degrees, px, py));
}
-
- d.preTransform(getRotate(degrees, px, py));
- return true;
}
@LayoutlibDelegate
- /*package*/ static boolean native_preRotate(long native_object, float degrees) {
+ /*package*/ static void native_preRotate(long native_object, float degrees) {
Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return false;
- }
+ if (d != null) {
- double rad = Math.toRadians(degrees);
- float sin = (float)Math.sin(rad);
- float cos = (float)Math.cos(rad);
+ double rad = Math.toRadians(degrees);
+ float sin = (float) Math.sin(rad);
+ float cos = (float) Math.cos(rad);
- d.preTransform(getRotate(sin, cos));
- return true;
+ d.preTransform(getRotate(sin, cos));
+ }
}
@LayoutlibDelegate
- /*package*/ static boolean native_preSkew(long native_object, float kx, float ky,
+ /*package*/ static void native_preSkew(long native_object, float kx, float ky,
float px, float py) {
Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return false;
+ if (d != null) {
+ d.preTransform(getSkew(kx, ky, px, py));
}
-
- d.preTransform(getSkew(kx, ky, px, py));
- return true;
}
@LayoutlibDelegate
- /*package*/ static boolean native_preSkew(long native_object, float kx, float ky) {
+ /*package*/ static void native_preSkew(long native_object, float kx, float ky) {
Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return false;
+ if (d != null) {
+ d.preTransform(getSkew(kx, ky));
}
-
- d.preTransform(getSkew(kx, ky));
- return true;
}
@LayoutlibDelegate
- /*package*/ static boolean native_preConcat(long native_object, long other_matrix) {
+ /*package*/ static void native_preConcat(long native_object, long other_matrix) {
Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return false;
- }
-
Matrix_Delegate other = sManager.getDelegate(other_matrix);
- if (other == null) {
- return false;
+ if (d != null && other != null) {
+ d.preTransform(other.mValues);
}
-
- d.preTransform(other.mValues);
- return true;
}
@LayoutlibDelegate
- /*package*/ static boolean native_postTranslate(long native_object, float dx, float dy) {
+ /*package*/ static void native_postTranslate(long native_object, float dx, float dy) {
Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return false;
+ if (d != null) {
+ d.postTransform(getTranslate(dx, dy));
}
-
- d.postTransform(getTranslate(dx, dy));
- return true;
}
@LayoutlibDelegate
- /*package*/ static boolean native_postScale(long native_object, float sx, float sy,
+ /*package*/ static void native_postScale(long native_object, float sx, float sy,
float px, float py) {
Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return false;
+ if (d != null) {
+ d.postTransform(getScale(sx, sy, px, py));
}
-
- d.postTransform(getScale(sx, sy, px, py));
- return true;
}
@LayoutlibDelegate
- /*package*/ static boolean native_postScale(long native_object, float sx, float sy) {
+ /*package*/ static void native_postScale(long native_object, float sx, float sy) {
Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return false;
+ if (d != null) {
+ d.postTransform(getScale(sx, sy));
}
-
- d.postTransform(getScale(sx, sy));
- return true;
}
@LayoutlibDelegate
- /*package*/ static boolean native_postRotate(long native_object, float degrees,
+ /*package*/ static void native_postRotate(long native_object, float degrees,
float px, float py) {
Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return false;
+ if (d != null) {
+ d.postTransform(getRotate(degrees, px, py));
}
-
- d.postTransform(getRotate(degrees, px, py));
- return true;
}
@LayoutlibDelegate
- /*package*/ static boolean native_postRotate(long native_object, float degrees) {
+ /*package*/ static void native_postRotate(long native_object, float degrees) {
Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return false;
+ if (d != null) {
+ d.postTransform(getRotate(degrees));
}
-
- d.postTransform(getRotate(degrees));
- return true;
}
@LayoutlibDelegate
- /*package*/ static boolean native_postSkew(long native_object, float kx, float ky,
+ /*package*/ static void native_postSkew(long native_object, float kx, float ky,
float px, float py) {
Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return false;
+ if (d != null) {
+ d.postTransform(getSkew(kx, ky, px, py));
}
-
- d.postTransform(getSkew(kx, ky, px, py));
- return true;
}
@LayoutlibDelegate
- /*package*/ static boolean native_postSkew(long native_object, float kx, float ky) {
+ /*package*/ static void native_postSkew(long native_object, float kx, float ky) {
Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return false;
+ if (d != null) {
+ d.postTransform(getSkew(kx, ky));
}
-
- d.postTransform(getSkew(kx, ky));
- return true;
}
@LayoutlibDelegate
- /*package*/ static boolean native_postConcat(long native_object, long other_matrix) {
+ /*package*/ static void native_postConcat(long native_object, long other_matrix) {
Matrix_Delegate d = sManager.getDelegate(native_object);
- if (d == null) {
- return false;
- }
-
Matrix_Delegate other = sManager.getDelegate(other_matrix);
- if (other == null) {
- return false;
+ if (d != null && other != null) {
+ d.postTransform(other.mValues);
}
-
- d.postTransform(other.mValues);
- return true;
}
@LayoutlibDelegate
diff --git a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
index 74b2893..e16dbda 100644
--- a/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/NinePatch_Delegate.java
@@ -215,9 +215,9 @@ public final class NinePatch_Delegate {
if (c == null) {
// not a 9-patch?
BufferedImage image = bitmap_delegate.getImage();
- Canvas_Delegate.native_drawBitmap(canvas_instance, bitmap_instance,
- new Rect(0, 0, image.getWidth(), image.getHeight()),
- new Rect(left, top, right, bottom),
+ Canvas_Delegate.native_drawBitmap(null, canvas_instance, bitmap_instance,
+ 0f, 0f, (float)image.getWidth(), (float)image.getHeight(),
+ (float)left, (float)top, (float)right, (float)bottom,
paint_instance_or_null, destDensity, srcDensity);
return;
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
index 7007b71..7b07404 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java
@@ -21,6 +21,7 @@ import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import android.graphics.FontFamily_Delegate.FontVariant;
import android.graphics.Paint.FontMetrics;
import android.graphics.Paint.FontMetricsInt;
import android.text.TextUtils;
@@ -30,7 +31,6 @@ import java.awt.Font;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.Toolkit;
-import java.awt.font.FontRenderContext;
import java.awt.geom.AffineTransform;
import java.util.ArrayList;
import java.util.Collections;
@@ -53,7 +53,7 @@ import java.util.Locale;
public class Paint_Delegate {
/**
- * Class associating a {@link Font} and it's {@link java.awt.FontMetrics}.
+ * Class associating a {@link Font} and its {@link java.awt.FontMetrics}.
*/
/*package*/ static final class FontInfo {
Font mFont;
@@ -65,9 +65,9 @@ public class Paint_Delegate {
new DelegateManager<Paint_Delegate>(Paint_Delegate.class);
// ---- delegate helper data ----
+
+ // This list can contain null elements.
private List<FontInfo> mFonts;
- private final FontRenderContext mFontContext = new FontRenderContext(
- new AffineTransform(), true, true);
// ---- delegate data ----
private int mFlags;
@@ -83,6 +83,8 @@ public class Paint_Delegate {
private float mTextScaleX;
private float mTextSkewX;
private int mHintingMode = Paint.HINTING_ON;
+ // Variant of the font. A paint's variant can only be compact or elegant.
+ private FontVariant mFontVariant = FontVariant.COMPACT;
private Xfermode_Delegate mXfermode;
private ColorFilter_Delegate mColorFilter;
@@ -93,6 +95,8 @@ public class Paint_Delegate {
private Locale mLocale = Locale.getDefault();
+ // Used only to assert invariants.
+ public long mNativeTypeface;
// ---- Public Helper methods ----
@@ -101,8 +105,7 @@ public class Paint_Delegate {
}
/**
- * Returns the list of {@link Font} objects. The first item is the main font, the rest
- * are fall backs for characters not present in the main font.
+ * Returns the list of {@link Font} objects.
*/
public List<FontInfo> getFonts() {
return mFonts;
@@ -420,7 +423,7 @@ public class Paint_Delegate {
}
@LayoutlibDelegate
- /*package*/ static void nSetShadowLayer(Paint thisPaint, float radius, float dx, float dy,
+ /*package*/ static void native_setShadowLayer(long paint, float radius, float dx, float dy,
int color) {
// FIXME
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
@@ -428,6 +431,32 @@ public class Paint_Delegate {
}
@LayoutlibDelegate
+ /*package*/ static boolean native_hasShadowLayer(long paint) {
+ // FIXME
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "Paint.hasShadowLayer is not supported.", null, null /*data*/);
+ return false;
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static boolean isElegantTextHeight(Paint thisPaint) {
+ // get the delegate from the native int.
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ return delegate != null && delegate.mFontVariant == FontVariant.ELEGANT;
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void setElegantTextHeight(Paint thisPaint, boolean elegant) {
+ // get the delegate from the native int.
+ Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ if (delegate == null) {
+ return;
+ }
+
+ delegate.mFontVariant = elegant ? FontVariant.ELEGANT : FontVariant.COMPACT;
+ }
+
+ @LayoutlibDelegate
/*package*/ static float getTextSize(Paint thisPaint) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
@@ -575,7 +604,7 @@ public class Paint_Delegate {
return 0;
}
- RectF bounds = delegate.measureText(text, index, count, isRtl(bidiFlags));
+ RectF bounds = delegate.measureText(text, index, count, null, 0, bidiFlags);
return bounds.right - bounds.left;
}
@@ -591,11 +620,11 @@ public class Paint_Delegate {
}
@LayoutlibDelegate
- /*package*/ static int native_breakText(Paint thisPaint, char[] text, int index, int count,
- float maxWidth, int bidiFlags, float[] measuredWidth) {
+ /*package*/ static int native_breakText(long nativePaint, long nativeTypeface, char[] text,
+ int index, int count, float maxWidth, int bidiFlags, float[] measuredWidth) {
// get the delegate
- Paint_Delegate delegate = sManager.getDelegate(thisPaint.mNativePaint);
+ Paint_Delegate delegate = sManager.getDelegate(nativePaint);
if (delegate == null) {
return 0;
}
@@ -603,7 +632,6 @@ public class Paint_Delegate {
int inc = count > 0 ? 1 : -1;
int measureIndex = 0;
- float measureAcc = 0;
for (int i = index; i != index + count; i += inc, measureIndex++) {
int start, end;
if (i < index) {
@@ -615,14 +643,13 @@ public class Paint_Delegate {
}
// measure from start to end
- RectF bounds = delegate.measureText(text, start, end - start + 1, isRtl(bidiFlags));
+ RectF bounds = delegate.measureText(text, start, end - start + 1, null, 0, bidiFlags);
float res = bounds.right - bounds.left;
if (measuredWidth != null) {
measuredWidth[measureIndex] = res;
}
- measureAcc += 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;
@@ -635,10 +662,11 @@ public class Paint_Delegate {
}
@LayoutlibDelegate
- /*package*/ static int native_breakText(Paint thisPaint, String text, boolean measureForwards,
+ /*package*/ static int native_breakText(long nativePaint, long nativeTypeface, String text,
+ boolean measureForwards,
float maxWidth, int bidiFlags, float[] measuredWidth) {
- return native_breakText(thisPaint, text.toCharArray(), 0, text.length(), maxWidth,
- bidiFlags, measuredWidth);
+ return native_breakText(nativePaint, nativeTypeface, text.toCharArray(), 0, text.length(),
+ maxWidth, bidiFlags, measuredWidth);
}
@LayoutlibDelegate
@@ -688,7 +716,7 @@ public class Paint_Delegate {
}
@LayoutlibDelegate
- /*package*/ static long native_getStyle(long native_object) {
+ /*package*/ static int native_getStyle(long native_object) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(native_object);
if (delegate == null) {
@@ -710,7 +738,7 @@ public class Paint_Delegate {
}
@LayoutlibDelegate
- /*package*/ static long native_getStrokeCap(long native_object) {
+ /*package*/ static int native_getStrokeCap(long native_object) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(native_object);
if (delegate == null) {
@@ -732,7 +760,7 @@ public class Paint_Delegate {
}
@LayoutlibDelegate
- /*package*/ static long native_getStrokeJoin(long native_object) {
+ /*package*/ static int native_getStrokeJoin(long native_object) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(native_object);
if (delegate == null) {
@@ -800,10 +828,10 @@ public class Paint_Delegate {
return filter;
}
- delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);;
+ delegate.mColorFilter = ColorFilter_Delegate.getDelegate(filter);
- // since none of those are supported, display a fidelity warning right away
- if (delegate.mColorFilter != null && delegate.mColorFilter.isSupported() == false) {
+ // Log warning if it's not supported.
+ if (delegate.mColorFilter != null && !delegate.mColorFilter.isSupported()) {
Bridge.getLog().fidelityWarning(LayoutLog.TAG_COLORFILTER,
delegate.mColorFilter.getSupportMessage(), null, null /*data*/);
}
@@ -848,7 +876,7 @@ public class Paint_Delegate {
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() == false) {
+ if (delegate.mMaskFilter != null && !delegate.mMaskFilter.isSupported()) {
Bridge.getLog().fidelityWarning(LayoutLog.TAG_MASKFILTER,
delegate.mMaskFilter.getSupportMessage(), null, null /*data*/);
}
@@ -865,6 +893,7 @@ public class Paint_Delegate {
}
delegate.mTypeface = Typeface_Delegate.getDelegate(typeface);
+ delegate.mNativeTypeface = typeface;
delegate.updateFontObject();
return typeface;
}
@@ -880,7 +909,7 @@ public class Paint_Delegate {
delegate.mRasterizer = Rasterizer_Delegate.getDelegate(rasterizer);
// since none of those are supported, display a fidelity warning right away
- if (delegate.mRasterizer != null && delegate.mRasterizer.isSupported() == false) {
+ if (delegate.mRasterizer != null && !delegate.mRasterizer.isSupported()) {
Bridge.getLog().fidelityWarning(LayoutLog.TAG_RASTERIZER,
delegate.mRasterizer.getSupportMessage(), null, null /*data*/);
}
@@ -889,7 +918,7 @@ public class Paint_Delegate {
}
@LayoutlibDelegate
- /*package*/ static long native_getTextAlign(long native_object) {
+ /*package*/ static int native_getTextAlign(long native_object) {
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(native_object);
if (delegate == null) {
@@ -922,97 +951,81 @@ public class Paint_Delegate {
}
@LayoutlibDelegate
- /*package*/ static long native_getTextWidths(long native_object, char[] text, int index,
- int count, int bidiFlags, float[] widths) {
+ /*package*/ static int native_getTextWidths(long native_object, long native_typeface,
+ char[] text, int index, int count, int bidiFlags, float[] widths) {
+
+ if (widths != null) {
+ for (int i = 0; i< count; i++) {
+ widths[i]=0;
+ }
+ }
// get the delegate from the native int.
Paint_Delegate delegate = sManager.getDelegate(native_object);
if (delegate == null) {
return 0;
}
- if (delegate.mFonts.size() > 0) {
- // FIXME: handle multi-char characters (see measureText)
- float totalAdvance = 0;
- for (int i = 0; i < count; i++) {
- char c = text[i + index];
- boolean found = false;
- for (FontInfo info : delegate.mFonts) {
- if (info.mFont.canDisplay(c)) {
- float adv = info.mMetrics.charWidth(c);
- totalAdvance += adv;
- if (widths != null) {
- widths[i] = adv;
- }
-
- found = true;
- break;
- }
- }
-
- if (found == false) {
- // no advance for this char.
- if (widths != null) {
- widths[i] = 0.f;
- }
- }
- }
-
- return (int) totalAdvance;
- }
+ // native_typeface is passed here since Framework's old implementation did not have the
+ // typeface object associated with the Paint. Since, we follow the new framework way,
+ // we store the typeface with the paint and use it directly.
+ assert (native_typeface == delegate.mNativeTypeface);
- return 0;
+ RectF bounds = delegate.measureText(text, index, count, widths, 0, bidiFlags);
+ return ((int) (bounds.right - bounds.left));
}
@LayoutlibDelegate
- /*package*/ static long native_getTextWidths(long native_object, String text, int start,
- int end, int bidiFlags, float[] widths) {
- return native_getTextWidths(native_object, text.toCharArray(), start, end - start,
- bidiFlags, widths);
+ /*package*/ static int native_getTextWidths(long native_object, long native_typeface,
+ String text, int start, int end, int bidiFlags, float[] widths) {
+ return native_getTextWidths(native_object, native_typeface, text.toCharArray(), start,
+ end - start, bidiFlags, widths);
}
@LayoutlibDelegate
- /* package */static long native_getTextGlyphs(long native_object, String text, int start,
+ /* package */static int native_getTextGlyphs(long native_object, String text, int start,
int end, int contextStart, int contextEnd, int flags, char[] glyphs) {
// FIXME
return 0;
}
@LayoutlibDelegate
- /*package*/ static float native_getTextRunAdvances(long native_object,
+ /*package*/ static float native_getTextRunAdvances(long native_object, long native_typeface,
char[] text, int index, int count, int contextIndex, int contextCount,
- int flags, float[] advances, int advancesIndex) {
+ boolean isRtl, 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 || delegate.mFonts == null || delegate.mFonts.size() == 0) {
+ if (delegate == null) {
return 0.f;
}
- boolean isRtl = isRtl(flags);
- int limit = index + count;
- RectF bounds = new BidiRenderer(null, delegate, text).renderText(
- index, limit, isRtl, advances, advancesIndex, false, 0, 0);
+ // native_typeface is passed here since Framework's old implementation did not have the
+ // typeface object associated with the Paint. Since, we follow the new framework way,
+ // we store the typeface with the paint and use it directly.
+ assert (native_typeface == delegate.mNativeTypeface);
+
+ RectF bounds = delegate.measureText(text, index, count, advances, advancesIndex, isRtl);
return bounds.right - bounds.left;
}
@LayoutlibDelegate
- /*package*/ static float native_getTextRunAdvances(long native_object,
+ /*package*/ static float native_getTextRunAdvances(long native_object, long native_typeface,
String text, int start, int end, int contextStart, int contextEnd,
- int flags, float[] advances, int advancesIndex) {
+ boolean isRtl, 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 native_getTextRunAdvances(native_object, buffer, 0, count, contextStart,
- contextEnd - contextStart, flags, advances, advancesIndex);
+ return native_getTextRunAdvances(native_object, native_typeface, buffer, 0, count,
+ contextStart, contextEnd - contextStart, isRtl, advances, advancesIndex);
}
@LayoutlibDelegate
- /*package*/ static long native_getTextRunCursor(Paint thisPaint, long native_object, char[] text,
+ /*package*/ static int native_getTextRunCursor(Paint thisPaint, long native_object, char[] text,
int contextStart, int contextLength, int flags, int offset, int cursorOpt) {
// FIXME
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
@@ -1021,7 +1034,7 @@ public class Paint_Delegate {
}
@LayoutlibDelegate
- /*package*/ static long native_getTextRunCursor(Paint thisPaint, long native_object, String text,
+ /*package*/ static int native_getTextRunCursor(Paint thisPaint, long native_object, String text,
int contextStart, int contextEnd, int flags, int offset, int cursorOpt) {
// FIXME
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
@@ -1030,38 +1043,42 @@ public class Paint_Delegate {
}
@LayoutlibDelegate
- /*package*/ static void native_getTextPath(long native_object, int bidiFlags,
- char[] text, int index, int count, float x, float y, long path) {
+ /*package*/ static void native_getTextPath(long native_object, long native_typeface,
+ int bidiFlags, char[] text, int index, int count, float x, float y, long path) {
// FIXME
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
"Paint.getTextPath is not supported.", null, null /*data*/);
}
@LayoutlibDelegate
- /*package*/ static void native_getTextPath(long native_object, int bidiFlags,
- String text, int start, int end, float x, float y, long path) {
+ /*package*/ static void native_getTextPath(long native_object, long native_typeface,
+ int bidiFlags, String text, int start, int end, float x, float y, long path) {
// FIXME
Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
"Paint.getTextPath is not supported.", null, null /*data*/);
}
@LayoutlibDelegate
- /*package*/ static void nativeGetStringBounds(long nativePaint, String text, int start,
- int end, int bidiFlags, Rect bounds) {
- nativeGetCharArrayBounds(nativePaint, text.toCharArray(), start, end - start, bidiFlags,
- bounds);
+ /*package*/ static void nativeGetStringBounds(long nativePaint, long native_typeface,
+ String text, int start, int end, int bidiFlags, Rect bounds) {
+ nativeGetCharArrayBounds(nativePaint, native_typeface, text.toCharArray(), start,
+ end - start, bidiFlags, bounds);
}
@LayoutlibDelegate
- /*package*/ static void nativeGetCharArrayBounds(long nativePaint, char[] text, int index,
- int count, int bidiFlags, Rect bounds) {
+ /*package*/ static void nativeGetCharArrayBounds(long nativePaint, long native_typeface,
+ 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 || delegate.mFonts == null || delegate.mFonts.size() == 0) {
+ if (delegate == null) {
return;
}
- delegate.measureText(text, index, count, isRtl(bidiFlags)).roundOut(bounds);
+
+ // assert that the typeface passed is actually the one that we had stored.
+ assert (native_typeface == delegate.mNativeTypeface);
+
+ delegate.measureText(text, index, count, null, 0, bidiFlags).roundOut(bounds);
}
@LayoutlibDelegate
@@ -1069,6 +1086,22 @@ public class Paint_Delegate {
sManager.removeJavaReferenceFor(nativePaint);
}
+ @LayoutlibDelegate
+ /*package*/ static float native_getLetterSpacing(long nativePaint) {
+ // TODO: throw a fidelity warning.
+ return 0;
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void native_setLetterSpacing(long nativePaint, float letterSpacing) {
+ // pass.
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void native_setFontFeatureSettings(long nativePaint, String settings) {
+ // pass.
+ }
+
// ---- Private delegate/helper methods ----
/*package*/ Paint_Delegate() {
@@ -1087,6 +1120,7 @@ public class Paint_Delegate {
mJoin = paint.mJoin;
mTextAlign = paint.mTextAlign;
mTypeface = paint.mTypeface;
+ mNativeTypeface = paint.mNativeTypeface;
mStrokeWidth = paint.mStrokeWidth;
mStrokeMiter = paint.mStrokeMiter;
mTextSize = paint.mTextSize;
@@ -1110,6 +1144,7 @@ public class Paint_Delegate {
mJoin = Paint.Join.MITER.nativeInt;
mTextAlign = 0;
mTypeface = Typeface_Delegate.getDelegate(Typeface.sDefaults[0].native_instance);
+ mNativeTypeface = 0;
mStrokeWidth = 1.f;
mStrokeMiter = 4.f;
mTextSize = 20.f;
@@ -1132,12 +1167,18 @@ public class Paint_Delegate {
private void updateFontObject() {
if (mTypeface != null) {
// Get the fonts from the TypeFace object.
- List<Font> fonts = mTypeface.getFonts();
+ List<Font> fonts = mTypeface.getFonts(mFontVariant);
// create new font objects as well as FontMetrics, based on the current text size
// and skew info.
ArrayList<FontInfo> infoList = new ArrayList<FontInfo>(fonts.size());
for (Font font : fonts) {
+ if (font == null) {
+ // If the font is null, add null to infoList. When rendering the text, if this
+ // null is reached, a warning will be logged.
+ infoList.add(null);
+ continue;
+ }
FontInfo info = new FontInfo();
info.mFont = font.deriveFont(mTextSize);
if (mTextScaleX != 1.0 || mTextSkewX != 0) {
@@ -1155,9 +1196,16 @@ public class Paint_Delegate {
}
}
- /*package*/ RectF measureText(char[] text, int index, int count, boolean isRtl) {
- return new BidiRenderer(null, this, text).renderText(
- index, index + count, isRtl, null, 0, false, 0, 0);
+ /*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) {
@@ -1195,15 +1243,4 @@ public class Paint_Delegate {
delegate.mFlags &= ~flagMask;
}
}
-
- private static boolean isRtl(int flag) {
- switch(flag) {
- case Paint.BIDI_RTL:
- case Paint.BIDI_FORCE_RTL:
- case Paint.BIDI_DEFAULT_RTL:
- return true;
- default:
- return false;
- }
- }
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
index 6f6ef20..776398f 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Path_Delegate.java
@@ -142,7 +142,14 @@ public final class Path_Delegate {
}
@LayoutlibDelegate
- /*package*/ static long native_getFillType(long nPath) {
+ /*package*/ static boolean native_isConvex(long nPath) {
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
+ "Path.isConvex is not supported.", null, null);
+ return true;
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static int native_getFillType(long nPath) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
return 0;
@@ -290,14 +297,15 @@ public final class Path_Delegate {
}
@LayoutlibDelegate
- /*package*/ static void native_arcTo(long nPath, RectF oval,
+ /*package*/ static void native_arcTo(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(oval, startAngle, sweepAngle, forceMoveTo);
+ pathDelegate.arcTo(left, top, right, bottom, startAngle, sweepAngle, forceMoveTo);
}
@LayoutlibDelegate
@@ -311,16 +319,6 @@ public final class Path_Delegate {
}
@LayoutlibDelegate
- /*package*/ static void native_addRect(long nPath, RectF rect, int dir) {
- Path_Delegate pathDelegate = sManager.getDelegate(nPath);
- if (pathDelegate == null) {
- return;
- }
-
- pathDelegate.addRect(rect.left, rect.top, rect.right, rect.bottom, dir);
- }
-
- @LayoutlibDelegate
/*package*/ static void native_addRect(long nPath,
float left, float top, float right, float bottom, int dir) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
@@ -332,14 +330,15 @@ public final class Path_Delegate {
}
@LayoutlibDelegate
- /*package*/ static void native_addOval(long nPath, RectF oval, int dir) {
+ /*package*/ static void native_addOval(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(
- oval.left, oval.top, oval.width(), oval.height()), false);
+ left, top, right - left, bottom - top), false);
}
@LayoutlibDelegate
@@ -355,8 +354,8 @@ public final class Path_Delegate {
}
@LayoutlibDelegate
- /*package*/ static void native_addArc(long nPath, RectF oval,
- float startAngle, float sweepAngle) {
+ /*package*/ static void native_addArc(long nPath, float left, float top, float right,
+ float bottom, float startAngle, float sweepAngle) {
Path_Delegate pathDelegate = sManager.getDelegate(nPath);
if (pathDelegate == null) {
return;
@@ -364,13 +363,13 @@ public final class Path_Delegate {
// because x/y is the center of the circle, need to offset this by the radius
pathDelegate.mPath.append(new Arc2D.Float(
- oval.left, oval.top, oval.width(), oval.height(),
+ left, top, right - left, bottom - top,
-startAngle, -sweepAngle, Arc2D.OPEN), false);
}
@LayoutlibDelegate
- /*package*/ static void native_addRoundRect(
- long nPath, RectF rect, float rx, float ry, int dir) {
+ /*package*/ static void native_addRoundRect(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) {
@@ -378,14 +377,15 @@ public final class Path_Delegate {
}
pathDelegate.mPath.append(new RoundRectangle2D.Float(
- rect.left, rect.top, rect.width(), rect.height(), rx * 2, ry * 2), false);
+ left, top, right - left, bottom - top, rx * 2, ry * 2), false);
}
@LayoutlibDelegate
- /*package*/ static void native_addRoundRect(long nPath, RectF rect, float[] radii, int dir) {
+ /*package*/ static void native_addRoundRect(long nPath, float left, float top, float right,
+ float bottom, float[] radii, int dir) {
// Java2D doesn't support different rounded corners in each corner, so just use the
// first value.
- native_addRoundRect(nPath, rect, radii[0], radii[1], dir);
+ native_addRoundRect(nPath, left, top, right, bottom, radii[0], radii[1], dir);
// there can be a case where this API is used but with similar values for all corners, so
// in that case we don't warn.
@@ -484,6 +484,11 @@ public final class Path_Delegate {
sManager.removeJavaReferenceFor(nPath);
}
+ @LayoutlibDelegate
+ /*package*/ static float[] native_approximate(long nPath, float error) {
+ Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED, "Path.approximate() not supported", null);
+ return new float[0];
+ }
// ---- Private helper methods ----
@@ -603,6 +608,9 @@ public final class Path_Delegate {
* @param y The y-coordinate of the end of a line
*/
private void lineTo(float x, float y) {
+ if (isEmpty()) {
+ mPath.moveTo(mLastX = 0, mLastY = 0);
+ }
mPath.lineTo(mLastX = x, mLastY = y);
}
@@ -707,14 +715,19 @@ public final class Path_Delegate {
* 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 oval The bounds of oval defining shape and size of the arc
+ * @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
*/
- private void arcTo(RectF oval, float startAngle, float sweepAngle, boolean forceMoveTo) {
- Arc2D arc = new Arc2D.Float(oval.left, oval.top, oval.width(), oval.height(), -startAngle,
+ private 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*/);
diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
index 6049919..4ac376c 100644
--- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffColorFilter_Delegate.java
@@ -19,6 +19,14 @@ 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
*
@@ -40,32 +48,89 @@ public class PorterDuffColorFilter_Delegate extends ColorFilter_Delegate {
// ---- delegate data ----
+ private final int mSrcColor;
+ private final Mode mMode;
+
+
// ---- Public Helper methods ----
@Override
public boolean isSupported() {
- return false;
+ return true;
}
@Override
public String getSupportMessage() {
- return "PorterDuff Color Filters are not supported.";
+ return "PorterDuff Color Filter is not supported for mode: " + mMode.name() + ".";
+ }
+
+ @Override
+ public void applyFilter(Graphics2D g, int width, int height) {
+ BufferedImage image = createFilterImage(width, height);
+ g.setComposite(getComposite(mMode, 0xFF));
+ g.drawImage(image, 0, 0, null);
}
// ---- native methods ----
@LayoutlibDelegate
/*package*/ static long native_CreatePorterDuffFilter(int srcColor, int porterDuffMode) {
- PorterDuffColorFilter_Delegate newDelegate = new PorterDuffColorFilter_Delegate();
+ PorterDuffColorFilter_Delegate newDelegate =
+ new PorterDuffColorFilter_Delegate(srcColor, porterDuffMode);
return sManager.addNewDelegate(newDelegate);
}
- @LayoutlibDelegate
- /*package*/ static long nCreatePorterDuffFilter(long nativeFilter, int srcColor,
- int porterDuffMode) {
- // pass
- return 0;
- }
// ---- Private delegate/helper methods ----
+
+ private PorterDuffColorFilter_Delegate(int srcColor, int mode) {
+ mSrcColor = srcColor;
+ mMode = getCompatibleMode(getPorterDuffMode(mode));
+ }
+
+ private BufferedImage createFilterImage(int width, int height) {
+ BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ Graphics2D graphics = image.createGraphics();
+ try {
+ graphics.setColor(new java.awt.Color(mSrcColor, true /* hasAlpha */));
+ graphics.fillRect(0, 0, width, height);
+ } finally {
+ graphics.dispose();
+ }
+ return image;
+ }
+
+ // 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
+ // 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/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
index a89fd57..8825f84 100644
--- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java
@@ -16,14 +16,16 @@
package android.graphics;
-import com.android.ide.common.rendering.api.LayoutLog;
-import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager;
+import com.android.layoutlib.bridge.impl.PorterDuffUtility;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-import java.awt.AlphaComposite;
+import android.graphics.PorterDuff.Mode;
+
import java.awt.Composite;
+import static com.android.layoutlib.bridge.impl.PorterDuffUtility.getPorterDuffMode;
+
/**
* Delegate implementing the native methods of android.graphics.PorterDuffXfermode
*
@@ -43,17 +45,17 @@ public class PorterDuffXfermode_Delegate extends Xfermode_Delegate {
// ---- delegate data ----
- private final int mMode;
+ private final Mode mMode;
// ---- Public Helper methods ----
- public PorterDuff.Mode getMode() {
- return getPorterDuffMode(mMode);
+ public Mode getMode() {
+ return mMode;
}
@Override
public Composite getComposite(int alpha) {
- return getComposite(getPorterDuffMode(mMode), alpha);
+ return PorterDuffUtility.getComposite(mMode, alpha);
}
@Override
@@ -67,62 +69,6 @@ public class PorterDuffXfermode_Delegate extends Xfermode_Delegate {
return null;
}
- public static PorterDuff.Mode getPorterDuffMode(int mode) {
- for (PorterDuff.Mode m : PorterDuff.Mode.values()) {
- if (m.nativeInt == mode) {
- return m;
- }
- }
-
- Bridge.getLog().error(LayoutLog.TAG_BROKEN,
- String.format("Unknown PorterDuff.Mode: %d", mode), null /*data*/);
- assert false;
- return PorterDuff.Mode.SRC_OVER;
- }
-
- public static Composite getComposite(PorterDuff.Mode mode, int alpha) {
- float falpha = alpha != 0xFF ? (float)alpha / 255.f : 1.f;
- switch (mode) {
- case CLEAR:
- return AlphaComposite.getInstance(AlphaComposite.CLEAR, falpha);
- case DARKEN:
- break;
- case DST:
- return AlphaComposite.getInstance(AlphaComposite.DST, falpha);
- case DST_ATOP:
- return AlphaComposite.getInstance(AlphaComposite.DST_ATOP, falpha);
- case DST_IN:
- return AlphaComposite.getInstance(AlphaComposite.DST_IN, falpha);
- case DST_OUT:
- return AlphaComposite.getInstance(AlphaComposite.DST_OUT, falpha);
- case DST_OVER:
- return AlphaComposite.getInstance(AlphaComposite.DST_OVER, falpha);
- case LIGHTEN:
- break;
- case MULTIPLY:
- break;
- case SCREEN:
- break;
- case SRC:
- return AlphaComposite.getInstance(AlphaComposite.SRC, falpha);
- case SRC_ATOP:
- return AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, falpha);
- case SRC_IN:
- return AlphaComposite.getInstance(AlphaComposite.SRC_IN, falpha);
- case SRC_OUT:
- return AlphaComposite.getInstance(AlphaComposite.SRC_OUT, falpha);
- case SRC_OVER:
- return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha);
- case XOR:
- return AlphaComposite.getInstance(AlphaComposite.XOR, falpha);
- }
-
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
- String.format("Unsupported PorterDuff Mode: %s", mode.name()),
- null, null /*data*/);
-
- return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha);
- }
// ---- native methods ----
@@ -135,6 +81,7 @@ public class PorterDuffXfermode_Delegate extends Xfermode_Delegate {
// ---- Private delegate/helper methods ----
private PorterDuffXfermode_Delegate(int mode) {
- mMode = mode;
+ mMode = getPorterDuffMode(mode);
}
+
}
diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
index b640ece..eb29835 100644
--- a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java
@@ -68,20 +68,6 @@ public class RadialGradient_Delegate extends Gradient_Delegate {
tileMode);
}
- @LayoutlibDelegate
- /*package*/ static long nativePostCreate1(long native_shader, float x, float y, float radius,
- int colors[], float positions[], int tileMode) {
- // nothing to be done here.
- return 0;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nativePostCreate2(long native_shader, float x, float y, float radius,
- int color0, int color1, int tileMode) {
- // nothing to be done here.
- return 0;
- }
-
// ---- Private delegate/helper methods ----
/**
diff --git a/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java
index ea23649..edb7025 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Region_Delegate.java
@@ -275,21 +275,20 @@ public class Region_Delegate {
}
@LayoutlibDelegate
- /*package*/ static boolean nativeSetRegion(long native_dst, long native_src) {
+ /*package*/ static void nativeSetRegion(long native_dst, long native_src) {
Region_Delegate dstRegion = sManager.getDelegate(native_dst);
if (dstRegion == null) {
- return true;
+ return;
}
Region_Delegate srcRegion = sManager.getDelegate(native_src);
if (srcRegion == null) {
- return true;
+ return;
}
dstRegion.mArea.reset();
dstRegion.mArea.add(srcRegion.mArea);
- return true;
}
@LayoutlibDelegate
diff --git a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java
index 70a0a43..14e9960 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java
@@ -76,13 +76,12 @@ public abstract class Shader_Delegate {
// ---- native methods ----
@LayoutlibDelegate
- /*package*/ static void nativeDestructor(long native_shader, long native_skiaShader) {
+ /*package*/ static void nativeDestructor(long native_shader) {
sManager.removeJavaReferenceFor(native_shader);
}
@LayoutlibDelegate
- /*package*/ static void nativeSetLocalMatrix(long native_shader, long native_skiaShader,
- long matrix_instance) {
+ /*package*/ static void nativeSetLocalMatrix(long native_shader, long matrix_instance) {
// get the delegate from the native int.
Shader_Delegate shaderDelegate = sManager.getDelegate(native_shader);
if (shaderDelegate == null) {
diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
index f2b3e8d..95a57a9 100644
--- a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java
@@ -62,20 +62,6 @@ public class SweepGradient_Delegate extends Gradient_Delegate {
return nativeCreate1(x, y, new int[] { color0, color1 }, null /*positions*/);
}
- @LayoutlibDelegate
- /*package*/ static long nativePostCreate1(long native_shader, float cx, float cy,
- int[] colors, float[] positions) {
- // nothing to be done here.
- return 0;
- }
-
- @LayoutlibDelegate
- /*package*/ static long nativePostCreate2(long native_shader, float cx, float cy,
- int color0, int color1) {
- // nothing to be done here.
- return 0;
- }
-
// ---- Private delegate/helper methods ----
/**
diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
index 60cd157..276e134 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java
@@ -16,13 +16,11 @@
package android.graphics;
-import com.android.ide.common.rendering.api.LayoutLog;
-import com.android.layoutlib.bridge.Bridge;
+import com.android.annotations.NonNull;
import com.android.layoutlib.bridge.impl.DelegateManager;
-import com.android.layoutlib.bridge.impl.FontLoader;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-import android.content.res.AssetManager;
+import android.graphics.FontFamily_Delegate.FontVariant;
import java.awt.Font;
import java.io.File;
@@ -44,136 +42,133 @@ import java.util.List;
*/
public final class Typeface_Delegate {
- private static final String SYSTEM_FONTS = "/system/fonts/";
+ public static final String SYSTEM_FONTS = "/system/fonts/";
// ---- delegate manager ----
private static final DelegateManager<Typeface_Delegate> sManager =
new DelegateManager<Typeface_Delegate>(Typeface_Delegate.class);
// ---- delegate helper data ----
- private static final String DEFAULT_FAMILY = "sans-serif";
-
- private static FontLoader sFontLoader;
- private static final List<Typeface_Delegate> sPostInitDelegate =
- new ArrayList<Typeface_Delegate>();
+ private static String sFontLocation;
// ---- delegate data ----
- private final String mFamily;
- private int mStyle;
- private List<Font> mFonts;
+ @NonNull
+ private final FontFamily_Delegate[] mFontFamilies; // the reference to FontFamily_Delegate.
+ /** @see Font#getStyle() */
+ private final int mStyle;
+ private final int mWeight;
+ private static long sDefaultTypeface;
// ---- Public Helper methods ----
-
- public static synchronized void init(FontLoader fontLoader) {
- sFontLoader = fontLoader;
-
- for (Typeface_Delegate delegate : sPostInitDelegate) {
- delegate.init();
- }
- sPostInitDelegate.clear();
+ public static synchronized void setFontLocation(String fontLocation) {
+ sFontLocation = fontLocation;
+ FontFamily_Delegate.setFontLocation(fontLocation);
}
public static Typeface_Delegate getDelegate(long nativeTypeface) {
return sManager.getDelegate(nativeTypeface);
}
- public static List<Font> getFonts(Typeface typeface) {
- return getFonts(typeface.native_instance);
- }
-
- public static List<Font> getFonts(long native_int) {
- Typeface_Delegate delegate = sManager.getDelegate(native_int);
- if (delegate == null) {
- return null;
+ /**
+ * Return a list of fonts that match the style and variant. The list is ordered according to
+ * preference of fonts.
+ *
+ * The list 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 List<Font> getFonts(FontVariant variant) {
+ assert variant != FontVariant.NONE;
+
+ // Calculate the required weight based on style and weight of this typeface.
+ int weight = mWeight + ((mStyle & Font.BOLD) == 0 ? 0 : FontFamily_Delegate.BOLD_FONT_WEIGHT_DELTA);
+ if (weight > 900) {
+ weight = 900;
}
-
- return delegate.getFonts();
- }
-
- public List<Font> getFonts() {
- return mFonts;
+ final boolean isItalic = (mStyle & Font.ITALIC) != 0;
+ List<Font> fonts = new ArrayList<Font>(mFontFamilies.length);
+ for (int i = 0; i < mFontFamilies.length; i++) {
+ FontFamily_Delegate ffd = mFontFamilies[i];
+ if (ffd != null && ffd.isValid()) {
+ Font font = ffd.getFont(weight, isItalic);
+ if (font != null) {
+ FontVariant ffdVariant = ffd.getVariant();
+ if (ffdVariant == FontVariant.NONE) {
+ fonts.add(font);
+ continue;
+ }
+ // 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.
+ assert i < mFontFamilies.length - 1;
+ FontFamily_Delegate ffd2 = mFontFamilies[++i];
+ assert ffd2 != null;
+ FontVariant ffd2Variant = ffd2.getVariant();
+ Font font2 = ffd2.getFont(weight, isItalic);
+ assert ffd2Variant != FontVariant.NONE && ffd2Variant != ffdVariant
+ && font2 != null;
+ // Add the font with the matching variant to the list.
+ if (variant == ffd.getVariant()) {
+ fonts.add(font);
+ } else {
+ fonts.add(font2);
+ }
+ } else {
+ // 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.
+ fonts.add(null);
+ }
+ }
+ }
+ return fonts;
}
// ---- native methods ----
@LayoutlibDelegate
- /*package*/ static synchronized long nativeCreate(String familyName, int style) {
- if (familyName == null) {
- familyName = DEFAULT_FAMILY;
- }
- if (style < 0) {
- style = Typeface.NORMAL;
+ /*package*/ static synchronized long nativeCreateFromTypeface(long native_instance, int style) {
+ Typeface_Delegate delegate = sManager.getDelegate(native_instance);
+ if (delegate == null) {
+ delegate = sManager.getDelegate(sDefaultTypeface);
}
-
- Typeface_Delegate newDelegate = new Typeface_Delegate(familyName, style);
- if (sFontLoader != null) {
- newDelegate.init();
- } else {
- // font loader has not been initialized yet, add the delegate to a list of delegates
- // to init when the font loader is initialized.
- // There won't be any rendering before this happens anyway.
- sPostInitDelegate.add(newDelegate);
+ if (delegate == null) {
+ return 0;
}
- return sManager.addNewDelegate(newDelegate);
+ return sManager.addNewDelegate(new Typeface_Delegate(delegate.mFontFamilies, style,
+ delegate.mWeight));
}
@LayoutlibDelegate
- /*package*/ static synchronized long nativeCreateFromTypeface(long native_instance, int style) {
+ /*package*/ static long nativeCreateWeightAlias(long native_instance, int weight) {
Typeface_Delegate delegate = sManager.getDelegate(native_instance);
if (delegate == null) {
- return 0;
+ delegate = sManager.getDelegate(sDefaultTypeface);
}
-
- Typeface_Delegate newDelegate = new Typeface_Delegate(delegate.mFamily, style);
- if (sFontLoader != null) {
- newDelegate.init();
- } else {
- // font loader has not been initialized yet, add the delegate to a list of delegates
- // to init when the font loader is initialized.
- // There won't be any rendering before this happens anyway.
- sPostInitDelegate.add(newDelegate);
+ if (delegate == null) {
+ return 0;
}
-
- return sManager.addNewDelegate(newDelegate);
+ Typeface_Delegate weightAlias =
+ new Typeface_Delegate(delegate.mFontFamilies, delegate.mStyle, weight);
+ return sManager.addNewDelegate(weightAlias);
}
@LayoutlibDelegate
- /*package*/ static synchronized long nativeCreateFromAsset(AssetManager mgr, String path) {
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Typeface.createFromAsset() is not supported.", null /*throwable*/, null /*data*/);
- return 0;
- }
-
- @LayoutlibDelegate
- /*package*/ static synchronized long nativeCreateFromFile(String path) {
- if (path.startsWith(SYSTEM_FONTS) ) {
- String relativePath = path.substring(SYSTEM_FONTS.length());
- File f = new File(sFontLoader.getOsFontsLocation(), relativePath);
-
- try {
- Font font = Font.createFont(Font.TRUETYPE_FONT, f);
- if (font != null) {
- Typeface_Delegate newDelegate = new Typeface_Delegate(font);
- return sManager.addNewDelegate(newDelegate);
- }
- } catch (Exception e) {
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_BROKEN,
- String.format("Unable to load font %1$s", relativePath),
- null /*throwable*/, null /*data*/);
- }
- } else {
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_UNSUPPORTED,
- "Typeface.createFromFile() can only work with platform fonts located in " +
- SYSTEM_FONTS,
- null /*throwable*/, null /*data*/);
+ /*package*/ static synchronized long nativeCreateFromArray(long[] familyArray) {
+ FontFamily_Delegate[] fontFamilies = new FontFamily_Delegate[familyArray.length];
+ for (int i = 0; i < familyArray.length; i++) {
+ fontFamilies[i] = FontFamily_Delegate.getDelegate(familyArray[i]);
}
-
-
- // return a copy of the base font
- return nativeCreate(null, 0);
+ Typeface_Delegate delegate = new Typeface_Delegate(fontFamilies, Typeface.NORMAL);
+ return sManager.addNewDelegate(delegate);
}
@LayoutlibDelegate
@@ -191,24 +186,25 @@ public final class Typeface_Delegate {
return delegate.mStyle;
}
- // ---- Private delegate/helper methods ----
-
- private Typeface_Delegate(String family, int style) {
- mFamily = family;
- mStyle = style;
+ @LayoutlibDelegate
+ /*package*/ static void nativeSetDefault(long native_instance) {
+ sDefaultTypeface = native_instance;
}
- private Typeface_Delegate(Font font) {
- mFamily = font.getFamily();
- mStyle = Typeface.NORMAL;
+ @LayoutlibDelegate
+ /*package*/ static File getSystemFontConfigLocation() {
+ return new File(sFontLocation);
+ }
- mFonts = sFontLoader.getFallbackFonts(mStyle);
+ // ---- Private delegate/helper methods ----
- // insert the font glyph first.
- mFonts.add(0, font);
+ private Typeface_Delegate(@NonNull FontFamily_Delegate[] fontFamilies, int style) {
+ this(fontFamilies, style, FontFamily_Delegate.DEFAULT_FONT_WEIGHT);
}
- private void init() {
- mFonts = sFontLoader.getFont(mFamily, mStyle);
+ public Typeface_Delegate(@NonNull FontFamily_Delegate[] fontFamilies, int style, int weight) {
+ mFontFamilies = fontFamilies;
+ mStyle = style;
+ mWeight = weight;
}
}
diff --git a/tools/layoutlib/bridge/src/android/os/SystemProperties_Delegate.java b/tools/layoutlib/bridge/src/android/os/SystemProperties_Delegate.java
new file mode 100644
index 0000000..af0c456
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/os/SystemProperties_Delegate.java
@@ -0,0 +1,105 @@
+/*
+ * 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.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) {
+ return native_get(key, "");
+ }
+
+ @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.
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java b/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java
index 973fa0e..6247dae 100644
--- a/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/text/AndroidBidi_Delegate.java
@@ -37,14 +37,17 @@ public class AndroidBidi_Delegate {
switch (dir) {
case 0: // Layout.DIR_REQUEST_LTR
+ dir = Bidi.LTR;
+ break;
case 1: // Layout.DIR_REQUEST_RTL
- break; // No change.
- case -1:
- dir = Bidi.LEVEL_DEFAULT_LTR;
+ dir = Bidi.RTL;
break;
- case -2:
+ case -1: // Layout.DIR_REQUEST_DEFAULT_RTL
dir = Bidi.LEVEL_DEFAULT_RTL;
break;
+ case -2: // Layout.DIR_REQUEST_DEFAULT_LTR
+ dir = Bidi.LEVEL_DEFAULT_LTR;
+ break;
default:
// Invalid code. Log error, assume LEVEL_DEFAULT_LTR and continue.
Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Invalid direction flag", null);
diff --git a/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java
new file mode 100644
index 0000000..5a467b2
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/text/StaticLayout_Delegate.java
@@ -0,0 +1,55 @@
+package android.text;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import java.text.CharacterIterator;
+import java.util.Arrays;
+import java.util.Locale;
+
+import com.ibm.icu.lang.UCharacter;
+import com.ibm.icu.text.BreakIterator;
+import com.ibm.icu.util.ULocale;
+import javax.swing.text.Segment;
+
+/**
+ * Delegate that provides implementation for native methods in {@link android.text.StaticLayout}
+ *
+ * Through the layoutlib_create tool, selected methods of Handler have been replaced
+ * by calls to methods of the same name in this delegate class.
+ *
+ */
+public class StaticLayout_Delegate {
+
+ /**
+ * Fills the recycle array with positions that are suitable to break the text at. The array
+ * must be terminated by '-1'.
+ */
+ @LayoutlibDelegate
+ /*package*/ static int[] nLineBreakOpportunities(String locale, char[] text, int length,
+ int[] recycle) {
+ BreakIterator iterator = BreakIterator.getLineInstance(new ULocale(locale));
+ Segment segment = new Segment(text, 0, length);
+ iterator.setText(segment);
+ if (recycle == null) {
+ // Because 42 is the answer to everything.
+ recycle = new int[42];
+ }
+ int breakOpp = iterator.first();
+ recycle[0] = breakOpp;
+ //noinspection ConstantConditions
+ assert BreakIterator.DONE == -1;
+ for (int i = 1; breakOpp != BreakIterator.DONE; ++i) {
+ if (i >= recycle.length) {
+ recycle = doubleSize(recycle);
+ }
+ assert (i < recycle.length);
+ breakOpp = iterator.next();
+ recycle[i] = breakOpp;
+ }
+ return recycle;
+ }
+
+ private static int[] doubleSize(int[] array) {
+ return Arrays.copyOf(array, array.length * 2);
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/text/format/Time_Delegate.java b/tools/layoutlib/bridge/src/android/text/format/Time_Delegate.java
index 320dd0d..ed8498f 100644
--- a/tools/layoutlib/bridge/src/android/text/format/Time_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/text/format/Time_Delegate.java
@@ -39,91 +39,6 @@ public class Time_Delegate {
// Format used by toString()
private static final String FORMAT = "%1$tY%1$tm%1$tdT%1$tH%1$tM%1$tS<%1$tZ>";
- @LayoutlibDelegate
- /*package*/ static long normalize(Time thisTime, boolean ignoreDst) {
- long millis = toMillis(thisTime, ignoreDst);
- set(thisTime, millis);
- return millis;
- }
-
- @LayoutlibDelegate
- /*package*/ static void switchTimezone(Time thisTime, String timezone) {
- Calendar c = timeToCalendar(thisTime);
- c.setTimeZone(TimeZone.getTimeZone(timezone));
- calendarToTime(c, thisTime);
- }
-
- @LayoutlibDelegate
- /*package*/ static int nativeCompare(Time a, Time b) {
- return timeToCalendar(a).compareTo(timeToCalendar(b));
- }
-
- @LayoutlibDelegate
- /*package*/ static String format1(Time thisTime, String format) {
-
- try {
- // Change the format by adding changing '%' to "%1$t". This is required to tell the
- // formatter which argument to use from the argument list. '%%' is left as is. In the
- // replacement string, $0 refers to matched pattern. \\1 means '1', written this way to
- // separate it from 0. \\$ means '$', written this way to suppress the special meaning
- // of $.
- return String.format(
- p.matcher(format).replaceAll("$0\\1\\$t"),
- timeToCalendar(thisTime));
- } catch (UnknownFormatConversionException e) {
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_STRFTIME, "Unrecognized format", e, format);
- return format;
- }
- }
-
- /**
- * Return the current time in YYYYMMDDTHHMMSS<tz> format
- */
- @LayoutlibDelegate
- /*package*/ static String toString(Time thisTime) {
- Calendar c = timeToCalendar(thisTime);
- return String.format(FORMAT, c);
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nativeParse(Time thisTime, String s) {
- Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
- "android.text.format.Time.parse() not supported.", null);
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static boolean nativeParse3339(Time thisTime, String s) {
- Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
- "android.text.format.Time.parse3339() not supported.", null);
- return false;
- }
-
- @LayoutlibDelegate
- /*package*/ static void setToNow(Time thisTime) {
- calendarToTime(getCalendarInstance(thisTime), thisTime);
- }
-
- @LayoutlibDelegate
- /*package*/ static long toMillis(Time thisTime, boolean ignoreDst) {
- // TODO: Respect ignoreDst.
- return timeToCalendar(thisTime).getTimeInMillis();
- }
-
- @LayoutlibDelegate
- /*package*/ static void set(Time thisTime, long millis) {
- Calendar c = getCalendarInstance(thisTime);
- c.setTimeInMillis(millis);
- calendarToTime(c,thisTime);
- }
-
- @LayoutlibDelegate
- /*package*/ static String format2445(Time thisTime) {
- Bridge.getLog().error(LayoutLog.TAG_UNSUPPORTED,
- "android.text.format.Time.format2445() not supported.", null);
- return "";
- }
-
// ---- private helper methods ----
private static Calendar timeToCalendar(Time time) {
diff --git a/tools/layoutlib/bridge/src/android/util/BridgeXmlPullAttributes.java b/tools/layoutlib/bridge/src/android/util/BridgeXmlPullAttributes.java
index 6ac5b02..691339e 100644
--- a/tools/layoutlib/bridge/src/android/util/BridgeXmlPullAttributes.java
+++ b/tools/layoutlib/bridge/src/android/util/BridgeXmlPullAttributes.java
@@ -22,6 +22,7 @@ import com.android.internal.util.XmlUtils;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.BridgeConstants;
import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.impl.ResourceHelper;
import com.android.resources.ResourceType;
import org.xmlpull.v1.XmlPullParser;
@@ -210,6 +211,9 @@ public class BridgeXmlPullAttributes extends XmlPullAttributes {
value = r.getValue();
}
+ if (value.charAt(0) == '#') {
+ return ResourceHelper.getColor(value);
+ }
return XmlUtils.convertValueToInt(value, defaultValue);
}
diff --git a/tools/layoutlib/bridge/src/android/os/Build_Delegate.java b/tools/layoutlib/bridge/src/android/util/Xml_Delegate.java
index ff82a5e..a193330 100644
--- a/tools/layoutlib/bridge/src/android/os/Build_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/util/Xml_Delegate.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * 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.
@@ -14,35 +14,36 @@
* limitations under the License.
*/
-package android.os;
+package android.util;
-import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
-import java.util.Map;
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
/**
- * Delegate implementing the native methods of android.os.Build
+ * Delegate overriding some methods of android.util.Xml
*
- * Through the layoutlib_create tool, the original native methods of Build have been replaced
+ * Through the layoutlib_create tool, the original methods of Xml 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 Build_Delegate {
+public class Xml_Delegate {
@LayoutlibDelegate
- /*package*/ static String getString(String property) {
- Map<String, String> properties = Bridge.getPlatformProperties();
- String value = properties.get(property);
- if (value != null) {
- return value;
+ /*package*/ static XmlPullParser newPullParser() {
+ try {
+ KXmlParser parser = new KXmlParser();
+ // The prebuilt kxml2 library with the IDE doesn't support DOCECL.
+// parser.setFeature(XmlPullParser.FEATURE_PROCESS_DOCDECL, true);
+ parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
+ return parser;
+ } catch (XmlPullParserException e) {
+ throw new AssertionError();
}
-
- return Build.UNKNOWN;
}
-
}
diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
index 941f1ce6..36102f1 100644
--- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
+++ b/tools/layoutlib/bridge/src/android/view/BridgeInflater.java
@@ -32,10 +32,6 @@ import org.xmlpull.v1.XmlPullParser;
import android.content.Context;
import android.util.AttributeSet;
-import android.view.InflateException;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
import java.io.File;
@@ -125,10 +121,11 @@ public final class BridgeInflater extends LayoutInflater {
}
@Override
- public View createViewFromTag(View parent, String name, AttributeSet attrs) {
+ public View createViewFromTag(View parent, String name, AttributeSet attrs,
+ boolean inheritContext) {
View view = null;
try {
- view = super.createViewFromTag(parent, name, attrs);
+ view = super.createViewFromTag(parent, name, attrs, inheritContext);
} catch (InflateException e) {
// try to load the class from using the custom view loader
try {
@@ -154,6 +151,9 @@ public final class BridgeInflater extends LayoutInflater {
@Override
public View inflate(int resource, ViewGroup root) {
Context context = getContext();
+ while (context instanceof ContextThemeWrapper) {
+ context = ((ContextThemeWrapper) context).getBaseContext();
+ }
if (context instanceof BridgeContext) {
BridgeContext bridgeContext = (BridgeContext)context;
@@ -216,43 +216,16 @@ public final class BridgeInflater extends LayoutInflater {
}
private void setupViewInContext(View view, AttributeSet attrs) {
- if (getContext() instanceof BridgeContext) {
- BridgeContext bc = (BridgeContext) getContext();
- if (attrs instanceof BridgeXmlBlockParser) {
- BridgeXmlBlockParser parser = (BridgeXmlBlockParser) attrs;
-
- // get the view key
- Object viewKey = parser.getViewCookie();
-
- if (viewKey == null) {
- int currentDepth = parser.getDepth();
-
- // test whether we are in an included file or in a adapter binding view.
- BridgeXmlBlockParser previousParser = bc.getPreviousParser();
- if (previousParser != null) {
- // looks like we inside an embedded layout.
- // only apply the cookie of the calling node (<include>) if we are at the
- // top level of the embedded layout. If there is a merge tag, then
- // skip it and look for the 2nd level
- int testDepth = mIsInMerge ? 2 : 1;
- if (currentDepth == testDepth) {
- viewKey = previousParser.getViewCookie();
- // if we are in a merge, wrap the cookie in a MergeCookie.
- if (viewKey != null && mIsInMerge) {
- viewKey = new MergeCookie(viewKey);
- }
- }
- } else if (mResourceReference != null && currentDepth == 1) {
- // else if there's a resource reference, this means we are in an adapter
- // binding case. Set the resource ref as the view cookie only for the top
- // level view.
- viewKey = mResourceReference;
- }
- }
-
- if (viewKey != null) {
- bc.addViewKey(view, viewKey);
- }
+ Context context = getContext();
+ while (context instanceof ContextThemeWrapper) {
+ context = ((ContextThemeWrapper) context).getBaseContext();
+ }
+ if (context instanceof BridgeContext) {
+ BridgeContext bc = (BridgeContext) context;
+ // get the view key
+ Object viewKey = getViewKeyFromParser(attrs, bc, mResourceReference, mIsInMerge);
+ if (viewKey != null) {
+ bc.addViewKey(view, viewKey);
}
}
}
@@ -269,4 +242,44 @@ public final class BridgeInflater extends LayoutInflater {
public LayoutInflater cloneInContext(Context newContext) {
return new BridgeInflater(this, newContext);
}
+
+ /*package*/ static Object getViewKeyFromParser(AttributeSet attrs, BridgeContext bc,
+ ResourceReference resourceReference, boolean isInMerge) {
+
+ if (!(attrs instanceof BridgeXmlBlockParser)) {
+ return null;
+ }
+ BridgeXmlBlockParser parser = ((BridgeXmlBlockParser) attrs);
+
+ // get the view key
+ Object viewKey = parser.getViewCookie();
+
+ if (viewKey == null) {
+ int currentDepth = parser.getDepth();
+
+ // test whether we are in an included file or in a adapter binding view.
+ BridgeXmlBlockParser previousParser = bc.getPreviousParser();
+ if (previousParser != null) {
+ // looks like we are inside an embedded layout.
+ // only apply the cookie of the calling node (<include>) if we are at the
+ // top level of the embedded layout. If there is a merge tag, then
+ // skip it and look for the 2nd level
+ int testDepth = isInMerge ? 2 : 1;
+ if (currentDepth == testDepth) {
+ viewKey = previousParser.getViewCookie();
+ // if we are in a merge, wrap the cookie in a MergeCookie.
+ if (viewKey != null && isInMerge) {
+ viewKey = new MergeCookie(viewKey);
+ }
+ }
+ } else if (resourceReference != null && currentDepth == 1) {
+ // else if there's a resource reference, this means we are in an adapter
+ // binding case. Set the resource ref as the view cookie only for the top
+ // level view.
+ viewKey = resourceReference;
+ }
+ }
+
+ return viewKey;
+ }
}
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index dd2cbc1..c403ce6 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -23,22 +23,13 @@ import com.android.internal.view.IInputMethodClient;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.graphics.Bitmap;
-import android.graphics.Rect;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IRemoteCallback;
import android.os.RemoteException;
import android.util.DisplayMetrics;
-import android.view.Display;
-import android.view.Gravity;
-import android.view.IApplicationToken;
-import android.view.IInputFilter;
-import android.view.IOnKeyguardExitResult;
-import android.view.IRotationWatcher;
-import android.view.IWindowManager;
-import android.view.IWindowSession;
-import java.util.List;
+import java.lang.Override;
/**
* Basic implementation of {@link IWindowManager} so that {@link Display} (and
@@ -81,7 +72,7 @@ public class IWindowManagerImpl implements IWindowManager {
@Override
public void addAppToken(int arg0, IApplicationToken arg1, int arg2, int arg3, int arg4,
- boolean arg5, boolean arg6, int arg7, int arg8)
+ boolean arg5, boolean arg6, int arg7, int arg8, boolean arg9, boolean arg10)
throws RemoteException {
// TODO Auto-generated method stub
@@ -204,8 +195,8 @@ public class IWindowManagerImpl implements IWindowManager {
}
@Override
- public IWindowSession openSession(IInputMethodClient arg0, IInputContext arg1)
- throws RemoteException {
+ public IWindowSession openSession(IWindowSessionCallback argn1, IInputMethodClient arg0,
+ IInputContext arg1) throws RemoteException {
// TODO Auto-generated method stub
return null;
}
@@ -230,6 +221,13 @@ public class IWindowManagerImpl implements IWindowManager {
}
@Override
+ public void overridePendingAppTransitionAspectScaledThumb(Bitmap srcThumb, int startX,
+ int startY, int targetWidth, int targetHeight, IRemoteCallback startedCallback,
+ boolean scaleUp) {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
public void pauseKeyDispatching(IBinder arg0) throws RemoteException {
// TODO Auto-generated method stub
@@ -285,6 +283,11 @@ public class IWindowManagerImpl implements IWindowManager {
}
@Override
+ public float getCurrentAnimatorScale() throws RemoteException {
+ return 0;
+ }
+
+ @Override
public void setAppGroupId(IBinder arg0, int arg1) throws RemoteException {
// TODO Auto-generated method stub
@@ -364,6 +367,11 @@ public class IWindowManagerImpl implements IWindowManager {
}
@Override
+ public void setScreenCaptureDisabled(int userId, boolean disabled) {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
public void updateRotation(boolean arg0, boolean arg1) throws RemoteException {
// TODO Auto-generated method stub
}
@@ -428,11 +436,6 @@ public class IWindowManagerImpl implements IWindowManager {
}
@Override
- public boolean waitForWindowDrawn(IBinder token, IRemoteCallback callback) {
- return false;
- }
-
- @Override
public IBinder asBinder() {
// TODO Auto-generated method stub
return null;
@@ -448,54 +451,41 @@ public class IWindowManagerImpl implements IWindowManager {
}
@Override
- public void lockNow(Bundle options) {
- // TODO Auto-generated method stub
- }
-
- @Override
- public boolean isSafeModeEnabled() {
- return false;
+ public void keyguardGoingAway(boolean disableWindowAnimations,
+ boolean keyguardGoingToNotificationShade) throws RemoteException {
}
@Override
- public IBinder getFocusedWindowToken() {
+ public void lockNow(Bundle options) {
// TODO Auto-generated method stub
- return null;
}
@Override
- public void setInputFilter(IInputFilter filter) throws RemoteException {
- // TODO Auto-generated method stub
+ public boolean isSafeModeEnabled() {
+ return false;
}
@Override
- public void getWindowFrame(IBinder token, Rect outFrame) {
+ public boolean isRotationFrozen() throws RemoteException {
// TODO Auto-generated method stub
+ return false;
}
@Override
- public void setMagnificationCallbacks(IMagnificationCallbacks callbacks) {
+ public void enableScreenIfNeeded() throws RemoteException {
// TODO Auto-generated method stub
}
@Override
- public void setMagnificationSpec(MagnificationSpec spec) {
+ public boolean clearWindowContentFrameStats(IBinder token) throws RemoteException {
// TODO Auto-generated method stub
+ return false;
}
@Override
- public MagnificationSpec getCompatibleMagnificationSpecForWindow(IBinder windowToken) {
+ public WindowContentFrameStats getWindowContentFrameStats(IBinder token)
+ throws RemoteException {
// TODO Auto-generated method stub
return null;
}
-
- @Override
- public boolean isRotationFrozen() throws RemoteException {
- // TODO Auto-generated method stub
- return false;
- }
-
- @Override
- public void setTouchExplorationEnabled(boolean enabled) {
- }
}
diff --git a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
index 3db3a1b..7a73fae 100644
--- a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
@@ -48,9 +48,9 @@ public class LayoutInflater_Delegate {
* This implementation just records the merge status before calling the default implementation.
*/
@LayoutlibDelegate
- /*package*/ static void rInflate(LayoutInflater thisInflater,
- XmlPullParser parser, View parent, final AttributeSet attrs,
- boolean finishInflate) throws XmlPullParserException, IOException {
+ /* package */ static void rInflate(LayoutInflater thisInflater, XmlPullParser parser,
+ View parent, final AttributeSet attrs, boolean finishInflate, boolean inheritContext)
+ throws XmlPullParserException, IOException {
if (finishInflate == false) {
// this is a merge rInflate!
@@ -61,7 +61,7 @@ public class LayoutInflater_Delegate {
// ---- START DEFAULT IMPLEMENTATION.
- thisInflater.rInflate_Original(parser, parent, attrs, finishInflate);
+ thisInflater.rInflate_Original(parser, parent, attrs, finishInflate, inheritContext);
// ---- END DEFAULT IMPLEMENTATION.
@@ -74,10 +74,8 @@ public class LayoutInflater_Delegate {
}
@LayoutlibDelegate
- public static void parseInclude(
- LayoutInflater thisInflater,
- XmlPullParser parser, View parent, AttributeSet attrs)
- throws XmlPullParserException, IOException {
+ public static void parseInclude(LayoutInflater thisInflater, XmlPullParser parser, View parent,
+ AttributeSet attrs, boolean inheritContext) throws XmlPullParserException, IOException {
int type;
@@ -113,9 +111,11 @@ public class LayoutInflater_Delegate {
if (TAG_MERGE.equals(childName)) {
// Inflate all children.
- thisInflater.rInflate(childParser, parent, childAttrs, false);
+ thisInflater.rInflate(childParser, parent, childAttrs, false,
+ inheritContext);
} else {
- final View view = thisInflater.createViewFromTag(parent, childName, childAttrs);
+ final View view = thisInflater.createViewFromTag(parent, childName,
+ childAttrs, inheritContext);
final ViewGroup group = (ViewGroup) parent;
// We try to load the layout params set in the <include /> tag. If
@@ -151,7 +151,7 @@ public class LayoutInflater_Delegate {
}
// Inflate all children.
- thisInflater.rInflate(childParser, view, childAttrs, true);
+ thisInflater.rInflate(childParser, view, childAttrs, true, true);
// Attempt to override the included layout's android:id with the
// one set on the <include /> tag itself.
diff --git a/tools/layoutlib/bridge/src/android/view/MenuInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/MenuInflater_Delegate.java
new file mode 100644
index 0000000..dafc96b
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/view/MenuInflater_Delegate.java
@@ -0,0 +1,74 @@
+/*
+ * 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.content.Context;
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.ViewInfo;
+import com.android.internal.view.menu.BridgeMenuItemImpl;
+import com.android.internal.view.menu.MenuView;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.util.AttributeSet;
+
+/**
+ * Delegate used to provide new implementation of a select few methods of {@link MenuInflater}
+ * <p/>
+ * Through the layoutlib_create tool, the original methods of MenuInflater have been
+ * replaced by calls to methods of the same name in this delegate class.
+ * <p/>
+ * The main purpose of the class is to get the view key from the menu xml parser and add it to
+ * the menu item. The view key is used by the IDE to match the individual view elements to the
+ * corresponding xml tag in the menu/layout file.
+ * <p/>
+ * For Menus, the views may be reused and the {@link MenuItem} is a better object to hold the
+ * view key than the {@link MenuView.ItemView}. At the time of computation of the rest of {@link
+ * ViewInfo}, we check the corresponding view key in the menu item for the view and add it
+ */
+public class MenuInflater_Delegate {
+
+ @LayoutlibDelegate
+ /*package*/ static void registerMenu(MenuInflater thisInflater, MenuItem menuItem,
+ AttributeSet attrs) {
+ if (menuItem instanceof BridgeMenuItemImpl) {
+ Context context = thisInflater.getContext();
+ while (context instanceof ContextThemeWrapper) {
+ context = ((ContextThemeWrapper) context).getBaseContext();
+ }
+ if (context instanceof BridgeContext) {
+ Object viewKey = BridgeInflater.getViewKeyFromParser(
+ attrs, ((BridgeContext) context), null, false);
+ ((BridgeMenuItemImpl) menuItem).setViewCookie(viewKey);
+ return;
+ }
+ }
+ // This means that Bridge did not take over the instantiation of some object properly.
+ // This is most likely a bug in the LayoutLib code.
+ Bridge.getLog().warning(LayoutLog.TAG_BROKEN,
+ "Action Bar Menu rendering may be incorrect.", null);
+
+ }
+
+ @LayoutlibDelegate
+ /*package*/ static void registerMenu(MenuInflater thisInflater, SubMenu subMenu,
+ AttributeSet parser) {
+ registerMenu(thisInflater, subMenu.getItem(), parser);
+ }
+
+}
diff --git a/tools/layoutlib/bridge/src/android/view/SurfaceView.java b/tools/layoutlib/bridge/src/android/view/SurfaceView.java
index 6aa4b3b..1e7dfbe 100644
--- a/tools/layoutlib/bridge/src/android/view/SurfaceView.java
+++ b/tools/layoutlib/bridge/src/android/view/SurfaceView.java
@@ -45,6 +45,10 @@ public class SurfaceView extends MockView {
super(context, attrs, defStyle);
}
+ public SurfaceView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
public SurfaceHolder getHolder() {
return mSurfaceHolder;
}
diff --git a/tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java b/tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java
index 1fd7836..d5170aa 100644
--- a/tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java
+++ b/tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java
@@ -16,6 +16,8 @@
package android.view.accessibility;
+import com.android.annotations.NonNull;
+
import android.accessibilityservice.AccessibilityServiceInfo;
import android.content.Context;
import android.content.pm.ServiceInfo;
@@ -31,14 +33,16 @@ import java.util.List;
* for example an {@link android.app.Activity} starts, the focus or selection of a
* {@link android.view.View} changes etc. Parties interested in handling accessibility
* events implement and register an accessibility service which extends
- * {@link android.accessibilityservice.AccessibilityService}.
+ * {@code android.accessibilityservice.AccessibilityService}.
*
* @see AccessibilityEvent
- * @see android.accessibilityservice.AccessibilityService
* @see android.content.Context#getSystemService
*/
+@SuppressWarnings("UnusedDeclaration")
public final class AccessibilityManager {
- private static AccessibilityManager sInstance = new AccessibilityManager();
+
+ private static AccessibilityManager sInstance = new AccessibilityManager(null, null, 0);
+
/**
* Listener for the accessibility state.
@@ -54,9 +58,46 @@ public final class AccessibilityManager {
}
/**
+ * Listener for the system touch exploration state. To listen for changes to
+ * the touch exploration state on the device, implement this interface and
+ * register it with the system by calling
+ * {@link #addTouchExplorationStateChangeListener}.
+ */
+ public interface TouchExplorationStateChangeListener {
+
+ /**
+ * Called when the touch exploration enabled state changes.
+ *
+ * @param enabled Whether touch exploration is enabled.
+ */
+ public void onTouchExplorationStateChanged(boolean enabled);
+ }
+
+ /**
+ * Listener for the system high text contrast state. To listen for changes to
+ * the high text contrast state on the device, implement this interface and
+ * register it with the system by calling
+ * {@link #addHighTextContrastStateChangeListener}.
+ */
+ public interface HighTextContrastChangeListener {
+
+ /**
+ * Called when the high text contrast enabled state changes.
+ *
+ * @param enabled Whether high text contrast is enabled.
+ */
+ public void onHighTextContrastStateChanged(boolean enabled);
+ }
+
+ private final IAccessibilityManagerClient.Stub mClient =
+ new IAccessibilityManagerClient.Stub() {
+ public void setState(int state) {
+ }
+ };
+
+ /**
* Get an AccessibilityManager instance (create one if necessary).
*
- * @hide
*/
public static AccessibilityManager getInstance(Context context) {
return sInstance;
@@ -67,7 +108,11 @@ public final class AccessibilityManager {
*
* @param context A {@link Context}.
*/
- private AccessibilityManager() {
+ public AccessibilityManager(Context context, IAccessibilityManager service, int userId) {
+ }
+
+ public IAccessibilityManagerClient getClient() {
+ return mClient;
}
/**
@@ -80,13 +125,28 @@ public final class AccessibilityManager {
}
/**
- * Sends an {@link AccessibilityEvent}. If this {@link AccessibilityManager} is not
- * enabled the call is a NOOP.
+ * Returns if the touch exploration in the system is enabled.
*
- * @param event The {@link AccessibilityEvent}.
+ * @return True if touch exploration is enabled, false otherwise.
+ */
+ public boolean isTouchExplorationEnabled() {
+ return true;
+ }
+
+ /**
+ * Returns if the high text contrast in the system is enabled.
+ * <p>
+ * <strong>Note:</strong> You need to query this only if you application is
+ * doing its own rendering and does not rely on the platform rendering pipeline.
+ * </p>
*
- * @throws IllegalStateException if a client tries to send an {@link AccessibilityEvent}
- * while accessibility is not enabled.
+ */
+ public boolean isHighTextContrastEnabled() {
+ return false;
+ }
+
+ /**
+ * Sends an {@link AccessibilityEvent}.
*/
public void sendAccessibilityEvent(AccessibilityEvent event) {
}
@@ -102,20 +162,40 @@ public final class AccessibilityManager {
*
* @return An unmodifiable list with {@link ServiceInfo}s.
*/
+ @Deprecated
public List<ServiceInfo> getAccessibilityServiceList() {
- // normal implementation does this in some case, so let's do the same
- // (unmodifiableList wrapped around null).
- List<ServiceInfo> services = null;
- return Collections.unmodifiableList(services);
+ return Collections.emptyList();
}
public List<AccessibilityServiceInfo> getInstalledAccessibilityServiceList() {
- // normal implementation does this in some case, so let's do the same
- // (unmodifiableList wrapped around null).
- List<AccessibilityServiceInfo> services = null;
- return Collections.unmodifiableList(services);
+ return Collections.emptyList();
+ }
+
+ /**
+ * Returns the {@link AccessibilityServiceInfo}s of the enabled accessibility services
+ * for a given feedback type.
+ *
+ * @param feedbackTypeFlags The feedback type flags.
+ * @return An unmodifiable list with {@link AccessibilityServiceInfo}s.
+ *
+ * @see AccessibilityServiceInfo#FEEDBACK_AUDIBLE
+ * @see AccessibilityServiceInfo#FEEDBACK_GENERIC
+ * @see AccessibilityServiceInfo#FEEDBACK_HAPTIC
+ * @see AccessibilityServiceInfo#FEEDBACK_SPOKEN
+ * @see AccessibilityServiceInfo#FEEDBACK_VISUAL
+ */
+ public List<AccessibilityServiceInfo> getEnabledAccessibilityServiceList(
+ int feedbackTypeFlags) {
+ return Collections.emptyList();
}
+ /**
+ * Registers an {@link AccessibilityStateChangeListener} for changes in
+ * the global accessibility state of the system.
+ *
+ * @param listener The listener.
+ * @return True if successfully registered.
+ */
public boolean addAccessibilityStateChangeListener(
AccessibilityStateChangeListener listener) {
return true;
@@ -126,6 +206,62 @@ public final class AccessibilityManager {
return true;
}
+ /**
+ * Registers a {@link TouchExplorationStateChangeListener} for changes in
+ * the global touch exploration state of the system.
+ *
+ * @param listener The listener.
+ * @return True if successfully registered.
+ */
+ public boolean addTouchExplorationStateChangeListener(
+ @NonNull TouchExplorationStateChangeListener listener) {
+ return true;
+ }
+
+ /**
+ * Unregisters a {@link TouchExplorationStateChangeListener}.
+ *
+ * @param listener The listener.
+ * @return True if successfully unregistered.
+ */
+ public boolean removeTouchExplorationStateChangeListener(
+ @NonNull TouchExplorationStateChangeListener listener) {
+ return true;
+ }
+
+ /**
+ * Registers a {@link HighTextContrastChangeListener} for changes in
+ * the global high text contrast state of the system.
+ *
+ * @param listener The listener.
+ * @return True if successfully registered.
+ *
+ */
+ public boolean addHighTextContrastStateChangeListener(
+ @NonNull HighTextContrastChangeListener listener) {
+ return true;
+ }
+
+ /**
+ * Unregisters a {@link HighTextContrastChangeListener}.
+ *
+ * @param listener The listener.
+ * @return True if successfully unregistered.
+ *
+ */
+ public boolean removeHighTextContrastStateChangeListener(
+ @NonNull HighTextContrastChangeListener listener) {
+ return true;
+ }
+
+ /**
+ * Sets the current state and notifies listeners, if necessary.
+ *
+ * @param stateFlags The state flags.
+ */
+ private void setStateLocked(int stateFlags) {
+ }
+
public int addAccessibilityInteractionConnection(IWindow windowToken,
IAccessibilityInteractionConnection connection) {
return View.NO_ID;
diff --git a/tools/layoutlib/bridge/src/com/android/internal/view/menu/BridgeMenuItemImpl.java b/tools/layoutlib/bridge/src/com/android/internal/view/menu/BridgeMenuItemImpl.java
new file mode 100644
index 0000000..8d1d0c1
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/internal/view/menu/BridgeMenuItemImpl.java
@@ -0,0 +1,68 @@
+/*
+ * 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.internal.view.menu;
+
+import com.android.layoutlib.bridge.android.BridgeContext;
+
+import android.content.Context;
+import android.view.ContextThemeWrapper;
+import android.view.View;
+
+/**
+ * An extension of the {@link MenuItemImpl} to store the view cookie also.
+ */
+public class BridgeMenuItemImpl extends MenuItemImpl {
+
+ /**
+ * An object returned by the IDE that helps mapping each View to the corresponding XML tag in
+ * the layout. For Menus, we store this cookie here and attach it to the corresponding view
+ * at the time of rendering.
+ */
+ private Object viewCookie;
+ private BridgeContext mContext;
+
+ /**
+ * Instantiates this menu item.
+ */
+ BridgeMenuItemImpl(MenuBuilder menu, int group, int id, int categoryOrder, int ordering,
+ CharSequence title, int showAsAction) {
+ super(menu, group, id, categoryOrder, ordering, title, showAsAction);
+ Context context = menu.getContext();
+ while (context instanceof ContextThemeWrapper) {
+ context = ((ContextThemeWrapper) context).getBaseContext();
+ }
+ if (context instanceof BridgeContext) {
+ mContext = ((BridgeContext) context);
+ }
+ }
+
+ public Object getViewCookie() {
+ return viewCookie;
+ }
+
+ public void setViewCookie(Object viewCookie) {
+ // If the menu item has an associated action provider view,
+ // directly set the cookie in the view to cookie map stored in BridgeContext.
+ View actionView = getActionView();
+ if (actionView != null && mContext != null) {
+ mContext.addViewKey(actionView, viewCookie);
+ // We don't need to add the view cookie to the this item now. But there's no harm in
+ // storing it, in case we need it in the future.
+ }
+ this.viewCookie = viewCookie;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/internal/view/menu/MenuBuilder_Delegate.java b/tools/layoutlib/bridge/src/com/android/internal/view/menu/MenuBuilder_Delegate.java
new file mode 100644
index 0000000..505fb81
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/internal/view/menu/MenuBuilder_Delegate.java
@@ -0,0 +1,38 @@
+/*
+ * 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.internal.view.menu;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate used to provide new implementation of a select few methods of {@link MenuBuilder}
+ * <p/>
+ * Through the layoutlib_create tool, the original methods of {@code MenuBuilder} have been
+ * replaced by calls to methods of the same name in this delegate class.
+ */
+public class MenuBuilder_Delegate {
+ /**
+ * The method overrides the instantiation of the {@link MenuItemImpl} with an instance of
+ * {@link BridgeMenuItemImpl} so that view cookies may be stored.
+ */
+ @LayoutlibDelegate
+ /*package*/ static MenuItemImpl createNewMenuItem(MenuBuilder thisMenu, int group, int id,
+ int categoryOrder, int ordering, CharSequence title, int defaultShowAsAction) {
+ return new BridgeMenuItemImpl(thisMenu, group, id, categoryOrder, ordering, title,
+ defaultShowAsAction);
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/internal/widget/ActionBarAccessor.java b/tools/layoutlib/bridge/src/com/android/internal/widget/ActionBarAccessor.java
new file mode 100644
index 0000000..40b6220
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/internal/widget/ActionBarAccessor.java
@@ -0,0 +1,32 @@
+/*
+ * 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.internal.widget;
+
+import android.widget.ActionMenuPresenter;
+
+/**
+ * To access non public members of AbsActionBarView
+ */
+public class ActionBarAccessor {
+
+ /**
+ * Returns the {@link ActionMenuPresenter} associated with the {@link AbsActionBarView}
+ */
+ public static ActionMenuPresenter getActionMenuPresenter(AbsActionBarView view) {
+ return view.mActionMenuPresenter;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
index ab4be71..3d0e1e8 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -26,7 +26,6 @@ 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.ide.common.rendering.api.SessionParams;
-import com.android.layoutlib.bridge.impl.FontLoader;
import com.android.layoutlib.bridge.impl.RenderDrawable;
import com.android.layoutlib.bridge.impl.RenderSessionImpl;
import com.android.layoutlib.bridge.util.DynamicIdMap;
@@ -61,7 +60,7 @@ import java.util.concurrent.locks.ReentrantLock;
/**
* Main entry point of the LayoutLib Bridge.
* <p/>To use this bridge, simply instantiate an object of type {@link Bridge} and call
- * {@link #createScene(SceneParams)}
+ * {@link #createSession(SessionParams)}
*/
public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
@@ -147,8 +146,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
if (getClass() != obj.getClass()) return false;
IntArray other = (IntArray) obj;
- if (!Arrays.equals(mArray, other.mArray)) return false;
- return true;
+ return Arrays.equals(mArray, other.mArray);
}
}
@@ -215,7 +213,9 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
Capability.ADAPTER_BINDING,
Capability.EXTENDED_VIEWINFO,
Capability.FIXED_SCALABLE_NINE_PATCH,
- Capability.RTL);
+ Capability.RTL,
+ Capability.ACTION_BAR,
+ Capability.SIMULATE_PLATFORM);
BridgeAssetManager.initSystem();
@@ -250,14 +250,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
}
// load the fonts.
- FontLoader fontLoader = FontLoader.create(fontLocation.getAbsolutePath());
- if (fontLoader != null) {
- Typeface_Delegate.init(fontLoader);
- } else {
- log.error(LayoutLog.TAG_BROKEN,
- "Failed create FontLoader in layout lib.", null);
- return false;
- }
+ Typeface_Delegate.setFontLocation(fontLocation.getAbsolutePath());
// 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.
@@ -297,7 +290,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
if (log != null) {
log.error(LayoutLog.TAG_BROKEN,
"Failed to load com.android.internal.R from the layout library jar",
- throwable);
+ throwable, null);
}
return false;
}
@@ -425,8 +418,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
locale = "";
}
ULocale uLocale = new ULocale(locale);
- return uLocale.getCharacterOrientation().equals(ICU_LOCALE_DIRECTION_RTL) ?
- true : false;
+ return uLocale.getCharacterOrientation().equals(ICU_LOCALE_DIRECTION_RTL);
}
/**
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
index f9f4b3a..e0f87fd 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeRenderSession.java
@@ -64,6 +64,11 @@ public class BridgeRenderSession extends RenderSession {
}
@Override
+ public List<ViewInfo> getSystemRootViews() {
+ return mSession.getSystemViewInfos();
+ }
+
+ @Override
public Map<String, String> getDefaultProperties(Object viewObject) {
return mSession.getDefaultProperties(viewObject);
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
index 3d50b2a..4a9f718 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java
@@ -31,7 +31,11 @@ import android.widget.TextView;
public class MockView extends TextView {
public MockView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
+ this(context, attrs, defStyle, 0);
+ }
+
+ public MockView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
setText(this.getClass().getSimpleName());
setTextColor(0xFF000000);
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java
new file mode 100644
index 0000000..ea5f1ea
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/AndroidLocale.java
@@ -0,0 +1,59 @@
+/*
+ * 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.android;
+
+import java.util.Locale;
+
+import com.ibm.icu.util.ULocale;
+
+/**
+ * This class provides an alternate implementation for {@code java.util.Locale#toLanguageTag}
+ * which is only available after Java 6.
+ *
+ * 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.
+ */
+@SuppressWarnings("UnusedDeclaration")
+public class AndroidLocale {
+
+ public static String toLanguageTag(Locale locale) {
+ return ULocale.forLocale(locale).toLanguageTag();
+ }
+
+ 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 forLanguageTag(String tag) {
+ return ULocale.forLanguageTag(tag).toLocale();
+ }
+
+ public static String getScript(Locale locale) {
+ return ULocale.forLocale(locale).getScript();
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
index 01740b1..89288bf 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java
@@ -135,6 +135,7 @@ public final class BridgeContentProvider implements IContentProvider {
return null;
}
+
@Override
public Uri canonicalize(String callingPkg, Uri uri) throws RemoteException {
return null;
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index b9294ab..04a52ea 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -16,6 +16,7 @@
package com.android.layoutlib.bridge.android;
+import com.android.annotations.Nullable;
import com.android.ide.common.rendering.api.ILayoutPullParser;
import com.android.ide.common.rendering.api.IProjectCallback;
import com.android.ide.common.rendering.api.LayoutLog;
@@ -57,6 +58,7 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
+import android.hardware.display.DisplayManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -100,6 +102,7 @@ public final class BridgeContext extends Context {
private final ApplicationInfo mApplicationInfo;
private final IProjectCallback mProjectCallback;
private final WindowManager mWindowManager;
+ private final DisplayManager mDisplayManager;
private Resources.Theme mTheme;
@@ -109,7 +112,7 @@ public final class BridgeContext extends Context {
// maps for dynamically generated id representing style objects (StyleResourceValue)
private Map<Integer, StyleResourceValue> mDynamicIdToStyleMap;
private Map<StyleResourceValue, Integer> mStyleToDynamicIdMap;
- private int mDynamicIdGenerator = 0x01030000; // Base id for framework R.style
+ private int mDynamicIdGenerator = 0x02030000; // Base id for R.style in custom namespace
// cache for TypedArray generated from IStyleResourceValue object
private Map<int[], Map<Integer, TypedArray>> mTypedArrayCache;
@@ -148,6 +151,7 @@ public final class BridgeContext extends Context {
}
mWindowManager = new WindowManagerImpl(mMetrics);
+ mDisplayManager = new DisplayManager(this);
}
/**
@@ -315,6 +319,11 @@ public final class BridgeContext extends Context {
}
}
+ // The base value for R.style is 0x01030000 and the custom style is 0x02030000.
+ // So, if the second byte is 03, it's probably a style.
+ if ((id >> 16 & 0xFF) == 0x03) {
+ return getStyleByDynamicId(id);
+ }
return null;
}
@@ -449,13 +458,20 @@ public final class BridgeContext extends Context {
return new PowerManager(this, new BridgePowerManager(), new Handler());
}
+ if (DISPLAY_SERVICE.equals(service)) {
+ return mDisplayManager;
+ }
+
throw new UnsupportedOperationException("Unsupported Service: " + service);
}
@Override
public final TypedArray obtainStyledAttributes(int[] attrs) {
- return createStyleBasedTypedArray(mRenderResources.getCurrentTheme(), attrs);
+ // No style is specified here, so create the typed array based on the default theme
+ // and the styles already applied to it. A null value of style indicates that the default
+ // theme should be used.
+ return createStyleBasedTypedArray(null, attrs);
}
@Override
@@ -551,7 +567,7 @@ public final class BridgeContext extends Context {
StyleResourceValue customStyleValues = null;
if (customStyle != null) {
ResourceValue item = mRenderResources.findResValue(customStyle,
- false /*forceFrameworkOnly*/);
+ isPlatformFile /*forceFrameworkOnly*/);
// resolve it in case it links to something else
item = mRenderResources.resolveResValue(item);
@@ -582,8 +598,7 @@ public final class BridgeContext extends Context {
if (item != null) {
// item is a reference to a style entry. Search for it.
- item = mRenderResources.findResValue(item.getValue(),
- false /*forceFrameworkOnly*/);
+ item = mRenderResources.findResValue(item.getValue(), item.isFramework());
if (item instanceof StyleResourceValue) {
defStyleValues = (StyleResourceValue)item;
@@ -604,38 +619,36 @@ public final class BridgeContext extends Context {
}
if (value != null) {
- if (value.getFirst() == ResourceType.STYLE) {
- // look for the style in the current theme, and its parent:
- ResourceValue item = mRenderResources.findItemInTheme(value.getSecond(),
+ if ((value.getFirst() == ResourceType.STYLE)) {
+ // look for the style in all resources:
+ StyleResourceValue item = mRenderResources.getStyle(value.getSecond(),
isFrameworkRes);
if (item != null) {
- if (item instanceof StyleResourceValue) {
- if (defaultPropMap != null) {
- defaultPropMap.put("style", item.getName());
- }
-
- defStyleValues = (StyleResourceValue)item;
+ if (defaultPropMap != null) {
+ defaultPropMap.put("style", item.getName());
}
+
+ defStyleValues = item;
} else {
Bridge.getLog().error(null,
String.format(
"Style with id 0x%x (resolved to '%s') does not exist.",
defStyleRes, value.getSecond()),
- null /*data*/);
+ null);
}
} else {
Bridge.getLog().error(null,
String.format(
- "Resouce id 0x%x is not of type STYLE (instead %s)",
+ "Resource id 0x%x is not of type STYLE (instead %s)",
defStyleRes, value.getFirst().toString()),
- null /*data*/);
+ null);
}
} else {
Bridge.getLog().error(null,
String.format(
"Failed to find style with id 0x%x in current theme",
defStyleRes),
- null /*data*/);
+ null);
}
}
@@ -723,11 +736,13 @@ public final class BridgeContext extends Context {
/**
* Creates a {@link BridgeTypedArray} by filling the values defined by the int[] with the
- * values found in the given style.
+ * values found in the given style. If no style is specified, the default theme, along with the
+ * styles applied to it are used.
+ *
* @see #obtainStyledAttributes(int, int[])
*/
- private BridgeTypedArray createStyleBasedTypedArray(StyleResourceValue style, int[] attrs)
- throws Resources.NotFoundException {
+ private BridgeTypedArray createStyleBasedTypedArray(@Nullable StyleResourceValue style,
+ int[] attrs) throws Resources.NotFoundException {
List<Pair<String, Boolean>> attributes = searchAttrs(attrs);
@@ -740,8 +755,14 @@ public final class BridgeContext extends Context {
if (attribute != null) {
// look for the value in the given style
- ResourceValue resValue = mRenderResources.findItemInStyle(style,
- attribute.getFirst(), attribute.getSecond());
+ ResourceValue resValue;
+ if (style != null) {
+ resValue = mRenderResources.findItemInStyle(style, attribute.getFirst(),
+ attribute.getSecond());
+ } else {
+ resValue = mRenderResources.findItemInTheme(attribute.getFirst(),
+ attribute.getSecond());
+ }
if (resValue != null) {
// resolve it to make sure there are no references left.
@@ -756,7 +777,6 @@ public final class BridgeContext extends Context {
return ta;
}
-
/**
* The input int[] attrs is a list of attributes. The returns a list of information about
* each attributes. The information is (name, isFramework)
@@ -947,6 +967,12 @@ public final class BridgeContext extends Context {
}
@Override
+ public Context createApplicationContext(ApplicationInfo application, int flags)
+ throws PackageManager.NameNotFoundException {
+ return null;
+ }
+
+ @Override
public boolean deleteDatabase(String arg0) {
// pass
return false;
@@ -1022,6 +1048,12 @@ public final class BridgeContext extends Context {
}
@Override
+ public File getCodeCacheDir() {
+ // pass
+ return null;
+ }
+
+ @Override
public File getExternalCacheDir() {
// pass
return null;
@@ -1060,6 +1092,12 @@ public final class BridgeContext extends Context {
}
@Override
+ public File getNoBackupFilesDir() {
+ // pass
+ return null;
+ }
+
+ @Override
public File getExternalFilesDir(String type) {
// pass
return null;
@@ -1260,6 +1298,14 @@ public final class BridgeContext extends Context {
}
@Override
+ public void sendOrderedBroadcastAsUser(Intent intent, UserHandle user,
+ String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
+ Handler scheduler,
+ int initialCode, String initialData, Bundle initialExtras) {
+ // pass
+ }
+
+ @Override
public void sendStickyBroadcast(Intent arg0) {
// pass
@@ -1434,4 +1480,10 @@ public final class BridgeContext extends Context {
// pass
return new File[0];
}
+
+ @Override
+ public File[] getExternalMediaDirs() {
+ // pass
+ return new File[0];
+ }
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
index 3cf5ed5..c44a57c 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeIInputMethodManager.java
@@ -135,7 +135,6 @@ public class BridgeIInputMethodManager implements IInputMethodManager {
@Override
public void setImeWindowStatus(IBinder arg0, int arg1, int arg2) throws RemoteException {
// TODO Auto-generated method stub
-
}
@Override
@@ -197,18 +196,29 @@ public class BridgeIInputMethodManager implements IInputMethodManager {
}
@Override
- public boolean switchToNextInputMethod(IBinder arg0, boolean arg1) throws RemoteException {
+ public boolean switchToNextInputMethod(IBinder arg0, boolean arg1) throws RemoteException {
// TODO Auto-generated method stub
return false;
}
@Override
- public boolean shouldOfferSwitchingToNextInputMethod(IBinder arg0) throws RemoteException {
+ public boolean shouldOfferSwitchingToNextInputMethod(IBinder arg0) throws RemoteException {
// TODO Auto-generated method stub
return false;
}
@Override
+ public int getInputMethodWindowVisibleHeight() throws RemoteException {
+ // TODO Auto-generated method stub
+ return 0;
+ }
+
+ @Override
+ public void notifyUserAction(int sequenceNumber) throws RemoteException {
+ // TODO Auto-generated method stub
+ }
+
+ @Override
public void updateStatusIcon(IBinder arg0, String arg1, int arg2) throws RemoteException {
// TODO Auto-generated method stub
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
index 281337c..22265a3 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
@@ -28,18 +28,28 @@ import android.os.WorkSource;
public class BridgePowerManager implements IPowerManager {
@Override
- public boolean isScreenOn() throws RemoteException {
+ public boolean isInteractive() throws RemoteException {
return true;
}
@Override
+ public boolean isPowerSaveMode() throws RemoteException {
+ return false;
+ }
+
+ @Override
+ public boolean setPowerSaveMode(boolean mode) throws RemoteException {
+ return false;
+ }
+
+ @Override
public IBinder asBinder() {
// pass for now.
return null;
}
@Override
- public void acquireWakeLock(IBinder arg0, int arg1, String arg2, String arg2_5, WorkSource arg3)
+ public void acquireWakeLock(IBinder arg0, int arg1, String arg2, String arg2_5, WorkSource arg3, String arg4)
throws RemoteException {
// pass for now.
}
@@ -51,12 +61,17 @@ public class BridgePowerManager implements IPowerManager {
}
@Override
+ public void powerHint(int hintId, int data) {
+ // pass for now.
+ }
+
+ @Override
public void crash(String arg0) throws RemoteException {
// pass for now.
}
@Override
- public void goToSleep(long arg0, int arg1) throws RemoteException {
+ public void goToSleep(long arg0, int arg1, int arg2) throws RemoteException {
// pass for now.
}
@@ -111,7 +126,7 @@ public class BridgePowerManager implements IPowerManager {
}
@Override
- public void updateWakeLockWorkSource(IBinder arg0, WorkSource arg1) throws RemoteException {
+ public void updateWakeLockWorkSource(IBinder arg0, WorkSource arg1, String arg2) throws RemoteException {
// pass for now.
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
index df576d2..997b199 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java
@@ -47,8 +47,8 @@ public final class BridgeWindow implements IWindow {
}
@Override
- public void resized(Rect arg1, Rect arg1p5, Rect arg2, Rect arg3,
- boolean arg4, Configuration arg5) throws RemoteException {
+ public void resized(Rect rect, Rect rect2, Rect rect3, Rect rect4, Rect rect5, boolean b,
+ Configuration configuration) throws RemoteException {
// pass for now.
}
@@ -58,11 +58,6 @@ public final class BridgeWindow implements IWindow {
}
@Override
- public void dispatchScreenState(boolean on) throws RemoteException {
- // pass for now.
- }
-
- @Override
public void windowFocusChanged(boolean arg0, boolean arg1) throws RemoteException {
// pass for now.
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
index 09e6878..0ed6ab1 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java
@@ -85,10 +85,11 @@ public final class BridgeWindowSession implements IWindowSession {
// pass for now.
return false;
}
+
@Override
- public int relayout(IWindow arg0, int seq, LayoutParams arg1, int arg2, int arg3, int arg4,
- int arg4_5, Rect arg5Z, Rect arg5, Rect arg6, Rect arg7, Configuration arg7b,
- Surface arg8) throws RemoteException {
+ public int relayout(IWindow iWindow, int i, LayoutParams layoutParams, int i2,
+ int i3, int i4, int i5, Rect rect, Rect rect2, Rect rect3, Rect rect4, Rect rect5,
+ Configuration configuration, Surface surface) throws RemoteException {
// pass for now.
return 0;
}
@@ -173,6 +174,11 @@ public final class BridgeWindowSession implements IWindowSession {
}
@Override
+ public void setWallpaperDisplayOffset(IBinder windowToken, int x, int y) {
+ // pass for now.
+ }
+
+ @Override
public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y,
int z, Bundle extras, boolean sync) {
// pass for now.
@@ -197,7 +203,7 @@ public final class BridgeWindowSession implements IWindowSession {
}
@Override
- public void onRectangleOnScreenRequested(IBinder window, Rect rectangle, boolean immediate) {
+ public void onRectangleOnScreenRequested(IBinder window, Rect rectangle) {
// pass for now.
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
new file mode 100644
index 0000000..d95c815
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/ActionBarLayout.java
@@ -0,0 +1,355 @@
+/*
+ * 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.bars;
+
+import com.android.annotations.NonNull;
+import com.android.annotations.Nullable;
+import com.android.ide.common.rendering.api.ActionBarCallback;
+import com.android.ide.common.rendering.api.ActionBarCallback.HomeButtonStyle;
+import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.SessionParams;
+import com.android.internal.R;
+import com.android.internal.app.WindowDecorActionBar;
+import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.view.menu.MenuItemImpl;
+import com.android.internal.widget.ActionBarAccessor;
+import com.android.internal.widget.ActionBarContainer;
+import com.android.internal.widget.ActionBarView;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.impl.ResourceHelper;
+import com.android.resources.ResourceType;
+
+import android.app.ActionBar;
+import android.app.ActionBar.Tab;
+import android.app.ActionBar.TabListener;
+import android.app.FragmentTransaction;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.MenuInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.FrameLayout;
+import android.widget.LinearLayout;
+import android.widget.ListAdapter;
+import android.widget.ListView;
+import android.widget.RelativeLayout;
+
+import java.util.ArrayList;
+
+/**
+ * A layout representing the action bar.
+ */
+public class ActionBarLayout extends LinearLayout {
+
+ // Store another reference to the context so that we don't have to cast it repeatedly.
+ @NonNull private final BridgeContext mBridgeContext;
+ @NonNull private final Context mThemedContext;
+
+ @NonNull private final ActionBar mActionBar;
+
+ // Data for Action Bar.
+ @Nullable private final String mIcon;
+ @Nullable private final String mTitle;
+ @Nullable private final String mSubTitle;
+ private final boolean mSplit;
+ private final boolean mShowHomeAsUp;
+ private final int mNavMode;
+
+ // Helper fields.
+ @NonNull private final MenuBuilder mMenuBuilder;
+ private final int mPopupMaxWidth;
+ @NonNull private final RenderResources res;
+ @Nullable private final ActionBarView mActionBarView;
+ @Nullable private FrameLayout mContentRoot;
+ @NonNull private final ActionBarCallback mCallback;
+
+ // A fake parent for measuring views.
+ @Nullable private ViewGroup mMeasureParent;
+
+ public ActionBarLayout(@NonNull BridgeContext context, @NonNull SessionParams params) {
+
+ super(context);
+ setOrientation(LinearLayout.HORIZONTAL);
+ setGravity(Gravity.CENTER_VERTICAL);
+
+ // Inflate action bar layout.
+ LayoutInflater.from(context).inflate(R.layout.screen_action_bar, this,
+ true /*attachToRoot*/);
+ mActionBar = new WindowDecorActionBar(this);
+
+ // Set contexts.
+ mBridgeContext = context;
+ mThemedContext = mActionBar.getThemedContext();
+
+ // Set data for action bar.
+ mCallback = params.getProjectCallback().getActionBarCallback();
+ mIcon = params.getAppIcon();
+ mTitle = params.getAppLabel();
+ // Split Action Bar when the screen size is narrow and the application requests split action
+ // bar when narrow.
+ mSplit = context.getResources().getBoolean(R.bool.split_action_bar_is_narrow) &&
+ mCallback.getSplitActionBarWhenNarrow();
+ mNavMode = mCallback.getNavigationMode();
+ // TODO: Support Navigation Drawer Indicator.
+ mShowHomeAsUp = mCallback.getHomeButtonStyle() == HomeButtonStyle.SHOW_HOME_AS_UP;
+ mSubTitle = mCallback.getSubTitle();
+
+
+ // Set helper fields.
+ mMenuBuilder = new MenuBuilder(mThemedContext);
+ res = mBridgeContext.getRenderResources();
+ mPopupMaxWidth = Math.max(mBridgeContext.getMetrics().widthPixels / 2,
+ mThemedContext.getResources().getDimensionPixelSize(
+ R.dimen.config_prefDialogWidth));
+ mActionBarView = (ActionBarView) findViewById(R.id.action_bar);
+ mContentRoot = (FrameLayout) findViewById(android.R.id.content);
+
+ setupActionBar();
+ }
+
+ /**
+ * Sets up the action bar by filling the appropriate data.
+ */
+ private void setupActionBar() {
+ // Add title and sub title.
+ ResourceValue titleValue = res.findResValue(mTitle, false /*isFramework*/);
+ if (titleValue != null && titleValue.getValue() != null) {
+ mActionBar.setTitle(titleValue.getValue());
+ } else {
+ mActionBar.setTitle(mTitle);
+ }
+ if (mSubTitle != null) {
+ mActionBar.setSubtitle(mSubTitle);
+ }
+
+ // Add show home as up icon.
+ if (mShowHomeAsUp) {
+ mActionBar.setDisplayOptions(0xFF, ActionBar.DISPLAY_HOME_AS_UP);
+ }
+
+ // Set the navigation mode.
+ mActionBar.setNavigationMode(mNavMode);
+ if (mNavMode == ActionBar.NAVIGATION_MODE_TABS) {
+ setupTabs(3);
+ }
+
+ if (mActionBarView != null) {
+ // If the action bar style doesn't specify an icon, set the icon obtained from the session
+ // params.
+ if (!mActionBarView.hasIcon() && mIcon != null) {
+ Drawable iconDrawable = getDrawable(mIcon, false /*isFramework*/);
+ if (iconDrawable != null) {
+ mActionBar.setIcon(iconDrawable);
+ }
+ }
+
+ // Set action bar to be split, if needed.
+ ActionBarContainer splitView = (ActionBarContainer) findViewById(R.id.split_action_bar);
+ mActionBarView.setSplitView(splitView);
+ mActionBarView.setSplitToolbar(mSplit);
+
+ inflateMenus();
+ }
+ }
+
+ /**
+ * Gets the menus to add to the action bar from the callback, resolves them, inflates them and
+ * adds them to the action bar.
+ */
+ private void inflateMenus() {
+ if (mActionBarView == null) {
+ return;
+ }
+ final MenuInflater inflater = new MenuInflater(mThemedContext);
+ for (String name : mCallback.getMenuIdNames()) {
+ if (mBridgeContext.getRenderResources().getProjectResource(ResourceType.MENU, name)
+ != null) {
+ int id = mBridgeContext.getProjectResourceValue(ResourceType.MENU, name, -1);
+ if (id > -1) {
+ inflater.inflate(id, mMenuBuilder);
+ }
+ }
+ }
+ mActionBarView.setMenu(mMenuBuilder, null /*callback*/);
+ }
+
+ // TODO: Use an adapter, like List View to set up tabs.
+ private void setupTabs(int num) {
+ for (int i = 1; i <= num; i++) {
+ Tab tab = mActionBar.newTab().setText("Tab" + i).setTabListener(new TabListener() {
+ @Override
+ public void onTabUnselected(Tab t, FragmentTransaction ft) {
+ // pass
+ }
+ @Override
+ public void onTabSelected(Tab t, FragmentTransaction ft) {
+ // pass
+ }
+ @Override
+ public void onTabReselected(Tab t, FragmentTransaction ft) {
+ // pass
+ }
+ });
+ mActionBar.addTab(tab);
+ }
+ }
+
+ @Nullable
+ private Drawable getDrawable(@NonNull String name, boolean isFramework) {
+ ResourceValue value = res.findResValue(name, isFramework);
+ value = res.resolveResValue(value);
+ if (value != null) {
+ return ResourceHelper.getDrawable(value, mBridgeContext);
+ }
+ return null;
+ }
+
+ /**
+ * Creates a Popup and adds it to the content frame. It also adds another {@link FrameLayout} to
+ * the content frame which shall serve as the new content root.
+ */
+ public void createMenuPopup() {
+ assert mContentRoot != null && findViewById(android.R.id.content) == mContentRoot
+ : "Action Bar Menus have already been created.";
+
+ if (!isOverflowPopupNeeded()) {
+ return;
+ }
+
+ // Create a layout to hold the menus and the user's content.
+ RelativeLayout layout = new RelativeLayout(mThemedContext);
+ layout.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
+ LayoutParams.MATCH_PARENT));
+ mContentRoot.addView(layout);
+ // Create a layout for the user's content.
+ FrameLayout contentRoot = new FrameLayout(mBridgeContext);
+ contentRoot.setLayoutParams(new LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+ // Add contentRoot and menus to the layout.
+ layout.addView(contentRoot);
+ layout.addView(createMenuView());
+ // ContentRoot is now the view we just created.
+ mContentRoot = contentRoot;
+ }
+
+ /**
+ * Returns a {@link LinearLayout} containing the menu list view to be embedded in a
+ * {@link RelativeLayout}
+ */
+ @NonNull
+ private View createMenuView() {
+ DisplayMetrics metrics = mBridgeContext.getMetrics();
+ OverflowMenuAdapter adapter = new OverflowMenuAdapter(mMenuBuilder, mThemedContext);
+
+ LinearLayout layout = new LinearLayout(mThemedContext);
+ RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
+ measureContentWidth(adapter), LayoutParams.WRAP_CONTENT);
+ layoutParams.addRule(RelativeLayout.ALIGN_PARENT_END);
+ if (mSplit) {
+ layoutParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
+ // TODO: Find correct value instead of hardcoded 10dp.
+ layoutParams.bottomMargin = getPixelValue("-10dp", metrics);
+ } else {
+ layoutParams.topMargin = getPixelValue("-10dp", metrics);
+ }
+ layout.setLayoutParams(layoutParams);
+ final TypedArray a = mThemedContext.obtainStyledAttributes(null,
+ R.styleable.PopupWindow, R.attr.popupMenuStyle, 0);
+ layout.setBackground(a.getDrawable(R.styleable.PopupWindow_popupBackground));
+ layout.setDividerDrawable(a.getDrawable(R.attr.actionBarDivider));
+ a.recycle();
+ layout.setOrientation(LinearLayout.VERTICAL);
+ layout.setDividerPadding(getPixelValue("12dp", metrics));
+ layout.setShowDividers(LinearLayout.SHOW_DIVIDER_MIDDLE);
+
+ ListView listView = new ListView(mThemedContext, null, R.attr.dropDownListViewStyle);
+ listView.setAdapter(adapter);
+ layout.addView(listView);
+ return layout;
+ }
+
+ private boolean isOverflowPopupNeeded() {
+ boolean needed = mCallback.isOverflowPopupNeeded();
+ if (!needed) {
+ return false;
+ }
+ // Copied from android.widget.ActionMenuPresenter.updateMenuView()
+ ArrayList<MenuItemImpl> menus = mMenuBuilder.getNonActionItems();
+ if (ActionBarAccessor.getActionMenuPresenter(mActionBarView).isOverflowReserved() &&
+ menus != null) {
+ final int count = menus.size();
+ if (count == 1) {
+ needed = !menus.get(0).isActionViewExpanded();
+ } else {
+ needed = count > 0;
+ }
+ }
+ return needed;
+ }
+
+ @Nullable
+ public FrameLayout getContentRoot() {
+ return mContentRoot;
+ }
+
+ // Copied from com.android.internal.view.menu.MenuPopHelper.measureContentWidth()
+ private int measureContentWidth(@NonNull ListAdapter adapter) {
+ // Menus don't tend to be long, so this is more sane than it looks.
+ int maxWidth = 0;
+ View itemView = null;
+ int itemType = 0;
+
+ final int widthMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final int heightMeasureSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED);
+ final int count = adapter.getCount();
+ for (int i = 0; i < count; i++) {
+ final int positionType = adapter.getItemViewType(i);
+ if (positionType != itemType) {
+ itemType = positionType;
+ itemView = null;
+ }
+
+ if (mMeasureParent == null) {
+ mMeasureParent = new FrameLayout(mThemedContext);
+ }
+
+ itemView = adapter.getView(i, itemView, mMeasureParent);
+ itemView.measure(widthMeasureSpec, heightMeasureSpec);
+
+ final int itemWidth = itemView.getMeasuredWidth();
+ if (itemWidth >= mPopupMaxWidth) {
+ return mPopupMaxWidth;
+ } else if (itemWidth > maxWidth) {
+ maxWidth = itemWidth;
+ }
+ }
+
+ return maxWidth;
+ }
+
+ private int getPixelValue(@NonNull String value, @NonNull DisplayMetrics metrics) {
+ TypedValue typedValue = ResourceHelper.getValue(null, value, false /*requireUnit*/);
+ return (int) typedValue.getDimension(metrics);
+ }
+
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java
new file mode 100644
index 0000000..82a5130
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/Config.java
@@ -0,0 +1,117 @@
+/*
+ * 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.bars;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import static android.os.Build.VERSION_CODES.*;
+
+/**
+ * Various helper methods to simulate older versions of platform.
+ */
+public class Config {
+
+ // each of these resource dirs must end in '/'
+ private static final String GINGERBREAD_DIR = "/bars/v9/";
+ private static final String JELLYBEAN_DIR = "/bars/v18/";
+ private static final String KITKAT_DIR = "/bars/v19/";
+ private static final String DEFAULT_RESOURCE_DIR = "/bars/v21/";
+
+ private static final List<String> sDefaultResourceDir =
+ Collections.singletonList(DEFAULT_RESOURCE_DIR);
+
+ private static final int WHITE = 0xFFFFFFFF;
+ private static final int BLACK = 0xFF000000;
+
+ public static boolean showOnScreenNavBar(int platformVersion) {
+ return platformVersion == 0 || platformVersion >= ICE_CREAM_SANDWICH;
+ }
+
+ public static int getStatusBarColor(int platformVersion) {
+ // return white for froyo and earlier; black otherwise.
+ return platformVersion == 0 || platformVersion >= GINGERBREAD ? BLACK : WHITE;
+ }
+
+ public static List<String> getResourceDirs(int platformVersion) {
+ // Special case the most used scenario.
+ if (platformVersion == 0) {
+ return sDefaultResourceDir;
+ }
+ List<String> list = new ArrayList<String>(4);
+ // Gingerbread - uses custom battery and wifi icons.
+ if (platformVersion <= GINGERBREAD) {
+ list.add(GINGERBREAD_DIR);
+ }
+ // ICS - JellyBean uses custom battery, wifi.
+ if (platformVersion <= JELLY_BEAN_MR2) {
+ list.add(JELLYBEAN_DIR);
+ }
+ // KitKat - uses custom wifi and nav icons.
+ if (platformVersion <= KITKAT) {
+ list.add(KITKAT_DIR);
+ }
+ list.add(DEFAULT_RESOURCE_DIR);
+
+ return Collections.unmodifiableList(list);
+ }
+
+ public static String getTime(int platformVersion) {
+ if (platformVersion == 0) {
+ return "5:00";
+ }
+ if (platformVersion < GINGERBREAD) {
+ return "2:20";
+ }
+ if (platformVersion < ICE_CREAM_SANDWICH) {
+ return "2:30";
+ }
+ if (platformVersion < JELLY_BEAN) {
+ return "4:00";
+ }
+ if (platformVersion < KITKAT) {
+ return "4:30";
+ }
+ if (platformVersion <= KITKAT_WATCH) {
+ return "4:40";
+ }
+ // Should never happen.
+ return "4:04";
+ }
+
+ public static int getTimeColor(int platformVersion) {
+ if (platformVersion == 0 || platformVersion >= KITKAT ||
+ platformVersion > FROYO && platformVersion < HONEYCOMB) {
+ // Gingerbread and KitKat onwards.
+ return WHITE;
+ }
+ // Black for froyo.
+ if (platformVersion < GINGERBREAD) {
+ return BLACK;
+ } else if (platformVersion < KITKAT) {
+ // Honeycomb to JB-mr2: Holo blue light.
+ return 0xff33b5e5;
+ }
+ // Should never happen.
+ return WHITE;
+ }
+
+ public static String getWifiIconType(int platformVersion) {
+ return platformVersion == 0 ? "xml" : "png";
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
index bcd08eb4..13ddf07 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -26,7 +26,6 @@ import com.android.layoutlib.bridge.impl.ParserFactory;
import com.android.layoutlib.bridge.impl.ResourceHelper;
import com.android.resources.Density;
import com.android.resources.LayoutDirection;
-import com.android.resources.ResourceType;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
@@ -59,11 +58,15 @@ import java.io.InputStream;
*/
abstract class CustomBar extends LinearLayout {
+
+ private final int mSimulatedPlatformVersion;
+
protected abstract TextView getStyleableTextView();
- protected CustomBar(Context context, Density density, int orientation, String layoutPath,
- String name) throws XmlPullParserException {
+ protected CustomBar(Context context, int orientation, String layoutPath,
+ String name, int simulatedPlatformVersion) throws XmlPullParserException {
super(context);
+ mSimulatedPlatformVersion = simulatedPlatformVersion;
setOrientation(orientation);
if (orientation == LinearLayout.HORIZONTAL) {
setGravity(Gravity.CENTER_VERTICAL);
@@ -87,40 +90,6 @@ abstract class CustomBar extends LinearLayout {
}
}
- private InputStream getIcon(String iconName, Density[] densityInOut, LayoutDirection direction,
- String[] pathOut, boolean tryOtherDensities) {
- // current density
- Density density = densityInOut[0];
-
- // bitmap url relative to this class
- if (direction != null) {
- pathOut[0] = "/bars/" + direction.getResourceValue() + "-" + density.getResourceValue()
- + "/" + iconName;
- } else {
- pathOut[0] = "/bars/" + density.getResourceValue() + "/" + iconName;
- }
-
- InputStream stream = getClass().getResourceAsStream(pathOut[0]);
- if (stream == null && tryOtherDensities) {
- for (Density d : Density.values()) {
- if (d != density) {
- densityInOut[0] = d;
- stream = getIcon(iconName, densityInOut, direction, pathOut,
- false /*tryOtherDensities*/);
- if (stream != null) {
- return stream;
- }
- }
- }
- // couldn't find resource with direction qualifier. try without.
- if (direction != null) {
- return getIcon(iconName, densityInOut, null, pathOut, true);
- }
- }
-
- return stream;
- }
-
protected void loadIcon(int index, String iconName, Density density) {
loadIcon(index, iconName, density, false);
}
@@ -130,20 +99,20 @@ abstract class CustomBar extends LinearLayout {
if (child instanceof ImageView) {
ImageView imageView = (ImageView) child;
- String[] pathOut = new String[1];
- Density[] densityInOut = new Density[] { density };
- LayoutDirection dir = isRtl ? LayoutDirection.RTL : LayoutDirection.LTR;
- InputStream stream = getIcon(iconName, densityInOut, dir, pathOut,
- true /*tryOtherDensities*/);
- density = densityInOut[0];
+ LayoutDirection dir = isRtl ? LayoutDirection.RTL : null;
+ IconLoader iconLoader = new IconLoader(iconName, density, mSimulatedPlatformVersion,
+ dir);
+ InputStream stream = iconLoader.getIcon();
if (stream != null) {
+ density = iconLoader.getDensity();
+ String path = iconLoader.getPath();
// look for a cached bitmap
- Bitmap bitmap = Bridge.getCachedBitmap(pathOut[0], true /*isFramework*/);
+ Bitmap bitmap = Bridge.getCachedBitmap(path, true /*isFramework*/);
if (bitmap == null) {
try {
bitmap = Bitmap_Delegate.createBitmap(stream, false /*isMutable*/, density);
- Bridge.setCachedBitmap(pathOut[0], bitmap, true /*isFramework*/);
+ Bridge.setCachedBitmap(path, bitmap, true /*isFramework*/);
} catch (IOException e) {
return;
}
@@ -158,94 +127,25 @@ abstract class CustomBar extends LinearLayout {
}
}
- protected void loadIcon(int index, String iconReference) {
- ResourceValue value = getResourceValue(iconReference);
- if (value != null) {
- loadIcon(index, value);
- }
- }
-
- protected void loadIconById(int id, String iconReference) {
- ResourceValue value = getResourceValue(iconReference);
- if (value != null) {
- loadIconById(id, value);
- }
- }
-
-
- protected Drawable loadIcon(int index, ResourceType type, String name) {
- BridgeContext bridgeContext = (BridgeContext) mContext;
- RenderResources res = bridgeContext.getRenderResources();
-
- // find the resource
- ResourceValue value = res.getFrameworkResource(type, name);
-
- // resolve it if needed
- value = res.resolveResValue(value);
- return loadIcon(index, value);
- }
-
- private Drawable loadIcon(int index, ResourceValue value) {
- View child = getChildAt(index);
- if (child instanceof ImageView) {
- ImageView imageView = (ImageView) child;
-
- return loadIcon(imageView, value);
- }
-
- return null;
- }
-
- private Drawable loadIconById(int id, ResourceValue value) {
- View child = findViewById(id);
- if (child instanceof ImageView) {
- ImageView imageView = (ImageView) child;
-
- return loadIcon(imageView, value);
- }
-
- return null;
- }
-
-
- private Drawable loadIcon(ImageView imageView, ResourceValue value) {
- Drawable drawable = ResourceHelper.getDrawable(value, (BridgeContext) mContext);
- if (drawable != null) {
- imageView.setImageDrawable(drawable);
- }
-
- return drawable;
- }
-
- protected TextView setText(int index, String stringReference) {
+ protected TextView setText(int index, String string, boolean reference) {
View child = getChildAt(index);
if (child instanceof TextView) {
TextView textView = (TextView) child;
- setText(textView, stringReference);
+ setText(textView, string, reference);
return textView;
}
return null;
}
- protected TextView setTextById(int id, String stringReference) {
- View child = findViewById(id);
- if (child instanceof TextView) {
- TextView textView = (TextView) child;
- setText(textView, stringReference);
- return textView;
- }
-
- return null;
- }
-
- private void setText(TextView textView, String stringReference) {
- ResourceValue value = getResourceValue(stringReference);
- if (value != null) {
- textView.setText(value.getValue());
- } else {
- textView.setText(stringReference);
+ private void setText(TextView textView, String string, boolean reference) {
+ if (reference) {
+ ResourceValue value = getResourceValue(string);
+ if (value != null) {
+ string = value.getValue();
+ }
}
+ textView.setText(string);
}
protected void setStyle(String themeEntryName) {
@@ -256,7 +156,7 @@ abstract class CustomBar extends LinearLayout {
ResourceValue value = res.findItemInTheme(themeEntryName, true /*isFrameworkAttr*/);
value = res.resolveResValue(value);
- if (value instanceof StyleResourceValue == false) {
+ if (!(value instanceof StyleResourceValue)) {
return;
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java
deleted file mode 100644
index 226649d..0000000
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/FakeActionBar.java
+++ /dev/null
@@ -1,48 +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 com.android.layoutlib.bridge.bars;
-
-import com.android.resources.Density;
-
-import org.xmlpull.v1.XmlPullParserException;
-
-import android.content.Context;
-import android.widget.LinearLayout;
-import android.widget.TextView;
-
-public class FakeActionBar extends CustomBar {
-
- private TextView mTextView;
-
- public FakeActionBar(Context context, Density density, String label, String icon)
- throws XmlPullParserException {
- super(context, density, LinearLayout.HORIZONTAL, "/bars/action_bar.xml", "action_bar.xml");
-
- // Cannot access the inside items through id because no R.id values have been
- // created for them.
- // We do know the order though.
- loadIconById(android.R.id.home, icon);
- mTextView = setText(1, label);
-
- setStyle("actionBarStyle");
- }
-
- @Override
- protected TextView getStyleableTextView() {
- return mTextView;
- }
-}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/IconLoader.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/IconLoader.java
new file mode 100644
index 0000000..9ab2e82
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/IconLoader.java
@@ -0,0 +1,125 @@
+/*
+ * 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.bars;
+
+import com.android.resources.Density;
+import com.android.resources.LayoutDirection;
+
+import java.io.InputStream;
+
+public class IconLoader {
+
+ private final String mIconName;
+ private final Density mDesiredDensity;
+ private final int mPlatformVersion;
+ private final LayoutDirection mDirection;
+
+ private Density mCurrentDensity;
+ private StringBuilder mCurrentPath;
+
+ IconLoader(String iconName, Density density, int platformVersion, LayoutDirection direction) {
+ mIconName = iconName;
+ mDesiredDensity = density;
+ mPlatformVersion = platformVersion;
+ mDirection = direction;
+ // An upper bound on the length of the path for the icon: /bars/v21/ldrtl-xxxhdpi/
+ final int iconPathLength = 24;
+ mCurrentPath = new StringBuilder(iconPathLength + iconName.length());
+ }
+
+ public InputStream getIcon() {
+ for (String resourceDir : Config.getResourceDirs(mPlatformVersion)) {
+ mCurrentDensity = null;
+ InputStream stream = getIcon(resourceDir);
+ if (stream != null) {
+ return stream;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Should only be called after {@link #getIcon()}. Returns the density of the icon, if found by
+ * {@code getIcon()}. If no icon was found, then the return value has no meaning.
+ */
+ public Density getDensity() {
+ return mCurrentDensity;
+ }
+
+ /**
+ * Should only be called after {@link #getIcon()}. Returns the path to the icon, if found by
+ * {@code getIcon()}. If no icon was found, then the return value has no meaning.
+ */
+ public String getPath() {
+ return mCurrentPath.toString();
+ }
+
+ /**
+ * Search for icon in the resource directory. This iterates over all densities.
+ * If a match is found, mCurrentDensity will be set to the icon's density.
+ */
+ private InputStream getIcon(String resourceDir) {
+ // First check for the desired density.
+ InputStream stream = getIcon(resourceDir, mDesiredDensity);
+ if (stream != null) {
+ mCurrentDensity = mDesiredDensity;
+ return stream;
+ }
+ // Didn't find in the desired density. Search in all.
+ for (Density density : Density.values()) {
+ if (density == mDesiredDensity) {
+ // Skip the desired density since it's already been checked.
+ continue;
+ }
+ stream = getIcon(resourceDir, density);
+ if (stream != null) {
+ mCurrentDensity = density;
+ return stream;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns the icon for given density present in the given resource directory, taking layout
+ * direction into consideration.
+ */
+ private InputStream getIcon(String resourceDir, Density density) {
+ mCurrentPath.setLength(0);
+ // Currently we don't have any LTR only resources and hence the check is skipped. If they
+ // are ever added, change to:
+ // if (mDirection == LayoutDirection.RTL || mDirection == LayoutDirection.LTR) {
+ if (mDirection == LayoutDirection.RTL) {
+ mCurrentPath.append(resourceDir)
+ .append(mDirection.getResourceValue())
+ .append('-')
+ .append(density.getResourceValue())
+ .append('/')
+ .append(mIconName);
+ InputStream stream = getClass().getResourceAsStream(mCurrentPath.toString());
+ if (stream != null) {
+ return stream;
+ }
+ mCurrentPath.setLength(0);
+ }
+ mCurrentPath.append(resourceDir)
+ .append(density.getResourceValue())
+ .append('/')
+ .append(mIconName);
+ return getClass().getResourceAsStream(mCurrentPath.toString());
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
index 84e676e..283ff57 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/NavigationBar.java
@@ -17,7 +17,6 @@
package com.android.layoutlib.bridge.bars;
import com.android.resources.Density;
-import com.android.layoutlib.bridge.Bridge;
import org.xmlpull.v1.XmlPullParserException;
@@ -28,8 +27,9 @@ import android.widget.TextView;
public class NavigationBar extends CustomBar {
public NavigationBar(Context context, Density density, int orientation, boolean isRtl,
- boolean rtlEnabled) throws XmlPullParserException {
- super(context, density, orientation, "/bars/navigation_bar.xml", "navigation_bar.xml");
+ boolean rtlEnabled, int simulatedPlatformVersion) throws XmlPullParserException {
+ super(context, orientation, "/bars/navigation_bar.xml", "navigation_bar.xml",
+ simulatedPlatformVersion);
setBackgroundColor(0xFF000000);
@@ -45,8 +45,11 @@ public class NavigationBar extends CustomBar {
recent = 1;
}
+ //noinspection SpellCheckingInspection
loadIcon(back, "ic_sysbar_back.png", density, isRtl);
+ //noinspection SpellCheckingInspection
loadIcon(2, "ic_sysbar_home.png", density, isRtl);
+ //noinspection SpellCheckingInspection
loadIcon(recent, "ic_sysbar_recent.png", density, isRtl);
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/OverflowMenuAdapter.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/OverflowMenuAdapter.java
new file mode 100644
index 0000000..778305d
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/OverflowMenuAdapter.java
@@ -0,0 +1,99 @@
+/*
+ * 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.bars;
+
+import com.android.internal.view.menu.MenuBuilder;
+import com.android.internal.view.menu.MenuItemImpl;
+import com.android.internal.view.menu.MenuView;
+
+import android.content.Context;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+
+import java.util.ArrayList;
+
+/**
+ * Provides an adapter for Overflow menu popup. This is very similar to
+ * {@code MenuPopupHelper.MenuAdapter}
+ */
+public class OverflowMenuAdapter extends BaseAdapter {
+
+ private final MenuBuilder mMenu;
+ private int mExpandedIndex = -1;
+ private final Context context;
+
+ public OverflowMenuAdapter(MenuBuilder menu, Context context) {
+ mMenu = menu;
+ findExpandedIndex();
+ this.context = context;
+ }
+
+ @Override
+ public int getCount() {
+ ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
+ if (mExpandedIndex < 0) {
+ return items.size();
+ }
+ return items.size() - 1;
+ }
+
+ @Override
+ public MenuItemImpl getItem(int position) {
+ ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
+ if (mExpandedIndex >= 0 && position >= mExpandedIndex) {
+ position++;
+ }
+ return items.get(position);
+ }
+
+ @Override
+ public long getItemId(int position) {
+ // Since a menu item's ID is optional, we'll use the position as an
+ // ID for the item in the AdapterView
+ return position;
+ }
+
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ if (convertView == null) {
+ LayoutInflater mInflater = LayoutInflater.from(context);
+ convertView = mInflater.inflate(com.android.internal.R.layout.popup_menu_item_layout,
+ parent, false);
+ }
+
+ MenuView.ItemView itemView = (MenuView.ItemView) convertView;
+ itemView.initialize(getItem(position), 0);
+ return convertView;
+ }
+
+ private void findExpandedIndex() {
+ final MenuItemImpl expandedItem = mMenu.getExpandedItem();
+ if (expandedItem != null) {
+ final ArrayList<MenuItemImpl> items = mMenu.getNonActionItems();
+ final int count = items.size();
+ for (int i = 0; i < count; i++) {
+ final MenuItemImpl item = items.get(i);
+ if (item == expandedItem) {
+ mExpandedIndex = i;
+ return;
+ }
+ }
+ }
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
index 3692d96..c7c62d6 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
@@ -16,36 +16,85 @@
package com.android.layoutlib.bridge.bars;
+import com.android.ide.common.rendering.api.LayoutLog;
+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.impl.ParserFactory;
import com.android.resources.Density;
-import com.android.resources.ResourceType;
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LevelListDrawable;
import android.view.Gravity;
+import android.view.View;
+import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
+import java.io.IOException;
+import java.io.InputStream;
+
public class StatusBar extends CustomBar {
- public StatusBar(Context context, Density density, int direction, boolean RtlEnabled)
- throws XmlPullParserException {
- // FIXME: if direction is RTL but it's not enabled in application manifest, mirror this bar.
+ private final Context mContext;
+ private final int mSimulatedPlatformVersion;
- super(context, density, LinearLayout.HORIZONTAL, "/bars/status_bar.xml", "status_bar.xml");
+ public StatusBar(Context context, Density density, int direction, boolean RtlEnabled,
+ int simulatedPlatformVersion) throws XmlPullParserException {
+ // FIXME: if direction is RTL but it's not enabled in application manifest, mirror this bar.
+ super(context, LinearLayout.HORIZONTAL, "/bars/status_bar.xml", "status_bar.xml",
+ simulatedPlatformVersion);
+ mContext = context;
+ mSimulatedPlatformVersion = simulatedPlatformVersion;
// FIXME: use FILL_H?
setGravity(Gravity.START | Gravity.TOP | Gravity.RIGHT);
- setBackgroundColor(0xFF000000);
+ setBackgroundColor(Config.getStatusBarColor(simulatedPlatformVersion));
// Cannot access the inside items through id because no R.id values have been
// created for them.
// We do know the order though.
// 0 is the spacer
- loadIcon(1, "stat_sys_wifi_signal_4_fully.png", density);
- loadIcon(2, "stat_sys_battery_charge_anim100.png", density);
+ loadIcon(1, "stat_sys_wifi_signal_4_fully."
+ + Config.getWifiIconType(simulatedPlatformVersion), density);
+ loadIcon(2, "stat_sys_battery_100.png", density);
+ setText(3, Config.getTime(simulatedPlatformVersion), false)
+ .setTextColor(Config.getTimeColor(simulatedPlatformVersion));
+ }
+
+ @Override
+ protected void loadIcon(int index, String iconName, Density density) {
+ if (!iconName.endsWith(".xml")) {
+ super.loadIcon(index, iconName, density);
+ return;
+ }
+ View child = getChildAt(index);
+ if (child instanceof ImageView) {
+ ImageView imageView = (ImageView) child;
+ // The xml is stored only in xhdpi.
+ IconLoader iconLoader = new IconLoader(iconName, Density.XHIGH,
+ mSimulatedPlatformVersion, null);
+ InputStream stream = iconLoader.getIcon();
+
+ if (stream != null) {
+ try {
+ BridgeXmlBlockParser parser = new BridgeXmlBlockParser(
+ ParserFactory.create(stream, null), (BridgeContext) mContext, true);
+ Drawable drawable = Drawable.createFromXml(mContext.getResources(), parser);
+ if (drawable != null) {
+ imageView.setImageDrawable(drawable);
+ }
+ } catch (XmlPullParserException e) {
+ Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Unable to draw wifi icon", e,
+ null);
+ } catch (IOException e) {
+ Bridge.getLog().error(LayoutLog.TAG_BROKEN, "Unable to draw wifi icon", e,
+ null);
+ }
+ }
+ }
}
@Override
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java
index c27859f..10f1383 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/bars/TitleBar.java
@@ -16,8 +16,6 @@
package com.android.layoutlib.bridge.bars;
-import com.android.resources.Density;
-
import org.xmlpull.v1.XmlPullParserException;
import android.content.Context;
@@ -28,14 +26,15 @@ public class TitleBar extends CustomBar {
private TextView mTextView;
- public TitleBar(Context context, Density density, String label)
+ public TitleBar(Context context, String label, int simulatedPlatformVersion)
throws XmlPullParserException {
- super(context, density, LinearLayout.HORIZONTAL, "/bars/title_bar.xml", "title_bar.xml");
+ super(context, LinearLayout.HORIZONTAL, "/bars/title_bar.xml", "title_bar.xml",
+ simulatedPlatformVersion);
// Cannot access the inside items through id because no R.id values have been
// created for them.
// We do know the order though.
- mTextView = setText(0, label);
+ mTextView = setText(0, label, true);
setStyle("windowTitleBackgroundStyle");
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java
deleted file mode 100644
index cc7338a..0000000
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java
+++ /dev/null
@@ -1,398 +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 com.android.layoutlib.bridge.impl;
-
-import org.xml.sax.Attributes;
-import org.xml.sax.SAXException;
-import org.xml.sax.helpers.DefaultHandler;
-
-import android.graphics.Typeface;
-
-import java.awt.Font;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import javax.xml.parsers.ParserConfigurationException;
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-
-/**
- * Provides {@link Font} object to the layout lib.
- * <p/>
- * The fonts are loaded from the SDK directory. Family/style mapping is done by parsing the
- * fonts.xml file located alongside the ttf files.
- */
-public final class FontLoader {
- private static final String FONTS_SYSTEM = "system_fonts.xml";
- private static final String FONTS_VENDOR = "vendor_fonts.xml";
- private static final String FONTS_FALLBACK = "fallback_fonts.xml";
-
- private static final String NODE_FAMILYSET = "familyset";
- private static final String NODE_FAMILY = "family";
- private static final String NODE_NAME = "name";
- private static final String NODE_FILE = "file";
-
- private static final String ATTRIBUTE_VARIANT = "variant";
- private static final String ATTRIBUTE_VALUE_ELEGANT = "elegant";
- private static final String FONT_SUFFIX_NONE = ".ttf";
- private static final String FONT_SUFFIX_REGULAR = "-Regular.ttf";
- private static final String FONT_SUFFIX_BOLD = "-Bold.ttf";
- // FONT_SUFFIX_ITALIC will always match FONT_SUFFIX_BOLDITALIC and hence it must be checked
- // separately.
- private static final String FONT_SUFFIX_ITALIC = "Italic.ttf";
- private static final String FONT_SUFFIX_BOLDITALIC = "-BoldItalic.ttf";
-
- // This must match the values of Typeface styles so that we can use them for indices in this
- // array.
- private static final int[] AWT_STYLES = new int[] {
- Font.PLAIN,
- Font.BOLD,
- Font.ITALIC,
- Font.BOLD | Font.ITALIC
- };
- private static int[] DERIVE_BOLD_ITALIC = new int[] {
- Typeface.ITALIC, Typeface.BOLD, Typeface.NORMAL
- };
- private static int[] DERIVE_ITALIC = new int[] { Typeface.NORMAL };
- private static int[] DERIVE_BOLD = new int[] { Typeface.NORMAL };
-
- private static final List<FontInfo> mMainFonts = new ArrayList<FontInfo>();
- private static final List<FontInfo> mFallbackFonts = new ArrayList<FontInfo>();
-
- private final String mOsFontsLocation;
-
- public static FontLoader create(String fontOsLocation) {
- try {
- SAXParserFactory parserFactory = SAXParserFactory.newInstance();
- parserFactory.setNamespaceAware(true);
-
- // parse the system fonts
- FontHandler handler = parseFontFile(parserFactory, fontOsLocation, FONTS_SYSTEM);
- List<FontInfo> systemFonts = handler.getFontList();
-
-
- // parse the fallback fonts
- handler = parseFontFile(parserFactory, fontOsLocation, FONTS_FALLBACK);
- List<FontInfo> fallbackFonts = handler.getFontList();
-
- return new FontLoader(fontOsLocation, systemFonts, fallbackFonts);
- } catch (ParserConfigurationException e) {
- // return null below
- } catch (SAXException e) {
- // return null below
- } catch (FileNotFoundException e) {
- // return null below
- } catch (IOException e) {
- // return null below
- }
-
- return null;
- }
-
- private static FontHandler parseFontFile(SAXParserFactory parserFactory,
- String fontOsLocation, String fontFileName)
- throws ParserConfigurationException, SAXException, IOException, FileNotFoundException {
-
- SAXParser parser = parserFactory.newSAXParser();
- File f = new File(fontOsLocation, fontFileName);
-
- FontHandler definitionParser = new FontHandler(
- fontOsLocation + File.separator);
- parser.parse(new FileInputStream(f), definitionParser);
- return definitionParser;
- }
-
- private FontLoader(String fontOsLocation,
- List<FontInfo> fontList, List<FontInfo> fallBackList) {
- mOsFontsLocation = fontOsLocation;
- mMainFonts.addAll(fontList);
- mFallbackFonts.addAll(fallBackList);
- }
-
-
- public String getOsFontsLocation() {
- return mOsFontsLocation;
- }
-
- /**
- * Returns a {@link Font} object given a family name and a style value (constant in
- * {@link Typeface}).
- * @param family the family name
- * @param style a 1-item array containing the requested style. Based on the font being read
- * the actual style may be different. The array contains the actual style after
- * the method returns.
- * @return the font object or null if no match could be found.
- */
- public synchronized List<Font> getFont(String family, int style) {
- List<Font> result = new ArrayList<Font>();
-
- if (family == null) {
- return result;
- }
-
-
- // get the font objects from the main list based on family.
- for (FontInfo info : mMainFonts) {
- if (info.families.contains(family)) {
- result.add(info.font[style]);
- break;
- }
- }
-
- // add all the fallback fonts for the given style
- for (FontInfo info : mFallbackFonts) {
- result.add(info.font[style]);
- }
-
- return result;
- }
-
-
- public synchronized List<Font> getFallbackFonts(int style) {
- List<Font> result = new ArrayList<Font>();
- // add all the fallback fonts
- for (FontInfo info : mFallbackFonts) {
- result.add(info.font[style]);
- }
- return result;
- }
-
-
- private final static class FontInfo {
- final Font[] font = new Font[4]; // Matches the 4 type-face styles.
- final Set<String> families;
-
- FontInfo() {
- families = new HashSet<String>();
- }
- }
-
- private final static class FontHandler extends DefaultHandler {
- private final String mOsFontsLocation;
-
- private FontInfo mFontInfo = null;
- private final StringBuilder mBuilder = new StringBuilder();
- private List<FontInfo> mFontList = new ArrayList<FontInfo>();
- private boolean isCompactFont = true;
-
- private FontHandler(String osFontsLocation) {
- super();
- mOsFontsLocation = osFontsLocation;
- }
-
- public List<FontInfo> getFontList() {
- return mFontList;
- }
-
- /* (non-Javadoc)
- * @see org.xml.sax.helpers.DefaultHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes)
- */
- @Override
- public void startElement(String uri, String localName, String name, Attributes attributes)
- throws SAXException {
- if (NODE_FAMILYSET.equals(localName)) {
- mFontList = new ArrayList<FontInfo>();
- } else if (NODE_FAMILY.equals(localName)) {
- if (mFontList != null) {
- mFontInfo = null;
- }
- } else if (NODE_NAME.equals(localName)) {
- if (mFontList != null && mFontInfo == null) {
- mFontInfo = new FontInfo();
- }
- } else if (NODE_FILE.equals(localName)) {
- if (mFontList != null && mFontInfo == null) {
- mFontInfo = new FontInfo();
- }
- if (ATTRIBUTE_VALUE_ELEGANT.equals(attributes.getValue(ATTRIBUTE_VARIANT))) {
- isCompactFont = false;
- } else {
- isCompactFont = true;
- }
- }
-
- mBuilder.setLength(0);
-
- super.startElement(uri, localName, name, attributes);
- }
-
- /* (non-Javadoc)
- * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int)
- */
- @Override
- public void characters(char[] ch, int start, int length) throws SAXException {
- if (isCompactFont) {
- mBuilder.append(ch, start, length);
- }
- }
-
- /* (non-Javadoc)
- * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String)
- */
- @Override
- public void endElement(String uri, String localName, String name) throws SAXException {
- if (NODE_FAMILY.equals(localName)) {
- if (mFontInfo != null) {
- // if has a normal font file, add to the list
- if (mFontInfo.font[Typeface.NORMAL] != null) {
- mFontList.add(mFontInfo);
-
- // create missing font styles, order is important.
- if (mFontInfo.font[Typeface.BOLD_ITALIC] == null) {
- computeDerivedFont(Typeface.BOLD_ITALIC, DERIVE_BOLD_ITALIC);
- }
- if (mFontInfo.font[Typeface.ITALIC] == null) {
- computeDerivedFont(Typeface.ITALIC, DERIVE_ITALIC);
- }
- if (mFontInfo.font[Typeface.BOLD] == null) {
- computeDerivedFont(Typeface.BOLD, DERIVE_BOLD);
- }
- }
-
- mFontInfo = null;
- }
- } else if (NODE_NAME.equals(localName)) {
- // handle a new name for an existing Font Info
- if (mFontInfo != null) {
- String family = trimXmlWhitespaces(mBuilder.toString());
- mFontInfo.families.add(family);
- }
- } else if (NODE_FILE.equals(localName)) {
- // handle a new file for an existing Font Info
- if (isCompactFont && mFontInfo != null) {
- String fileName = trimXmlWhitespaces(mBuilder.toString());
- Font font = getFont(fileName);
- if (font != null) {
- if (fileName.endsWith(FONT_SUFFIX_REGULAR)) {
- mFontInfo.font[Typeface.NORMAL] = font;
- } else if (fileName.endsWith(FONT_SUFFIX_BOLD)) {
- mFontInfo.font[Typeface.BOLD] = font;
- } else if (fileName.endsWith(FONT_SUFFIX_BOLDITALIC)) {
- mFontInfo.font[Typeface.BOLD_ITALIC] = font;
- } else if (fileName.endsWith(FONT_SUFFIX_ITALIC)) {
- mFontInfo.font[Typeface.ITALIC] = font;
- } else if (fileName.endsWith(FONT_SUFFIX_NONE)) {
- mFontInfo.font[Typeface.NORMAL] = font;
- }
- }
- }
- }
- }
-
- private Font getFont(String fileName) {
- try {
- File file = new File(mOsFontsLocation, fileName);
- if (file.exists()) {
- return Font.createFont(Font.TRUETYPE_FONT, file);
- }
- } catch (Exception e) {
-
- }
-
- return null;
- }
-
- private void computeDerivedFont( int toCompute, int[] basedOnList) {
- for (int basedOn : basedOnList) {
- if (mFontInfo.font[basedOn] != null) {
- mFontInfo.font[toCompute] =
- mFontInfo.font[basedOn].deriveFont(AWT_STYLES[toCompute]);
- return;
- }
- }
-
- // we really shouldn't stop there. This means we don't have a NORMAL font...
- assert false;
- }
-
- private String trimXmlWhitespaces(String value) {
- if (value == null) {
- return null;
- }
-
- // look for carriage return and replace all whitespace around it by just 1 space.
- int index;
-
- while ((index = value.indexOf('\n')) != -1) {
- // look for whitespace on each side
- int left = index - 1;
- while (left >= 0) {
- if (Character.isWhitespace(value.charAt(left))) {
- left--;
- } else {
- break;
- }
- }
-
- int right = index + 1;
- int count = value.length();
- while (right < count) {
- if (Character.isWhitespace(value.charAt(right))) {
- right++;
- } else {
- break;
- }
- }
-
- // remove all between left and right (non inclusive) and replace by a single space.
- String leftString = null;
- if (left >= 0) {
- leftString = value.substring(0, left + 1);
- }
- String rightString = null;
- if (right < count) {
- rightString = value.substring(right);
- }
-
- if (leftString != null) {
- value = leftString;
- if (rightString != null) {
- value += " " + rightString;
- }
- } else {
- value = rightString != null ? rightString : "";
- }
- }
-
- // now we un-escape the string
- int length = value.length();
- char[] buffer = value.toCharArray();
-
- for (int i = 0 ; i < length ; i++) {
- if (buffer[i] == '\\') {
- if (buffer[i+1] == 'n') {
- // replace the char with \n
- buffer[i+1] = '\n';
- }
-
- // offset the rest of the buffer since we go from 2 to 1 char
- System.arraycopy(buffer, i+1, buffer, i, length - i - 1);
- length--;
- }
- }
-
- return new String(buffer, 0, length);
- }
-
- }
-}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
index 21d6b1a..3a0321a 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/GcSnapshot.java
@@ -21,6 +21,7 @@ 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.Rect;
@@ -48,8 +49,8 @@ import java.util.ArrayList;
* 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)} and
- * {@link #draw(Drawable, Paint_Delegate)}
+ * 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}
@@ -203,7 +204,7 @@ public class GcSnapshot {
* called before the snapshot can be used to draw. Transform and clip operations are permitted
* before.
*
- * @param image the image to associate to the snapshot or null.
+ * @param bitmap the image to associate to the snapshot or null.
* @return the root snapshot
*/
public static GcSnapshot createDefaultSnapshot(Bitmap_Delegate bitmap) {
@@ -557,7 +558,6 @@ public class GcSnapshot {
* 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.
- * @param drawable
*/
public void draw(Drawable drawable) {
draw(drawable, null, false /*compositeOnly*/, false /*forceSrcMode*/);
@@ -567,20 +567,19 @@ public class GcSnapshot {
* 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 drawable
- * @param paint
* @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, forceSrcMode);
+ 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.
@@ -590,7 +589,7 @@ public class GcSnapshot {
do {
Layer layer = mLayers.get(i);
- drawInLayer(layer, drawable, paint, compositeOnly, forceSrcMode);
+ 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.
@@ -601,20 +600,61 @@ public class GcSnapshot {
}
private void drawInLayer(Layer layer, Drawable drawable, Paint_Delegate paint,
- boolean compositeOnly, boolean forceSrcMode) {
+ boolean compositeOnly, int forceMode) {
Graphics2D originalGraphics = layer.getGraphics();
- // get a Graphics2D object configured with the drawing parameters.
- Graphics2D configuredGraphics2D =
- paint != null ?
- createCustomGraphics(originalGraphics, paint, compositeOnly, forceSrcMode) :
- (Graphics2D) originalGraphics.create();
+ 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;
+ }
+
+ int width = layer.getImage().getWidth();
+ int height = layer.getImage().getHeight();
+
+ // Create a temporary image to which the color filter will be applied.
+ BufferedImage image = new BufferedImage(width, height,
+ BufferedImage.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);
+ try {
+ // The main draw operation.
+ drawable.draw(imageGraphics, paint);
+
+ // Apply the color filter.
+ filter.applyFilter(imageGraphics, width, height);
+
+ // Draw the tinted image on the main layer.
+ configuredGraphics.drawImage(image, 0, 0, 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(configuredGraphics2D, paint);
+ drawable.draw(g, paint);
layer.change();
} finally {
- // dispose Graphics2D object
- configuredGraphics2D.dispose();
+ g.dispose();
}
}
@@ -685,7 +725,7 @@ public class GcSnapshot {
// now draw put the content of the local layer onto the layer,
// using the paint information
Graphics2D g = createCustomGraphics(baseGfx, mLocalLayerPaint,
- true /*alphaOnly*/, false /*forceSrcMode*/);
+ true /*alphaOnly*/, 0 /*forceMode*/);
g.drawImage(mLocalLayer.getImage(),
mLayerBounds.left, mLayerBounds.top, mLayerBounds.right, mLayerBounds.bottom,
@@ -701,7 +741,7 @@ public class GcSnapshot {
* <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
*/
private Graphics2D createCustomGraphics(Graphics2D original, Paint_Delegate paint,
- boolean compositeOnly, boolean forceSrcMode) {
+ boolean compositeOnly, int forceMode) {
// make new one graphics
Graphics2D g = (Graphics2D) original.create();
@@ -714,70 +754,73 @@ public class GcSnapshot {
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.
boolean customShader = false;
+ if (!compositeOnly) {
+ customShader = setShader(g, paint);
+ // set the stroke
+ g.setStroke(paint.getJavaStroke());
+ }
+ // set the composite.
+ setComposite(g, paint, compositeOnly || customShader, forceMode);
- // get the shader first, as it'll replace the color if it can be used it.
- if (compositeOnly == false) {
- Shader_Delegate shaderDelegate = paint.getShader();
- if (shaderDelegate != null) {
- if (shaderDelegate.isSupported()) {
- java.awt.Paint shaderPaint = shaderDelegate.getJavaPaint();
- assert shaderPaint != null;
- if (shaderPaint != null) {
- g.setPaint(shaderPaint);
- customShader = true;
- }
- } else {
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_SHADER,
- shaderDelegate.getSupportMessage(),
- null /*throwable*/, null /*data*/);
+ return g;
+ }
+
+ private boolean 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;
+ if (shaderPaint != null) {
+ g.setPaint(shaderPaint);
+ return true;
}
+ } else {
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_SHADER,
+ shaderDelegate.getSupportMessage(),
+ null /*throwable*/, null /*data*/);
}
+ }
- // if no shader, use the paint color
- if (customShader == false) {
- g.setColor(new Color(paint.getColor(), true /*hasAlpha*/));
- }
+ // if no shader, use the paint color
+ g.setColor(new Color(paint.getColor(), true /*hasAlpha*/));
- // set the stroke
- g.setStroke(paint.getJavaStroke());
- }
+ return false;
+ }
+ 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 = (compositeOnly || customShader) ? paint.getAlpha() : 0xFF;
-
- if (forceSrcMode) {
- g.setComposite(AlphaComposite.getInstance(
- AlphaComposite.SRC, (float) alpha / 255.f));
- } else {
- boolean customXfermode = false;
- Xfermode_Delegate xfermodeDelegate = paint.getXfermode();
- if (xfermodeDelegate != null) {
- if (xfermodeDelegate.isSupported()) {
- Composite composite = xfermodeDelegate.getComposite(alpha);
- assert composite != null;
- if (composite != null) {
- g.setComposite(composite);
- customXfermode = true;
- }
- } else {
- Bridge.getLog().fidelityWarning(LayoutLog.TAG_XFERMODE,
- xfermodeDelegate.getSupportMessage(),
- null /*throwable*/, null /*data*/);
+ int alpha = usePaintAlpha ? paint.getAlpha() : 0xFF;
+ if (forceMode != 0) {
+ g.setComposite(AlphaComposite.getInstance(forceMode, (float) alpha / 255.f));
+ return;
+ }
+ Xfermode_Delegate xfermodeDelegate = paint.getXfermode();
+ if (xfermodeDelegate != null) {
+ if (xfermodeDelegate.isSupported()) {
+ Composite composite = xfermodeDelegate.getComposite(alpha);
+ assert composite != null;
+ if (composite != null) {
+ g.setComposite(composite);
+ return;
}
- }
-
- // if there was no custom xfermode, but we have alpha (due to a shader and a non
- // opaque alpha channel in the paint color), then we create an AlphaComposite anyway
- // that will handle the alpha.
- if (customXfermode == false && alpha != 0xFF) {
- g.setComposite(AlphaComposite.getInstance(
- AlphaComposite.SRC_OVER, (float) alpha / 255.f));
+ } else {
+ Bridge.getLog().fidelityWarning(LayoutLog.TAG_XFERMODE,
+ xfermodeDelegate.getSupportMessage(),
+ null /*throwable*/, null /*data*/);
}
}
-
- return g;
+ // if there was no custom xfermode, but we have alpha (due to a shader and a non
+ // opaque alpha channel in the paint color), then we create an AlphaComposite anyway
+ // that will handle the alpha.
+ if (alpha != 0xFF) {
+ g.setComposite(AlphaComposite.getInstance(
+ AlphaComposite.SRC_OVER, (float) alpha / 255.f));
+ }
}
private void mapRect(AffineTransform matrix, RectF dst, RectF src) {
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
new file mode 100644
index 0000000..9588035
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/PorterDuffUtility.java
@@ -0,0 +1,107 @@
+/*
+ * 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.LayoutLog;
+import com.android.layoutlib.bridge.Bridge;
+
+import android.graphics.BlendComposite;
+import android.graphics.BlendComposite.BlendingMode;
+import android.graphics.PorterDuff.Mode;
+import android.graphics.PorterDuffColorFilter_Delegate;
+import android.graphics.PorterDuffXfermode_Delegate;
+
+import java.awt.AlphaComposite;
+import java.awt.Composite;
+
+/**
+ * Provides various utility methods for {@link PorterDuffColorFilter_Delegate} and {@link
+ * PorterDuffXfermode_Delegate}.
+ */
+public final class PorterDuffUtility {
+
+ // 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) {
+ Mode[] values = Mode.values();
+ if (porterDuffMode >= 0 && porterDuffMode < values.length) {
+ return values[porterDuffMode];
+ }
+ Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+ String.format("Unknown PorterDuff.Mode: %1$d", porterDuffMode), null /*data*/);
+ 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(LayoutLog.TAG_BROKEN,
+ String.format("Unsupported PorterDuff Mode: %1$s", mode.name()),
+ null, null /*data*/);
+
+ return AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha1);
+ }
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index 377d996..b8dce70 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -28,7 +28,6 @@ import com.android.ide.common.rendering.api.HardwareConfig;
import com.android.ide.common.rendering.api.IAnimationListener;
import com.android.ide.common.rendering.api.ILayoutPullParser;
import com.android.ide.common.rendering.api.IProjectCallback;
-import com.android.ide.common.rendering.api.RenderParams;
import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.RenderSession;
import com.android.ide.common.rendering.api.ResourceReference;
@@ -38,17 +37,26 @@ import com.android.ide.common.rendering.api.Result.Status;
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.ViewType;
import com.android.internal.util.XmlUtils;
+import com.android.internal.view.menu.ActionMenuItemView;
+import com.android.internal.view.menu.BridgeMenuItemImpl;
+import com.android.internal.view.menu.IconMenuItemView;
+import com.android.internal.view.menu.ListMenuItemView;
+import com.android.internal.view.menu.MenuItemImpl;
+import com.android.internal.view.menu.MenuView;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
-import com.android.layoutlib.bridge.bars.FakeActionBar;
+import com.android.layoutlib.bridge.bars.Config;
import com.android.layoutlib.bridge.bars.NavigationBar;
import com.android.layoutlib.bridge.bars.StatusBar;
import com.android.layoutlib.bridge.bars.TitleBar;
+import com.android.layoutlib.bridge.bars.ActionBarLayout;
import com.android.layoutlib.bridge.impl.binding.FakeAdapter;
import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
+import com.android.resources.Density;
import com.android.resources.ResourceType;
import com.android.resources.ScreenOrientation;
import com.android.util.Pair;
@@ -77,9 +85,12 @@ 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.WindowManagerGlobal_Delegate;
+import android.view.ViewParent;
import android.widget.AbsListView;
import android.widget.AbsSpinner;
+import android.widget.ActionMenuView;
import android.widget.AdapterView;
import android.widget.ExpandableListView;
import android.widget.FrameLayout;
@@ -100,11 +111,10 @@ import java.util.Map;
/**
* Class implementing the render session.
- *
+ * <p/>
* A session is a stateful representation of a layout file. It is initialized with data coming
* through the {@link Bridge} API to inflate the layout. Further actions and rendering can then
* be done on the layout.
- *
*/
public class RenderSessionImpl extends RenderAction<SessionParams> {
@@ -134,6 +144,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
// information being returned through the API
private BufferedImage mImage;
private List<ViewInfo> mViewInfoList;
+ private List<ViewInfo> mSystemViewInfoList;
private static final class PostInflateException extends Exception {
private static final long serialVersionUID = 1L;
@@ -146,10 +157,11 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
/**
* Creates a layout scene with all the information coming from the layout bridge API.
* <p>
- * This <b>must</b> be followed by a call to {@link RenderSessionImpl#init()}, which act as a
+ * This <b>must</b> be followed by a call to {@link RenderSessionImpl#init(long)},
+ * which act as a
* call to {@link RenderSessionImpl#acquire(long)}
*
- * @see LayoutBridge#createScene(com.android.layoutlib.api.SceneParams)
+ * @see Bridge#createSession(SessionParams)
*/
public RenderSessionImpl(SessionParams params) {
super(new SessionParams(params));
@@ -169,13 +181,14 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
@Override
public Result init(long timeout) {
Result result = super.init(timeout);
- if (result.isSuccess() == false) {
+ if (!result.isSuccess()) {
return result;
}
SessionParams params = getParams();
BridgeContext context = getContext();
+
RenderResources resources = getParams().getResources();
DisplayMetrics metrics = getContext().getMetrics();
@@ -193,6 +206,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
// FIXME: find those out, and possibly add them to the render params
boolean hasNavigationBar = true;
+ //noinspection ConstantConditions
IWindowManager iwm = new IWindowManagerImpl(getContext().getConfiguration(),
metrics, Surface.ROTATION_0,
hasNavigationBar);
@@ -225,15 +239,16 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
HardwareConfig hardwareConfig = params.getHardwareConfig();
BridgeContext context = getContext();
boolean isRtl = Bridge.isLocaleRtl(params.getLocale());
- int direction = isRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR;
+ int layoutDirection = isRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR;
// the view group that receives the window background.
- ViewGroup backgroundView = null;
+ ViewGroup backgroundView;
if (mWindowIsFloating || params.isForceNoDecor()) {
backgroundView = mViewRoot = mContentRoot = new FrameLayout(context);
- mViewRoot.setLayoutDirection(direction);
+ mViewRoot.setLayoutDirection(layoutDirection);
} else {
+ int simulatedPlatformVersion = params.getSimulatedPlatformVersion();
if (hasSoftwareButtons() && mNavigationBarOrientation == LinearLayout.VERTICAL) {
/*
* This is a special case where the navigation bar is on the right.
@@ -254,21 +269,18 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
the bottom
*/
LinearLayout topLayout = new LinearLayout(context);
- topLayout.setLayoutDirection(direction);
+ topLayout.setLayoutDirection(layoutDirection);
mViewRoot = topLayout;
topLayout.setOrientation(LinearLayout.HORIZONTAL);
- try {
- NavigationBar navigationBar = new NavigationBar(context,
- hardwareConfig.getDensity(), LinearLayout.VERTICAL, isRtl,
- params.isRtlSupported());
- navigationBar.setLayoutParams(
- new LinearLayout.LayoutParams(
- mNavigationBarSize,
- LayoutParams.MATCH_PARENT));
- topLayout.addView(navigationBar);
- } catch (XmlPullParserException e) {
-
+ if (Config.showOnScreenNavBar(simulatedPlatformVersion)) {
+ try {
+ NavigationBar navigationBar = createNavigationBar(context,
+ hardwareConfig.getDensity(), isRtl, params.isRtlSupported(),
+ simulatedPlatformVersion);
+ topLayout.addView(navigationBar);
+ } catch (XmlPullParserException ignored) {
+ }
}
}
@@ -293,14 +305,15 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
LinearLayout topLayout = new LinearLayout(context);
topLayout.setOrientation(LinearLayout.VERTICAL);
- topLayout.setLayoutDirection(direction);
+ topLayout.setLayoutDirection(layoutDirection);
// if we don't already have a view root this is it
if (mViewRoot == null) {
mViewRoot = topLayout;
} else {
+ int topLayoutWidth =
+ params.getHardwareConfig().getScreenWidth() - mNavigationBarSize;
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
- LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT);
- layoutParams.weight = 1;
+ topLayoutWidth, LayoutParams.MATCH_PARENT);
topLayout.setLayoutParams(layoutParams);
// this is the case of soft buttons + vertical bar.
@@ -319,13 +332,11 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
if (mStatusBarSize > 0) {
// system bar
try {
- StatusBar systemBar = new StatusBar(context, hardwareConfig.getDensity(),
- direction, params.isRtlSupported());
- systemBar.setLayoutParams(
- new LinearLayout.LayoutParams(
- LayoutParams.MATCH_PARENT, mStatusBarSize));
- topLayout.addView(systemBar);
- } catch (XmlPullParserException e) {
+ StatusBar statusBar = createStatusBar(context, hardwareConfig.getDensity(),
+ layoutDirection, params.isRtlSupported(),
+ simulatedPlatformVersion);
+ topLayout.addView(statusBar);
+ } catch (XmlPullParserException ignored) {
}
}
@@ -342,50 +353,41 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
// if the theme says no title/action bar, then the size will be 0
if (mActionBarSize > 0) {
- try {
- FakeActionBar actionBar = new FakeActionBar(context,
- hardwareConfig.getDensity(),
- params.getAppLabel(), params.getAppIcon());
- actionBar.setLayoutParams(
- new LinearLayout.LayoutParams(
- LayoutParams.MATCH_PARENT, mActionBarSize));
- backgroundLayout.addView(actionBar);
- } catch (XmlPullParserException e) {
-
- }
+ ActionBarLayout actionBar = createActionBar(context, params);
+ backgroundLayout.addView(actionBar);
+ actionBar.createMenuPopup();
+ mContentRoot = actionBar.getContentRoot();
} else if (mTitleBarSize > 0) {
try {
- TitleBar titleBar = new TitleBar(context,
- hardwareConfig.getDensity(), params.getAppLabel());
- titleBar.setLayoutParams(
- new LinearLayout.LayoutParams(
- LayoutParams.MATCH_PARENT, mTitleBarSize));
+ TitleBar titleBar = createTitleBar(context,
+ params.getAppLabel(),
+ simulatedPlatformVersion);
backgroundLayout.addView(titleBar);
- } catch (XmlPullParserException e) {
+ } catch (XmlPullParserException ignored) {
}
}
// content frame
- mContentRoot = new FrameLayout(context);
- layoutParams = new LinearLayout.LayoutParams(
- LayoutParams.MATCH_PARENT, 0);
- layoutParams.weight = 1;
- mContentRoot.setLayoutParams(layoutParams);
- backgroundLayout.addView(mContentRoot);
+ if (mContentRoot == null) {
+ mContentRoot = new FrameLayout(context);
+ layoutParams = new LinearLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT, 0);
+ layoutParams.weight = 1;
+ mContentRoot.setLayoutParams(layoutParams);
+ backgroundLayout.addView(mContentRoot);
+ }
- if (mNavigationBarOrientation == LinearLayout.HORIZONTAL &&
+ if (Config.showOnScreenNavBar(simulatedPlatformVersion) &&
+ mNavigationBarOrientation == LinearLayout.HORIZONTAL &&
mNavigationBarSize > 0) {
// system bar
try {
- NavigationBar navigationBar = new NavigationBar(context,
- hardwareConfig.getDensity(), LinearLayout.HORIZONTAL, isRtl,
- params.isRtlSupported());
- navigationBar.setLayoutParams(
- new LinearLayout.LayoutParams(
- LayoutParams.MATCH_PARENT, mNavigationBarSize));
+ NavigationBar navigationBar = createNavigationBar(context,
+ hardwareConfig.getDensity(), isRtl, params.isRtlSupported(),
+ simulatedPlatformVersion);
topLayout.addView(navigationBar);
- } catch (XmlPullParserException e) {
+ } catch (XmlPullParserException ignored) {
}
}
@@ -410,7 +412,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
postInflateProcess(view, params.getProjectCallback());
// get the background drawable
- if (mWindowBackground != null && backgroundView != null) {
+ if (mWindowBackground != null) {
Drawable d = ResourceHelper.getDrawable(mWindowBackground, context);
backgroundView.setBackground(d);
}
@@ -441,7 +443,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
* @throws IllegalStateException if the current context is different than the one owned by
* the scene, or if {@link #acquire(long)} was not called.
*
- * @see RenderParams#getRenderingMode()
+ * @see SessionParams#getRenderingMode()
* @see RenderSession#render(long)
*/
public Result render(boolean freshRender) {
@@ -487,6 +489,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
// first measure the full layout, with EXACTLY to get the size of the
// content as it is inside the decor/dialog
+ @SuppressWarnings("deprecation")
Pair<Integer, Integer> exactMeasure = measureView(
mViewRoot, mContentRoot.getChildAt(0),
mMeasuredScreenWidth, MeasureSpec.EXACTLY,
@@ -494,6 +497,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
// now measure the content only using UNSPECIFIED (where applicable, based on
// the rendering mode). This will give us the size the content needs.
+ @SuppressWarnings("deprecation")
Pair<Integer, Integer> result = measureView(
mContentRoot, mContentRoot.getChildAt(0),
mMeasuredScreenWidth, widthMeasureSpecMode,
@@ -569,7 +573,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
mCanvas.setDensity(hardwareConfig.getDensity().getDpiValue());
}
- if (freshRender && newImage == false) {
+ if (freshRender && !newImage) {
Graphics2D gc = mImage.createGraphics();
gc.setComposite(AlphaComposite.Src);
@@ -584,7 +588,8 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
mViewRoot.draw(mCanvas);
}
- mViewInfoList = startVisitingViews(mViewRoot, 0, params.getExtendedViewInfoMode());
+ mSystemViewInfoList = visitAllChildren(mViewRoot, 0, params.getExtendedViewInfoMode(),
+ false);
// success!
return SUCCESS.createResult();
@@ -614,6 +619,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
* @param heightMode the MeasureSpec mode to use for the height.
* @return the measured width/height if measuredView is non-null, null otherwise.
*/
+ @SuppressWarnings("deprecation") // For the use of Pair
private Pair<Integer, Integer> measureView(ViewGroup viewToMeasure, View measuredView,
int width, int widthMode, int height, int heightMode) {
int w_spec = MeasureSpec.makeMeasureSpec(width, widthMode);
@@ -644,7 +650,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
BridgeContext context = getContext();
// find the animation file.
- ResourceValue animationResource = null;
+ ResourceValue animationResource;
int animationId = 0;
if (isFrameworkAnimation) {
animationResource = context.getRenderResources().getFrameworkResource(
@@ -734,7 +740,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
// add it to the parentView in the correct location
Result result = addView(parentView, child, index);
- if (result.isSuccess() == false) {
+ if (!result.isSuccess()) {
return result;
}
@@ -804,13 +810,13 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
public void run() {
Result result = moveView(previousParent, newParentView, childView, index,
params);
- if (result.isSuccess() == false) {
+ if (!result.isSuccess()) {
listener.done(result);
}
// ready to do the work, acquire the scene.
result = acquire(250);
- if (result.isSuccess() == false) {
+ if (!result.isSuccess()) {
listener.done(result);
return;
}
@@ -868,7 +874,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
}
Result result = moveView(previousParent, newParentView, childView, index, layoutParams);
- if (result.isSuccess() == false) {
+ if (!result.isSuccess()) {
return result;
}
@@ -1002,7 +1008,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
}
Result result = removeView(parent, childView);
- if (result.isSuccess() == false) {
+ if (!result.isSuccess()) {
return result;
}
@@ -1030,7 +1036,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
private void findBackground(RenderResources resources) {
- if (getParams().isBgColorOverridden() == false) {
+ if (!getParams().isBgColorOverridden()) {
mWindowBackground = resources.findItemInTheme("windowBackground",
true /*isFrameworkAttr*/);
if (mWindowBackground != null) {
@@ -1047,7 +1053,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
boolean windowFullscreen = getBooleanThemeValue(resources,
"windowFullscreen", false /*defaultValue*/);
- if (windowFullscreen == false && mWindowIsFloating == false) {
+ if (!windowFullscreen && !mWindowIsFloating) {
// default value
mStatusBarSize = DEFAULT_STATUS_BAR_HEIGHT;
@@ -1101,7 +1107,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
boolean windowNoTitle = getBooleanThemeValue(resources,
"windowNoTitle", false /*defaultValue*/);
- if (windowNoTitle == false) {
+ if (!windowNoTitle) {
// default size of the window title bar
mTitleBarSize = DEFAULT_TITLE_BAR_HEIGHT;
@@ -1128,7 +1134,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
}
private void findNavigationBar(RenderResources resources, DisplayMetrics metrics) {
- if (hasSoftwareButtons() && mWindowIsFloating == false) {
+ if (hasSoftwareButtons() && !mWindowIsFloating) {
// default value
mNavigationBarSize = 48; // ??
@@ -1142,15 +1148,12 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
int shortSize = hardwareConfig.getScreenHeight();
// compute in dp
- int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT / hardwareConfig.getDensity().getDpiValue();
+ int shortSizeDp = shortSize * DisplayMetrics.DENSITY_DEFAULT /
+ hardwareConfig.getDensity().getDpiValue();
- if (shortSizeDp < 600) {
- // 0-599dp: "phone" UI with bar on the side
- barOnBottom = false;
- } else {
- // 600+dp: "tablet" UI with bar on the bottom
- barOnBottom = true;
- }
+ // 0-599dp: "phone" UI with bar on the side
+ // 600+dp: "tablet" UI with bar on the bottom
+ barOnBottom = shortSizeDp >= 600;
}
if (barOnBottom) {
@@ -1201,13 +1204,15 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
}
/**
- * Post process on a view hierachy that was just inflated.
- * <p/>At the moment this only support TabHost: If {@link TabHost} is detected, look for the
+ * Post process on a view hierarchy that was just inflated.
+ * <p/>
+ * At the moment this only supports TabHost: If {@link TabHost} is detected, look for the
* {@link TabWidget}, and the corresponding {@link FrameLayout} and make new tabs automatically
* based on the content of the {@link FrameLayout}.
* @param view the root view to process.
* @param projectCallback callback to the project.
*/
+ @SuppressWarnings("deprecation") // For the use of Pair
private void postInflateProcess(View view, IProjectCallback projectCallback)
throws PostInflateException {
if (view instanceof TabHost) {
@@ -1310,7 +1315,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
"TabHost requires a TabWidget with id \"android:id/tabs\".\n");
}
- if ((v instanceof TabWidget) == false) {
+ if (!(v instanceof TabWidget)) {
throw new PostInflateException(String.format(
"TabHost requires a TabWidget with id \"android:id/tabs\".\n" +
"View found with id 'tabs' is '%s'", v.getClass().getCanonicalName()));
@@ -1319,12 +1324,14 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
v = tabHost.findViewById(android.R.id.tabcontent);
if (v == null) {
- // TODO: see if we can fake tabs even without the FrameLayout (same below when the framelayout is empty)
+ // TODO: see if we can fake tabs even without the FrameLayout (same below when the frameLayout is empty)
+ //noinspection SpellCheckingInspection
throw new PostInflateException(
"TabHost requires a FrameLayout with id \"android:id/tabcontent\".");
}
- if ((v instanceof FrameLayout) == false) {
+ if (!(v instanceof FrameLayout)) {
+ //noinspection SpellCheckingInspection
throw new PostInflateException(String.format(
"TabHost requires a FrameLayout with id \"android:id/tabcontent\".\n" +
"View found with id 'tabcontent' is '%s'", v.getClass().getCanonicalName()));
@@ -1332,7 +1339,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
FrameLayout content = (FrameLayout)v;
- // now process the content of the framelayout and dynamically create tabs for it.
+ // now process the content of the frameLayout and dynamically create tabs for it.
final int count = content.getChildCount();
// this must be called before addTab() so that the TabHost searches its TabWidget
@@ -1350,13 +1357,13 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
}
});
tabHost.addTab(spec);
- return;
} else {
- // for each child of the framelayout, add a new TabSpec
+ // for each child of the frameLayout, add a new TabSpec
for (int i = 0 ; i < count ; i++) {
View child = content.getChildAt(i);
String tabSpec = String.format("tab_spec%d", i+1);
int id = child.getId();
+ @SuppressWarnings("deprecation")
Pair<ResourceType, String> resource = projectCallback.resolveResourceId(id);
String name;
if (resource != null) {
@@ -1369,50 +1376,159 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
}
}
- private List<ViewInfo> startVisitingViews(View view, int offset, boolean setExtendedInfo) {
- if (view == null) {
- return null;
- }
+ /**
+ * Visits a {@link View} and its children and generate a {@link ViewInfo} containing the
+ * bounds of all the views.
+ *
+ * @param view the root View
+ * @param offset an offset for the view bounds.
+ * @param setExtendedInfo whether to set the extended view info in the {@link ViewInfo} object.
+ * @param isContentFrame {@code true} if the {@code ViewInfo} to be created is part of the
+ * content frame.
+ *
+ * @return {@code ViewInfo} containing the bounds of the view and it children otherwise.
+ */
+ private ViewInfo visit(View view, int offset, boolean setExtendedInfo,
+ boolean isContentFrame) {
+ ViewInfo result = createViewInfo(view, offset, setExtendedInfo, isContentFrame);
- // adjust the offset to this view.
- offset += view.getTop();
+ if (view instanceof ViewGroup) {
+ ViewGroup group = ((ViewGroup) view);
+ result.setChildren(visitAllChildren(group, isContentFrame ? 0 : offset,
+ setExtendedInfo, isContentFrame));
+ }
+ return result;
+ }
- if (view == mContentRoot) {
- return visitAllChildren(mContentRoot, offset, setExtendedInfo);
+ /**
+ * Visits all the children of a given ViewGroup and generates a list of {@link ViewInfo}
+ * containing the bounds of all the views. It also initializes the {@link #mViewInfoList} with
+ * the children of the {@code mContentRoot}.
+ *
+ * @param viewGroup the root View
+ * @param offset an offset from the top for the content view frame.
+ * @param setExtendedInfo whether to set the extended view info in the {@link ViewInfo} object.
+ * @param isContentFrame {@code true} if the {@code ViewInfo} to be created is part of the
+ * content frame. {@code false} if the {@code ViewInfo} to be created is
+ * part of the system decor.
+ */
+ private List<ViewInfo> visitAllChildren(ViewGroup viewGroup, int offset,
+ boolean setExtendedInfo, boolean isContentFrame) {
+ if (viewGroup == null) {
+ return null;
}
- // otherwise, look for mContentRoot in the children
- if (view instanceof ViewGroup) {
- ViewGroup group = ((ViewGroup) view);
+ if (!isContentFrame) {
+ offset += viewGroup.getTop();
+ }
- for (int i = 0; i < group.getChildCount(); i++) {
- List<ViewInfo> list = startVisitingViews(group.getChildAt(i), offset,
+ int childCount = viewGroup.getChildCount();
+ if (viewGroup == mContentRoot) {
+ List<ViewInfo> childrenWithoutOffset = new ArrayList<ViewInfo>(childCount);
+ List<ViewInfo> childrenWithOffset = new ArrayList<ViewInfo>(childCount);
+ for (int i = 0; i < childCount; i++) {
+ ViewInfo[] childViewInfo = visitContentRoot(viewGroup.getChildAt(i), offset,
setExtendedInfo);
- if (list != null) {
- return list;
- }
+ childrenWithoutOffset.add(childViewInfo[0]);
+ childrenWithOffset.add(childViewInfo[1]);
}
+ mViewInfoList = childrenWithOffset;
+ return childrenWithoutOffset;
+ } else {
+ List<ViewInfo> children = new ArrayList<ViewInfo>(childCount);
+ for (int i = 0; i < childCount; i++) {
+ children.add(visit(viewGroup.getChildAt(i), offset, setExtendedInfo,
+ isContentFrame));
+ }
+ return children;
}
+ }
- return null;
+ /**
+ * Visits the children of {@link #mContentRoot} and generates {@link ViewInfo} containing the
+ * bounds of all the views. It returns two {@code ViewInfo} objects with the same children,
+ * one with the {@code offset} and other without the {@code offset}. The offset is needed to
+ * get the right bounds if the {@code ViewInfo} hierarchy is accessed from
+ * {@code mViewInfoList}. When the hierarchy is accessed via {@code mSystemViewInfoList}, the
+ * offset is not needed.
+ *
+ * @return an array of length two, with ViewInfo at index 0 is without offset and ViewInfo at
+ * index 1 is with the offset.
+ */
+ private ViewInfo[] visitContentRoot(View view, int offset, boolean setExtendedInfo) {
+ ViewInfo[] result = new ViewInfo[2];
+ if (view == null) {
+ return result;
+ }
+
+ result[0] = createViewInfo(view, 0, setExtendedInfo, true);
+ result[1] = createViewInfo(view, offset, setExtendedInfo, true);
+ if (view instanceof ViewGroup) {
+ List<ViewInfo> children = visitAllChildren((ViewGroup) view, 0, setExtendedInfo, true);
+ result[0].setChildren(children);
+ result[1].setChildren(children);
+ }
+ return result;
}
/**
- * Visits a View and its children and generate a {@link ViewInfo} containing the
- * bounds of all the views.
- * @param view the root View
- * @param offset an offset for the view bounds.
- * @param setExtendedInfo whether to set the extended view info in the {@link ViewInfo} object.
+ * Creates a {@link ViewInfo} for the view. The {@code ViewInfo} corresponding to the children
+ * of the {@code view} are not created. Consequently, the children of {@code ViewInfo} is not
+ * set.
+ * @param offset an offset for the view bounds. Used only if view is part of the content frame.
*/
- private ViewInfo visit(View view, int offset, boolean setExtendedInfo) {
+ private ViewInfo createViewInfo(View view, int offset, boolean setExtendedInfo,
+ boolean isContentFrame) {
if (view == null) {
return null;
}
- ViewInfo result = new ViewInfo(view.getClass().getName(),
- getContext().getViewKey(view),
- view.getLeft(), view.getTop() + offset, view.getRight(), view.getBottom() + offset,
- view, view.getLayoutParams());
+ ViewInfo result;
+ if (isContentFrame) {
+ // The view is part of the layout added by the user. Hence,
+ // the ViewCookie may be obtained only through the Context.
+ result = new ViewInfo(view.getClass().getName(),
+ getContext().getViewKey(view),
+ view.getLeft(), view.getTop() + offset, view.getRight(),
+ view.getBottom() + offset, view, view.getLayoutParams());
+ } else {
+ // We are part of the system decor.
+ SystemViewInfo r = new SystemViewInfo(view.getClass().getName(),
+ getViewKey(view),
+ view.getLeft(), view.getTop(), view.getRight(),
+ view.getBottom(), view, view.getLayoutParams());
+ result = r;
+ // We currently mark three kinds of views:
+ // 1. Menus in the Action Bar
+ // 2. Menus in the Overflow popup.
+ // 3. The overflow popup button.
+ if (view instanceof ListMenuItemView) {
+ // Mark 2.
+ // All menus in the popup are of type ListMenuItemView.
+ r.setViewType(ViewType.ACTION_BAR_OVERFLOW_MENU);
+ } else {
+ // Mark 3.
+ ViewGroup.LayoutParams lp = view.getLayoutParams();
+ if (lp instanceof ActionMenuView.LayoutParams &&
+ ((ActionMenuView.LayoutParams) lp).isOverflowButton) {
+ r.setViewType(ViewType.ACTION_BAR_OVERFLOW);
+ } else {
+ // Mark 1.
+ // A view is a menu in the Action Bar is it is not the overflow button and of
+ // its parent is of type ActionMenuView. We can also check if the view is
+ // instanceof ActionMenuItemView but that will fail for menus using
+ // actionProviderClass.
+ ViewParent parent = view.getParent();
+ while (parent != mViewRoot && parent instanceof ViewGroup) {
+ if (parent instanceof ActionMenuView) {
+ r.setViewType(ViewType.ACTION_BAR_MENU);
+ break;
+ }
+ parent = parent.getParent();
+ }
+ }
+ }
+ }
if (setExtendedInfo) {
MarginLayoutParams marginParams = null;
@@ -1427,37 +1543,92 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
marginParams != null ? marginParams.bottomMargin : 0);
}
- if (view instanceof ViewGroup) {
- ViewGroup group = ((ViewGroup) view);
- result.setChildren(visitAllChildren(group, 0 /*offset*/, setExtendedInfo));
+ return result;
+ }
+
+ /* (non-Javadoc)
+ * The cookie for menu items are stored in menu item and not in the map from View stored in
+ * BridgeContext.
+ */
+ private Object getViewKey(View view) {
+ BridgeContext context = getContext();
+ if (!(view instanceof MenuView.ItemView)) {
+ return context.getViewKey(view);
+ }
+ MenuItemImpl menuItem;
+ if (view instanceof ActionMenuItemView) {
+ menuItem = ((ActionMenuItemView) view).getItemData();
+ } else if (view instanceof ListMenuItemView) {
+ menuItem = ((ListMenuItemView) view).getItemData();
+ } else if (view instanceof IconMenuItemView) {
+ menuItem = ((IconMenuItemView) view).getItemData();
+ } else {
+ menuItem = null;
+ }
+ if (menuItem instanceof BridgeMenuItemImpl) {
+ return ((BridgeMenuItemImpl) menuItem).getViewCookie();
}
- return result;
+ return null;
+ }
+
+ private void invalidateRenderingSize() {
+ mMeasuredScreenWidth = mMeasuredScreenHeight = -1;
}
/**
- * Visits all the children of a given ViewGroup generate a list of {@link ViewInfo}
- * containing the bounds of all the views.
- * @param view the root View
- * @param offset an offset for the view bounds.
- * @param setExtendedInfo whether to set the extended view info in the {@link ViewInfo} object.
+ * Creates the status bar with wifi and battery icons.
*/
- private List<ViewInfo> visitAllChildren(ViewGroup viewGroup, int offset,
- boolean setExtendedInfo) {
- if (viewGroup == null) {
- return null;
- }
+ private StatusBar createStatusBar(BridgeContext context, Density density, int direction,
+ boolean isRtlSupported, int platformVersion) throws XmlPullParserException {
+ StatusBar statusBar = new StatusBar(context, density,
+ direction, isRtlSupported, platformVersion);
+ statusBar.setLayoutParams(
+ new LinearLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT, mStatusBarSize));
+ return statusBar;
+ }
- List<ViewInfo> children = new ArrayList<ViewInfo>();
- for (int i = 0; i < viewGroup.getChildCount(); i++) {
- children.add(visit(viewGroup.getChildAt(i), offset, setExtendedInfo));
+ /**
+ * Creates the navigation bar with back, home and recent buttons.
+ *
+ * @param isRtl true if the current locale is right-to-left
+ * @param isRtlSupported true is the project manifest declares that the application
+ * is RTL aware.
+ */
+ private NavigationBar createNavigationBar(BridgeContext context, Density density,
+ boolean isRtl, boolean isRtlSupported, int simulatedPlatformVersion)
+ throws XmlPullParserException {
+ NavigationBar navigationBar = new NavigationBar(context,
+ density, mNavigationBarOrientation, isRtl,
+ isRtlSupported, simulatedPlatformVersion);
+ if (mNavigationBarOrientation == LinearLayout.VERTICAL) {
+ navigationBar.setLayoutParams(new LinearLayout.LayoutParams(mNavigationBarSize,
+ LayoutParams.MATCH_PARENT));
+ } else {
+ navigationBar.setLayoutParams(new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
+ mNavigationBarSize));
}
- return children;
+ return navigationBar;
}
+ private TitleBar createTitleBar(BridgeContext context, String title,
+ int simulatedPlatformVersion)
+ throws XmlPullParserException {
+ TitleBar titleBar = new TitleBar(context, title, simulatedPlatformVersion);
+ titleBar.setLayoutParams(
+ new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, mTitleBarSize));
+ return titleBar;
+ }
- private void invalidateRenderingSize() {
- mMeasuredScreenWidth = mMeasuredScreenHeight = -1;
+ /**
+ * Creates the action bar. Also queries the project callback for missing information.
+ */
+ private ActionBarLayout createActionBar(BridgeContext context, SessionParams params) {
+ ActionBarLayout actionBar = new ActionBarLayout(context, params);
+ actionBar.setLayoutParams(new LinearLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
+ return actionBar;
}
public BufferedImage getImage() {
@@ -1472,6 +1643,10 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
return mViewInfoList;
}
+ public List<ViewInfo> getSystemViewInfos() {
+ return mSystemViewInfoList;
+ }
+
public Map<String, String> getDefaultProperties(Object viewObject) {
return getContext().getDefaultPropMap(viewObject);
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index 6dcb693..22f8e1c 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -16,6 +16,7 @@
package com.android.layoutlib.bridge.impl;
+import com.android.annotations.NonNull;
import com.android.ide.common.rendering.api.DensityBasedResourceValue;
import com.android.ide.common.rendering.api.LayoutLog;
import com.android.ide.common.rendering.api.RenderResources;
@@ -63,11 +64,11 @@ public final class ResourceHelper {
* Returns the color value represented by the given string value
* @param value the color value
* @return the color as an int
- * @throw NumberFormatException if the conversion failed.
+ * @throws NumberFormatException if the conversion failed.
*/
public static int getColor(String value) {
if (value != null) {
- if (value.startsWith("#") == false) {
+ if (!value.startsWith("#")) {
throw new NumberFormatException(
String.format("Color value '%s' must start with #", value));
}
@@ -113,7 +114,7 @@ public final class ResourceHelper {
public static ColorStateList getColorStateList(ResourceValue resValue, BridgeContext context) {
String value = resValue.getValue();
- if (value != null && RenderResources.REFERENCE_NULL.equals(value) == false) {
+ if (value != null && !RenderResources.REFERENCE_NULL.equals(value)) {
// first check if the value is a file (xml most likely)
File f = new File(value);
if (f.isFile()) {
@@ -165,6 +166,9 @@ public final class ResourceHelper {
* @param context the current context
*/
public static Drawable getDrawable(ResourceValue value, BridgeContext context) {
+ if (value == null) {
+ return null;
+ }
String stringValue = value.getValue();
if (RenderResources.REFERENCE_NULL.equals(stringValue)) {
return null;
@@ -355,9 +359,9 @@ public final class ResourceHelper {
* @param requireUnit whether the value is expected to contain a unit.
* @return true if success.
*/
- public static boolean parseFloatAttribute(String attribute, String value,
+ public static boolean parseFloatAttribute(String attribute, @NonNull String value,
TypedValue outValue, boolean requireUnit) {
- assert requireUnit == false || attribute != null;
+ assert !requireUnit || attribute != null;
// remove the space before and after
value = value.trim();
@@ -376,7 +380,7 @@ public final class ResourceHelper {
}
// check the first character
- if (buf[0] < '0' && buf[0] > '9' && buf[0] != '.' && buf[0] != '-') {
+ if ((buf[0] < '0' || buf[0] > '9') && buf[0] != '.' && buf[0] != '-' && buf[0] != '+') {
return false;
}
@@ -408,7 +412,7 @@ public final class ResourceHelper {
if (end.length() == 0) {
if (outValue != null) {
- if (requireUnit == false) {
+ if (!requireUnit) {
outValue.type = TypedValue.TYPE_FLOAT;
outValue.data = Float.floatToIntBits(f);
} else {
@@ -486,6 +490,8 @@ public final class ResourceHelper {
private static void applyUnit(UnitEntry unit, TypedValue outValue, float[] outScale) {
outValue.type = unit.type;
+ // COMPLEX_UNIT_SHIFT is 0 and hence intelliJ complains about it. Suppress the warning.
+ //noinspection PointlessBitwiseExpression
outValue.data = unit.unit << TypedValue.COMPLEX_UNIT_SHIFT;
outScale[0] = unit.scale;
}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/SystemViewInfo.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/SystemViewInfo.java
new file mode 100644
index 0000000..9fea167
--- /dev/null
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/SystemViewInfo.java
@@ -0,0 +1,51 @@
+/*
+ * 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.ViewInfo;
+import com.android.ide.common.rendering.api.ViewType;
+
+/**
+ * ViewInfo for views added by the platform.
+ */
+public class SystemViewInfo extends ViewInfo {
+
+ private ViewType mViewType;
+
+ public SystemViewInfo(String name, Object cookie, int left, int top,
+ int right, int bottom) {
+ super(name, cookie, left, top, right, bottom);
+ }
+
+ public SystemViewInfo(String name, Object cookie, int left, int top,
+ int right, int bottom, Object viewObject, Object layoutParamsObject) {
+ super(name, cookie, left, top, right, bottom, viewObject,
+ layoutParamsObject);
+ }
+
+ @Override
+ public ViewType getViewType() {
+ if (mViewType != null) {
+ return mViewType;
+ }
+ return ViewType.SYSTEM_UNKNOWN;
+ }
+
+ public void setViewType(ViewType type) {
+ mViewType = type;
+ }
+}
diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/SparseWeakArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/SparseWeakArray.java
index 53e1640..a2a8aa9 100644
--- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/SparseWeakArray.java
+++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/util/SparseWeakArray.java
@@ -18,6 +18,7 @@ package com.android.layoutlib.bridge.util;
import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.GrowingArrayUtils;
import android.util.SparseArray;
@@ -59,10 +60,8 @@ public class SparseWeakArray<E> {
* number of mappings.
*/
public SparseWeakArray(int initialCapacity) {
- initialCapacity = ArrayUtils.idealLongArraySize(initialCapacity);
-
- mKeys = new long[initialCapacity];
- mValues = new WeakReference[initialCapacity];
+ mKeys = ArrayUtils.newUnpaddedLongArray(initialCapacity);
+ mValues = new WeakReference[mKeys.length];
mSize = 0;
}
@@ -142,18 +141,6 @@ public class SparseWeakArray<E> {
mGarbage = false;
mSize = o;
-
- int newSize = ArrayUtils.idealLongArraySize(mSize);
- if (newSize < mKeys.length) {
- long[] nkeys = new long[newSize];
- WeakReference<?>[] nvalues = new WeakReference[newSize];
-
- System.arraycopy(mKeys, 0, nkeys, 0, newSize);
- System.arraycopy(mValues, 0, nvalues, 0, newSize);
-
- mKeys = nkeys;
- mValues = nvalues;
- }
}
/**
@@ -182,28 +169,8 @@ public class SparseWeakArray<E> {
i = ~binarySearch(mKeys, 0, mSize, key);
}
- if (mSize >= mKeys.length) {
- int n = ArrayUtils.idealLongArraySize(mSize + 1);
-
- long[] nkeys = new long[n];
- WeakReference<?>[] nvalues = new WeakReference[n];
-
- // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
- System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
- System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
-
- mKeys = nkeys;
- mValues = nvalues;
- }
-
- if (mSize - i != 0) {
- // Log.e("SparseArray", "move " + (mSize - i));
- System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
- System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
- }
-
- mKeys[i] = key;
- mValues[i] = new WeakReference(value);
+ mKeys = GrowingArrayUtils.insert(mKeys, mSize, i, key);
+ mValues = GrowingArrayUtils.insert(mValues, mSize, i, new WeakReference(value));
mSize++;
}
}
@@ -321,24 +288,9 @@ public class SparseWeakArray<E> {
gc();
}
- int pos = mSize;
- if (pos >= mKeys.length) {
- int n = ArrayUtils.idealLongArraySize(pos + 1);
-
- long[] nkeys = new long[n];
- WeakReference<?>[] nvalues = new WeakReference[n];
-
- // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
- System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
- System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
-
- mKeys = nkeys;
- mValues = nvalues;
- }
-
- mKeys[pos] = key;
- mValues[pos] = new WeakReference(value);
- mSize = pos + 1;
+ mKeys = GrowingArrayUtils.append(mKeys, mSize, key);
+ mValues = GrowingArrayUtils.append(mValues, mSize, new WeakReference(value));
+ mSize++;
}
private boolean hasReclaimedRefs() {
diff --git a/tools/layoutlib/bridge/src/dalvik/system/VMRuntime_Delegate.java b/tools/layoutlib/bridge/src/dalvik/system/VMRuntime_Delegate.java
new file mode 100644
index 0000000..36efc3a
--- /dev/null
+++ b/tools/layoutlib/bridge/src/dalvik/system/VMRuntime_Delegate.java
@@ -0,0 +1,78 @@
+/*
+ * 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 dalvik.system;
+
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+/**
+ * Delegate used to provide implementation of a select few native methods of {@link VMRuntime}
+ * <p/>
+ * Through the layoutlib_create tool, the original native methods of VMRuntime have been replaced
+ * by calls to methods of the same name in this delegate class.
+ */
+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");
+ }
+ }
+
+}
diff --git a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
index b3a173f..b8b5fed 100644
--- a/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
+++ b/tools/layoutlib/bridge/src/libcore/icu/ICU_Delegate.java
@@ -18,6 +18,7 @@ package libcore.icu;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import com.ibm.icu.text.DateTimePatternGenerator;
+import com.ibm.icu.util.Currency;
import com.ibm.icu.util.ULocale;
import java.util.Locale;
@@ -117,6 +118,11 @@ public class ICU_Delegate {
}
@LayoutlibDelegate
+ /*package*/ static int getCurrencyNumericCode(String currencyCode) {
+ return Currency.getInstance(currencyCode).getNumericCode();
+ }
+
+ @LayoutlibDelegate
/*package*/ static String getCurrencySymbol(String locale, String currencyCode) {
return "";
}
@@ -142,12 +148,12 @@ public class ICU_Delegate {
}
@LayoutlibDelegate
- /*package*/ static String getISO3CountryNative(String locale) {
+ /*package*/ static String getISO3Country(String locale) {
return "";
}
@LayoutlibDelegate
- /*package*/ static String getISO3LanguageNative(String locale) {
+ /*package*/ static String getISO3Language(String locale) {
return "";
}
@@ -171,17 +177,6 @@ public class ICU_Delegate {
return Locale.getISOCountries();
}
-
- @LayoutlibDelegate
- /*package*/ static String localeForLanguageTag(String languageTag, boolean strict) {
- return "";
- }
-
- @LayoutlibDelegate
- /*package*/ static String languageTagForLocale(String locale) {
- return "";
- }
-
@LayoutlibDelegate
/*package*/ static boolean initLocaleDataNative(String locale, LocaleData result) {
diff --git a/tools/layoutlib/bridge/tests/Android.mk b/tools/layoutlib/bridge/tests/Android.mk
index 98cade9..11390c3 100644
--- a/tools/layoutlib/bridge/tests/Android.mk
+++ b/tools/layoutlib/bridge/tests/Android.mk
@@ -18,15 +18,24 @@ include $(CLEAR_VARS)
# Only compile source java files in this lib.
LOCAL_SRC_FILES := $(call all-java-files-under, src)
-
LOCAL_JAVA_RESOURCE_DIRS := res
+LOCAL_JAVACFLAGS := -source 6 -target 6
LOCAL_MODULE := layoutlib-tests
LOCAL_MODULE_TAGS := optional
-LOCAL_JAVA_LIBRARIES := layoutlib kxml2-2.3.0 junit
+LOCAL_JAVA_LIBRARIES := layoutlib \
+ kxml2-2.3.0 \
+ icu4j \
+ layoutlib_api-prebuilt \
+ tools-common-prebuilt \
+ sdk-common \
+ junit
include $(BUILD_HOST_JAVA_LIBRARY)
+# Copy the jar to DIST_DIR for sdk builds
+$(call dist-for-goals, sdk win_sdk, $(LOCAL_INSTALLED_MODULE))
+
# Build all sub-directories
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/.gitignore b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/.gitignore
new file mode 100644
index 0000000..a2ce0dc
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/.gitignore
@@ -0,0 +1,14 @@
+.gradle
+local.properties
+.idea
+.DS_Store
+*.iml
+# We need the built .class files to load custom views and R class.
+# The only way to negate an exclusion is by including every single parent
+# and excluding all children of those parents.
+
+/build/*
+!/build/intermediates/
+
+/build/intermediates/*
+!/build/intermediates/classes/
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build.gradle b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build.gradle
new file mode 100644
index 0000000..80be12d
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build.gradle
@@ -0,0 +1,43 @@
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:0.12.+'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
+
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 20
+ buildToolsVersion '20'
+ defaultConfig {
+ applicationId 'com.android.layoutlib.test.myapplication'
+ minSdkVersion 19
+ targetSdkVersion 20
+ versionCode 1
+ versionName '1.0'
+ }
+ buildTypes {
+ release {
+ runProguard false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+ productFlavors {
+ }
+}
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+}
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/BuildConfig.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/BuildConfig.class
new file mode 100644
index 0000000..2b4f7bf
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/BuildConfig.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class
new file mode 100644
index 0000000..d252462
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/MyActivity.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$attr.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$attr.class
new file mode 100644
index 0000000..9bab801
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$attr.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class
new file mode 100644
index 0000000..7ad8605
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$dimen.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$drawable.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$drawable.class
new file mode 100644
index 0000000..e9e0a33
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$drawable.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class
new file mode 100644
index 0000000..d109302
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$id.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class
new file mode 100644
index 0000000..816ecc8
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$layout.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class
new file mode 100644
index 0000000..b034b75
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$menu.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class
new file mode 100644
index 0000000..f86b1d3
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$string.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class
new file mode 100644
index 0000000..8bbae90
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R$style.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class
new file mode 100644
index 0000000..8af745d
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/build/intermediates/classes/debug/com/android/layoutlib/test/myapplication/R.class
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradle.properties b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradle.properties
new file mode 100644
index 0000000..5d08ba7
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradle.properties
@@ -0,0 +1,18 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Settings specified in this file will override any Gradle settings
+# configured through the IDE.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+# Default value: -Xmx10248m -XX:MaxPermSize=256m
+# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true \ No newline at end of file
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradle/wrapper/gradle-wrapper.jar b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..8c0fb64
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradle/wrapper/gradle-wrapper.properties b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..5de946b
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Wed Apr 10 15:27:10 PDT 2013
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-all.zip
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradlew b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradlew.bat b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/proguard-rules.pro b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/proguard-rules.pro
new file mode 100644
index 0000000..b0fcd2d
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in /usr/local/google/home/deepanshu/ssd/sdk_out/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/androidTest/java/com/android/layoulib/test/myapplication/ApplicationTest.java b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/androidTest/java/com/android/layoulib/test/myapplication/ApplicationTest.java
new file mode 100644
index 0000000..7304af1
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/androidTest/java/com/android/layoulib/test/myapplication/ApplicationTest.java
@@ -0,0 +1,13 @@
+package com.android.layoulib.test.myapplication;
+
+import android.app.Application;
+import android.test.ApplicationTestCase;
+
+/**
+ * <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
+ */
+public class ApplicationTest extends ApplicationTestCase<Application> {
+ public ApplicationTest() {
+ super(Application.class);
+ }
+} \ No newline at end of file
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/AndroidManifest.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..2067474
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/AndroidManifest.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.layoutlib.test.myapplication" >
+<!-- If changing package here, update LayoutLibCallBack in tests. -->
+ <application
+ android:allowBackup="true"
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name"
+ android:theme="@style/AppTheme" >
+ <activity
+ android:name="com.android.layoutlib.test.myapplication.MyActivity"
+ android:label="@string/app_name" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/MyActivity.java b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/MyActivity.java
new file mode 100644
index 0000000..59de457
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/java/com/android/layoutlib/test/myapplication/MyActivity.java
@@ -0,0 +1,35 @@
+package com.android.layoutlib.test.myapplication;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+
+public class MyActivity extends Activity {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity);
+ }
+
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ // Inflate the menu; this adds items to the action bar if it is present.
+ getMenuInflater().inflate(R.menu.my, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // Handle action bar item clicks here. The action bar will
+ // automatically handle clicks on the Home/Up button, so long
+ // as you specify a parent activity in AndroidManifest.xml.
+ int id = item.getItemId();
+ if (id == R.id.action_settings) {
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/ic_launcher.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/ic_launcher.xml
new file mode 100644
index 0000000..67481d4
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/drawable/ic_launcher.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle">
+ <solid android:color="#ff0000" />
+ <size
+ android:width="20dp"
+ android:height="20dp" />
+</shape> \ No newline at end of file
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/activity.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/activity.xml
new file mode 100644
index 0000000..97d1983
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/activity.xml
@@ -0,0 +1,21 @@
+<RelativeLayout 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:paddingLeft="@dimen/activity_horizontal_margin"
+ android:paddingRight="@dimen/activity_horizontal_margin"
+ android:paddingTop="@dimen/activity_vertical_margin"
+ android:paddingBottom="@dimen/activity_vertical_margin"
+ tools:context=".MyActivity">
+
+ <TextView
+ android:text="@string/hello_world"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:id="@+id/text1"/>
+
+ <include layout="@layout/layout"
+ android:layout_below="@+id/text1"
+ android:layout_height="wrap_content"
+ android:layout_width="wrap_content" />
+</RelativeLayout>
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml
new file mode 100644
index 0000000..2704c07
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/layout/layout.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical" android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Some text"/>
+</LinearLayout> \ No newline at end of file
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/menu/my.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/menu/my.xml
new file mode 100644
index 0000000..bea58cc
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/menu/my.xml
@@ -0,0 +1,8 @@
+<menu xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:tools="http://schemas.android.com/tools"
+ tools:context=".MyActivity" >
+ <item android:id="@+id/action_settings"
+ android:title="@string/action_settings"
+ android:orderInCategory="100"
+ android:showAsAction="never" />
+</menu>
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/dimens.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+<resources>
+ <!-- Default screen margins, per the Android Design guidelines. -->
+ <dimen name="activity_horizontal_margin">16dp</dimen>
+ <dimen name="activity_vertical_margin">16dp</dimen>
+</resources>
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/strings.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/strings.xml
new file mode 100644
index 0000000..2b7083b
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/strings.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+ <string name="app_name">My Application</string>
+ <string name="hello_world">Hello world!</string>
+ <string name="action_settings">Settings</string>
+
+</resources>
diff --git a/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml
new file mode 100644
index 0000000..ff6c9d2
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml
@@ -0,0 +1,8 @@
+<resources>
+
+ <!-- Base application theme. -->
+ <style name="AppTheme" parent="android:Theme.Holo.Light.DarkActionBar">
+ <!-- Customize your theme here. -->
+ </style>
+
+</resources>
diff --git a/tools/layoutlib/bridge/tests/src/android/graphics/Matrix_DelegateTest.java b/tools/layoutlib/bridge/tests/src/android/graphics/Matrix_DelegateTest.java
index ec4edac..d20fb14 100644
--- a/tools/layoutlib/bridge/tests/src/android/graphics/Matrix_DelegateTest.java
+++ b/tools/layoutlib/bridge/tests/src/android/graphics/Matrix_DelegateTest.java
@@ -23,16 +23,6 @@ import junit.framework.TestCase;
*/
public class Matrix_DelegateTest extends TestCase {
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- }
-
public void testIdentity() {
Matrix m1 = new Matrix();
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java
index d3218db..8b362ec 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/TestDelegates.java
@@ -41,27 +41,29 @@ import junit.framework.TestCase;
*/
public class TestDelegates extends TestCase {
+ private List<String> mErrors = new ArrayList<String>();
+
public void testNativeDelegates() {
final String[] classes = CreateInfo.DELEGATE_CLASS_NATIVES;
- final int count = classes.length;
- for (int i = 0 ; i < count ; i++) {
- loadAndCompareClasses(classes[i], classes[i] + "_Delegate");
+ mErrors.clear();
+ for (String clazz : classes) {
+ loadAndCompareClasses(clazz, clazz + "_Delegate");
}
+ assertTrue(getErrors(), mErrors.isEmpty());
}
public void testMethodDelegates() {
final String[] methods = CreateInfo.DELEGATE_METHODS;
- final int count = methods.length;
- for (int i = 0 ; i < count ; i++) {
- String methodName = methods[i];
-
+ mErrors.clear();
+ for (String methodName : methods) {
// extract the class name
String className = methodName.substring(0, methodName.indexOf('#'));
String targetClassName = className.replace('$', '_') + "_Delegate";
loadAndCompareClasses(className, targetClassName);
}
+ assertTrue(getErrors(), mErrors.isEmpty());
}
private void loadAndCompareClasses(String originalClassName, String delegateClassName) {
@@ -73,9 +75,9 @@ public class TestDelegates extends TestCase {
compare(originalClass, delegateClass);
} catch (ClassNotFoundException e) {
- fail("Failed to load class: " + e.getMessage());
+ mErrors.add("Failed to load class: " + e.getMessage());
} catch (SecurityException e) {
- fail("Failed to load class: " + e.getMessage());
+ mErrors.add("Failed to load class: " + e.getMessage());
}
}
@@ -121,27 +123,40 @@ public class TestDelegates extends TestCase {
Method delegateMethod = delegateClass.getDeclaredMethod(originalMethod.getName(),
parameters);
+ // check the return type of the methods match.
+ if (delegateMethod.getReturnType() != originalMethod.getReturnType()) {
+ mErrors.add(
+ String.format("Delegate method %1$s.%2$s does not match the " +
+ "corresponding framework method which returns %3$s",
+ delegateClass.getName(),
+ getMethodName(delegateMethod),
+ originalMethod.getReturnType().getName()));
+ }
+
// check that the method has the annotation
- assertNotNull(
- String.format(
- "Delegate method %1$s for class %2$s does not have the @LayoutlibDelegate annotation",
- delegateMethod.getName(),
- originalClass.getName()),
- delegateMethod.getAnnotation(LayoutlibDelegate.class));
+ if (delegateMethod.getAnnotation(LayoutlibDelegate.class) == null) {
+ mErrors.add(
+ String.format("Delegate method %1$s for class %2$s does not have the " +
+ "@LayoutlibDelegate annotation",
+ delegateMethod.getName(),
+ originalClass.getName()));
+ }
// check that the method is static
- assertTrue(
- String.format(
- "Delegate method %1$s for class %2$s is not static",
- delegateMethod.getName(),
- originalClass.getName()),
- (delegateMethod.getModifiers() & Modifier.STATIC) == Modifier.STATIC);
+ if ((delegateMethod.getModifiers() & Modifier.STATIC) != Modifier.STATIC) {
+ mErrors.add(
+ String.format(
+ "Delegate method %1$s for class %2$s is not static",
+ delegateMethod.getName(),
+ originalClass.getName())
+ );
+ }
// add the method as checked.
checkedDelegateMethods.add(delegateMethod);
} catch (NoSuchMethodException e) {
String name = getMethodName(originalMethod, parameters);
- fail(String.format("Missing %1$s.%2$s", delegateClass.getName(), name));
+ mErrors.add(String.format("Missing %1$s.%2$s", delegateClass.getName(), name));
}
}
@@ -158,12 +173,12 @@ public class TestDelegates extends TestCase {
continue;
}
- assertTrue(
- String.format(
- "Delegate method %1$s.%2$s is not used anymore and must be removed",
- delegateClass.getName(),
- getMethodName(delegateMethod)),
- checkedDelegateMethods.contains(delegateMethod));
+ if (!checkedDelegateMethods.contains(delegateMethod)) {
+ mErrors.add(String.format(
+ "Delegate method %1$s.%2$s is not used anymore and must be removed",
+ delegateClass.getName(),
+ getMethodName(delegateMethod)));
+ }
}
}
@@ -194,4 +209,12 @@ public class TestDelegates extends TestCase {
return sb.toString();
}
+
+ private String getErrors() {
+ StringBuilder s = new StringBuilder();
+ for (String error : mErrors) {
+ s.append(error).append('\n');
+ }
+ return s.toString();
+ }
}
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
index 865a008..92fcf90 100644
--- a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java
@@ -24,17 +24,6 @@ import org.xmlpull.v1.XmlPullParser;
import junit.framework.TestCase;
public class BridgeXmlBlockParserTest extends TestCase {
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- }
-
- @Override
- protected void tearDown() throws Exception {
- super.tearDown();
- }
-
public void testXmlBlockParser() throws Exception {
XmlPullParser parser = ParserFactory.create(
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
new file mode 100644
index 0000000..a2588a6
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/Main.java
@@ -0,0 +1,374 @@
+/*
+ * 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.intensive;
+
+import com.android.annotations.NonNull;
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.RenderSession;
+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.resources.FrameworkResources;
+import com.android.ide.common.resources.ResourceItem;
+import com.android.ide.common.resources.ResourceRepository;
+import com.android.ide.common.resources.ResourceResolver;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.io.FolderWrapper;
+import com.android.layoutlib.bridge.Bridge;
+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.utils.ILogger;
+
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Comparator;
+
+import static org.junit.Assert.fail;
+
+/**
+ * This is a set of tests that loads all the framework resources and a project checked in this
+ * test's resources. The main dependencies
+ * are:
+ * 1. Fonts directory.
+ * 2. Framework Resources.
+ * 3. App resources.
+ * 4. build.prop file
+ *
+ * These are configured by two variables set in the system properties.
+ *
+ * 1. platform.dir: This is the directory for the current platform in the built SDK
+ * (.../sdk/platforms/android-<version>).
+ *
+ * The fonts are platform.dir/data/fonts.
+ * The Framework resources are platform.dir/data/res.
+ * build.prop is at platform.dir/build.prop.
+ *
+ * 2. test_res.dir: This is the directory for the resources of the test. If not specified, this
+ * falls back to getClass().getProtectionDomain().getCodeSource().getLocation()
+ *
+ * The app resources are at: test_res.dir/testApp/MyApplication/app/src/main/res
+ */
+public class Main {
+
+ private static final String PLATFORM_DIR_PROPERTY = "platform.dir";
+ private static final String RESOURCE_DIR_PROPERTY = "test_res.dir";
+
+ private static final String PLATFORM_DIR;
+ private static final String TEST_RES_DIR;
+ private static final String APP_TEST_RES = "/testApp/MyApplication/src/main/res";
+
+ private LayoutLog mLayoutLibLog;
+ private FrameworkResources mFrameworkRepo;
+ private ResourceRepository mProjectResources;
+ private ILogger mLogger;
+ private Bridge mBridge;
+
+ static {
+ // Test that System Properties are properly set.
+ PLATFORM_DIR = getPlatformDir();
+ if (PLATFORM_DIR == null) {
+ fail(String.format("System Property %1$s not properly set. The value is %2$s",
+ PLATFORM_DIR_PROPERTY, System.getProperty(PLATFORM_DIR_PROPERTY)));
+ }
+
+ 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",
+ RESOURCE_DIR_PROPERTY, System.getProperty(RESOURCE_DIR_PROPERTY)));
+ }
+ }
+
+ private static String getPlatformDir() {
+ String platformDir = System.getProperty(PLATFORM_DIR_PROPERTY);
+ if (platformDir != null && !platformDir.isEmpty() && new File(platformDir).isDirectory()) {
+ return platformDir;
+ }
+ // System Property not set. Try to find the directory in the build directory.
+ String androidHostOut = System.getenv("ANDROID_HOST_OUT");
+ if (androidHostOut != null) {
+ platformDir = getPlatformDirFromHostOut(new File(androidHostOut));
+ if (platformDir != null) {
+ return platformDir;
+ }
+ }
+ String workingDirString = System.getProperty("user.dir");
+ File workingDir = new File(workingDirString);
+ // Test if workingDir is android checkout root.
+ platformDir = getPlatformDirFromRoot(workingDir);
+ if (platformDir != null) {
+ return platformDir;
+ }
+ // Test if workingDir is platform/frameworks/base/tools/layoutlib. That is, root should be
+ // workingDir/../../../../ (4 levels up)
+ File currentDir = workingDir;
+ for (int i = 0; i < 4; i++) {
+ if (currentDir != null) {
+ currentDir = currentDir.getParentFile();
+ }
+ }
+ return currentDir == null ? null : getPlatformDirFromRoot(currentDir);
+ }
+
+ private static String getPlatformDirFromRoot(File root) {
+ if (!root.isDirectory()) {
+ return null;
+ }
+ File out = new File(root, "out");
+ if (!out.isDirectory()) {
+ return null;
+ }
+ File host = new File(out, "host");
+ if (!host.isDirectory()) {
+ return null;
+ }
+ File[] hosts = host.listFiles(new FileFilter() {
+ @Override
+ public boolean accept(File path) {
+ return path.isDirectory() && (path.getName().startsWith("linux-") || path.getName()
+ .startsWith("darwin-"));
+ }
+ });
+ for (File hostOut : hosts) {
+ String platformDir = getPlatformDirFromHostOut(hostOut);
+ if (platformDir != null) {
+ return platformDir;
+ }
+ }
+ return null;
+ }
+
+ private static String getPlatformDirFromHostOut(File out) {
+ if (!out.isDirectory()) {
+ return null;
+ }
+ File sdkDir = new File(out, "sdk" + File.separator + "sdk");
+ if (!sdkDir.isDirectory()) {
+ // The directory we thought that should contain the sdk is not a directory.
+ return null;
+ }
+ File[] possibleSdks = sdkDir.listFiles(new FileFilter() {
+ @Override
+ public boolean accept(File path) {
+ return path.isDirectory() && path.getName().contains("android-sdk");
+ }
+ });
+ for (File possibleSdk : possibleSdks) {
+ File platformsDir = new File(possibleSdk, "platforms");
+ File[] platforms = platformsDir.listFiles(new FileFilter() {
+ @Override
+ public boolean accept(File path) {
+ return path.isDirectory() && path.getName().startsWith("android-");
+ }
+ });
+ if (platforms == null || platforms.length == 0) {
+ continue;
+ }
+ Arrays.sort(platforms, new Comparator<File>() {
+ // Codenames before ints. Higher APIs precede lower.
+ @Override
+ public int compare(File o1, File o2) {
+ final int MAX_VALUE = 1000;
+ String suffix1 = o1.getName().substring("android-".length());
+ String suffix2 = o2.getName().substring("android-".length());
+ int suff1, suff2;
+ try {
+ suff1 = Integer.parseInt(suffix1);
+ } catch (NumberFormatException e) {
+ suff1 = MAX_VALUE;
+ }
+ try {
+ suff2 = Integer.parseInt(suffix2);
+ } catch (NumberFormatException e) {
+ suff2 = MAX_VALUE;
+ }
+ if (suff1 != MAX_VALUE || suff2 != MAX_VALUE) {
+ return suff2 - suff1;
+ }
+ return suffix2.compareTo(suffix1);
+ }
+ });
+ return platforms[0].getAbsolutePath();
+ }
+ return null;
+ }
+
+ private static String getTestResDir() {
+ String resourceDir = System.getProperty(RESOURCE_DIR_PROPERTY);
+ if (resourceDir != null && !resourceDir.isEmpty() && new File(resourceDir).isDirectory()) {
+ return resourceDir;
+ }
+ // TEST_RES_DIR not explicitly set. Fallback to the class's source location.
+ try {
+ URL location = Main.class.getProtectionDomain().getCodeSource().getLocation();
+ return new File(location.getPath()).exists() ? location.getPath() : null;
+ } catch (NullPointerException e) {
+ // Prevent a lot of null checks by just catching the exception.
+ return null;
+ }
+ }
+ /**
+ * Initialize the bridge and the resource maps.
+ */
+ @Before
+ public void setUp() {
+ File data_dir = new File(PLATFORM_DIR, "data");
+ File res = new File(data_dir, "res");
+ mFrameworkRepo = new FrameworkResources(new FolderWrapper(res));
+ mFrameworkRepo.loadResources();
+ mFrameworkRepo.loadPublicResources(getLogger());
+
+ mProjectResources =
+ new ResourceRepository(new FolderWrapper(TEST_RES_DIR + APP_TEST_RES), false) {
+ @NonNull
+ @Override
+ protected ResourceItem createResourceItem(String name) {
+ return new ResourceItem(name);
+ }
+ };
+ mProjectResources.loadResources();
+
+ File fontLocation = new File(data_dir, "fonts");
+ File buildProp = new File(PLATFORM_DIR, "build.prop");
+ File attrs = new File(res, "values" + File.separator + "attrs.xml");
+ mBridge = new Bridge();
+ mBridge.init(ConfigGenerator.loadProperties(buildProp), fontLocation,
+ ConfigGenerator.getEnumMap(attrs), getLayoutLog());
+ }
+
+ /**
+ * Create a new rendering session and test that rendering /layout/activity.xml on nexus 5
+ * doesn't throw any exceptions.
+ */
+ @Test
+ public void testRendering() throws ClassNotFoundException {
+ // Create the layout pull parser.
+ LayoutPullParser parser = new LayoutPullParser(APP_TEST_RES + "/layout/activity.xml");
+ // Create LayoutLibCallback.
+ LayoutLibTestCallback layoutLibCallback = new LayoutLibTestCallback(getLogger());
+ layoutLibCallback.initResources();
+ // TODO: Set up action bar handler properly to test menu rendering.
+ // Create session params.
+ SessionParams params = getSessionParams(parser, ConfigGenerator.NEXUS_5, layoutLibCallback);
+ RenderSession session = mBridge.createSession(params);
+ if (!session.getResult().isSuccess()) {
+ getLogger().error(session.getResult().getException(),
+ session.getResult().getErrorMessage());
+ }
+ // Render the session with a timeout of 50s.
+ Result renderResult = session.render(50000);
+ if (!renderResult.isSuccess()) {
+ getLogger().error(session.getResult().getException(),
+ session.getResult().getErrorMessage());
+ }
+ }
+
+ /**
+ * Uses Theme.Material and Target sdk version as 21.
+ */
+ private SessionParams getSessionParams(LayoutPullParser layoutParser,
+ ConfigGenerator configGenerator, LayoutLibTestCallback layoutLibCallback) {
+ FolderConfiguration config = configGenerator.getFolderConfig();
+ ResourceResolver resourceResolver =
+ ResourceResolver.create(mProjectResources.getConfiguredResources(config),
+ mFrameworkRepo.getConfiguredResources(config), "Theme.Material", false);
+
+ return new SessionParams(
+ layoutParser,
+ RenderingMode.NORMAL,
+ null /*used for caching*/,
+ configGenerator.getHardwareConfig(),
+ resourceResolver,
+ layoutLibCallback,
+ 0,
+ 21, // TODO: Make it more configurable to run tests for various versions.
+ getLayoutLog());
+ }
+
+ private LayoutLog getLayoutLog() {
+ if (mLayoutLibLog == null) {
+ mLayoutLibLog = new LayoutLog() {
+ @Override
+ public void warning(String tag, String message, Object data) {
+ System.out.println("Warning " + tag + ": " + message);
+ fail(message);
+ }
+
+ @Override
+ public void fidelityWarning(String tag, String message, Throwable throwable,
+ Object data) {
+ System.out.println("FidelityWarning " + tag + ": " + message);
+ if (throwable != null) {
+ throwable.printStackTrace();
+ }
+ fail(message);
+ }
+
+ @Override
+ public void error(String tag, String message, Object data) {
+ System.out.println("Error " + tag + ": " + message);
+ fail(message);
+ }
+
+ @Override
+ public void error(String tag, String message, Throwable throwable, Object data) {
+ System.out.println("Error " + tag + ": " + message);
+ if (throwable != null) {
+ throwable.printStackTrace();
+ }
+ fail(message);
+ }
+ };
+ }
+ return mLayoutLibLog;
+ }
+
+ private ILogger getLogger() {
+ if (mLogger == null) {
+ mLogger = new ILogger() {
+ @Override
+ public void error(Throwable t, String msgFormat, Object... args) {
+ if (t != null) {
+ t.printStackTrace();
+ }
+ fail(String.format(msgFormat, args));
+ }
+
+ @Override
+ public void warning(String msgFormat, Object... args) {
+ fail(String.format(msgFormat, args));
+ }
+
+ @Override
+ public void info(String msgFormat, Object... args) {
+ // pass.
+ }
+
+ @Override
+ public void verbose(String msgFormat, Object... args) {
+ // pass.
+ }
+ };
+ }
+ return mLogger;
+ }
+}
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ConfigGenerator.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ConfigGenerator.java
new file mode 100644
index 0000000..a5c3202
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/ConfigGenerator.java
@@ -0,0 +1,294 @@
+/*
+ * 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.intensive.setup;
+
+import com.android.ide.common.rendering.api.HardwareConfig;
+import com.android.ide.common.resources.configuration.CountryCodeQualifier;
+import com.android.ide.common.resources.configuration.DensityQualifier;
+import com.android.ide.common.resources.configuration.FolderConfiguration;
+import com.android.ide.common.resources.configuration.KeyboardStateQualifier;
+import com.android.ide.common.resources.configuration.LanguageQualifier;
+import com.android.ide.common.resources.configuration.LayoutDirectionQualifier;
+import com.android.ide.common.resources.configuration.NavigationMethodQualifier;
+import com.android.ide.common.resources.configuration.NetworkCodeQualifier;
+import com.android.ide.common.resources.configuration.NightModeQualifier;
+import com.android.ide.common.resources.configuration.RegionQualifier;
+import com.android.ide.common.resources.configuration.ScreenDimensionQualifier;
+import com.android.ide.common.resources.configuration.ScreenOrientationQualifier;
+import com.android.ide.common.resources.configuration.ScreenRatioQualifier;
+import com.android.ide.common.resources.configuration.ScreenSizeQualifier;
+import com.android.ide.common.resources.configuration.TextInputMethodQualifier;
+import com.android.ide.common.resources.configuration.TouchScreenQualifier;
+import com.android.ide.common.resources.configuration.UiModeQualifier;
+import com.android.ide.common.resources.configuration.VersionQualifier;
+import com.android.resources.Density;
+import com.android.resources.Keyboard;
+import com.android.resources.KeyboardState;
+import com.android.resources.Navigation;
+import com.android.resources.NightMode;
+import com.android.resources.ScreenOrientation;
+import com.android.resources.ScreenRatio;
+import com.android.resources.ScreenSize;
+import com.android.resources.TouchScreen;
+import com.android.resources.UiMode;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlPullParserFactory;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Map;
+import java.util.Properties;
+
+import com.google.android.collect.Maps;
+
+/**
+ * Provides {@link FolderConfiguration} and {@link HardwareConfig} for various devices. Also
+ * provides utility methods to parse build.prop and attrs.xml to generate the appropriate maps.
+ */
+@SuppressWarnings("UnusedDeclaration") // For the pre-configured nexus generators.
+public class ConfigGenerator {
+
+ public static final ConfigGenerator NEXUS_4 = new ConfigGenerator();
+
+ public static final ConfigGenerator NEXUS_5 = new ConfigGenerator()
+ .setScreenHeight(1920)
+ .setScreenWidth(1080)
+ .setXdpi(445)
+ .setYdpi(445)
+ .setOrientation(ScreenOrientation.PORTRAIT)
+ .setDensity(Density.XXHIGH)
+ .setRatio(ScreenRatio.NOTLONG)
+ .setSize(ScreenSize.NORMAL)
+ .setKeyboard(Keyboard.NOKEY)
+ .setTouchScreen(TouchScreen.FINGER)
+ .setKeyboardState(KeyboardState.SOFT)
+ .setSoftButtons(true)
+ .setNavigation(Navigation.NONAV);
+
+ public static final ConfigGenerator NEXUS_7 = new ConfigGenerator()
+ .setScreenHeight(1920)
+ .setScreenWidth(1200)
+ .setXdpi(323)
+ .setYdpi(323)
+ .setOrientation(ScreenOrientation.PORTRAIT)
+ .setDensity(Density.XHIGH)
+ .setRatio(ScreenRatio.NOTLONG)
+ .setSize(ScreenSize.LARGE)
+ .setKeyboard(Keyboard.NOKEY)
+ .setTouchScreen(TouchScreen.FINGER)
+ .setKeyboardState(KeyboardState.SOFT)
+ .setSoftButtons(true)
+ .setNavigation(Navigation.NONAV);
+
+ public static final ConfigGenerator NEXUS_10 = new ConfigGenerator()
+ .setScreenHeight(1600)
+ .setScreenWidth(2560)
+ .setXdpi(300)
+ .setYdpi(300)
+ .setOrientation(ScreenOrientation.LANDSCAPE)
+ .setDensity(Density.XHIGH)
+ .setRatio(ScreenRatio.NOTLONG)
+ .setSize(ScreenSize.XLARGE)
+ .setKeyboard(Keyboard.NOKEY)
+ .setTouchScreen(TouchScreen.FINGER)
+ .setKeyboardState(KeyboardState.SOFT)
+ .setSoftButtons(true)
+ .setNavigation(Navigation.NONAV);
+
+ private static final String TAG_ATTR = "attr";
+ private static final String TAG_ENUM = "enum";
+ private static final String TAG_FLAG = "flag";
+ private static final String ATTR_NAME = "name";
+ private static final String ATTR_VALUE = "value";
+
+ // Device Configuration. Defaults are for a Nexus 4 device.
+ private int mScreenHeight = 1280;
+ private int mScreenWidth = 768;
+ private int mXdpi = 320;
+ private int mYdpi = 320;
+ private ScreenOrientation mOrientation = ScreenOrientation.PORTRAIT;
+ private Density mDensity = Density.XHIGH;
+ private ScreenRatio mRatio = ScreenRatio.NOTLONG;
+ private ScreenSize mSize = ScreenSize.NORMAL;
+ private Keyboard mKeyboard = Keyboard.NOKEY;
+ private TouchScreen mTouchScreen = TouchScreen.FINGER;
+ private KeyboardState mKeyboardState = KeyboardState.SOFT;
+ private boolean mSoftButtons = true;
+ private Navigation mNavigation = Navigation.NONAV;
+
+ public FolderConfiguration getFolderConfig() {
+ FolderConfiguration config = new FolderConfiguration();
+ config.createDefault();
+ config.setDensityQualifier(new DensityQualifier(mDensity));
+ config.setNavigationMethodQualifier(new NavigationMethodQualifier(mNavigation));
+ if (mScreenWidth > mScreenHeight) {
+ config.setScreenDimensionQualifier(new ScreenDimensionQualifier(mScreenWidth,
+ mScreenHeight));
+ } else {
+ config.setScreenDimensionQualifier(new ScreenDimensionQualifier(mScreenHeight,
+ mScreenWidth));
+ }
+ config.setScreenRatioQualifier(new ScreenRatioQualifier(mRatio));
+ config.setScreenSizeQualifier(new ScreenSizeQualifier(mSize));
+ config.setTextInputMethodQualifier(new TextInputMethodQualifier(mKeyboard));
+ config.setTouchTypeQualifier(new TouchScreenQualifier(mTouchScreen));
+ config.setKeyboardStateQualifier(new KeyboardStateQualifier(mKeyboardState));
+ config.setScreenOrientationQualifier(new ScreenOrientationQualifier(mOrientation));
+
+ config.updateScreenWidthAndHeight();
+
+ // some default qualifiers.
+ config.setUiModeQualifier(new UiModeQualifier(UiMode.NORMAL));
+ config.setNightModeQualifier(new NightModeQualifier(NightMode.NOTNIGHT));
+ config.setCountryCodeQualifier(new CountryCodeQualifier());
+ config.setLanguageQualifier(new LanguageQualifier());
+ config.setLayoutDirectionQualifier(new LayoutDirectionQualifier());
+ config.setNetworkCodeQualifier(new NetworkCodeQualifier());
+ config.setRegionQualifier(new RegionQualifier());
+ config.setVersionQualifier(new VersionQualifier());
+ return config;
+ }
+
+ public HardwareConfig getHardwareConfig() {
+ return new HardwareConfig(mScreenWidth, mScreenHeight, mDensity, mXdpi, mYdpi, mSize,
+ mOrientation, mSoftButtons);
+ }
+
+ public static Map<String, String> loadProperties(File path) {
+ Properties p = new Properties();
+ Map<String, String> map = Maps.newHashMap();
+ try {
+ p.load(new FileInputStream(path));
+ for (String key : p.stringPropertyNames()) {
+ map.put(key, p.getProperty(key));
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return map;
+ }
+
+ public static Map<String, Map<String, Integer>> getEnumMap(File path) {
+ Map<String, Map<String, Integer>> map = Maps.newHashMap();
+ try {
+ XmlPullParser xmlPullParser = XmlPullParserFactory.newInstance().newPullParser();
+ xmlPullParser.setInput(new FileInputStream(path), null);
+ int eventType = xmlPullParser.getEventType();
+ String attr = null;
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ if (TAG_ATTR.equals(xmlPullParser.getName())) {
+ attr = xmlPullParser.getAttributeValue(null, ATTR_NAME);
+ } else if (TAG_ENUM.equals(xmlPullParser.getName())
+ || TAG_FLAG.equals(xmlPullParser.getName())) {
+ String name = xmlPullParser.getAttributeValue(null, ATTR_NAME);
+ String value = xmlPullParser.getAttributeValue(null, ATTR_VALUE);
+ // Integer.decode cannot handle "ffffffff", see JDK issue 6624867
+ int i = (int) (long) Long.decode(value);
+ assert attr != null;
+ Map<String, Integer> attributeMap = map.get(attr);
+ if (attributeMap == null) {
+ attributeMap = Maps.newHashMap();
+ map.put(attr, attributeMap);
+ }
+ attributeMap.put(name, i);
+ }
+ } else if (eventType == XmlPullParser.END_TAG) {
+ if (TAG_ATTR.equals(xmlPullParser.getName())) {
+ attr = null;
+ }
+ }
+ eventType = xmlPullParser.next();
+ }
+ } catch (XmlPullParserException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return map;
+ }
+
+ // Methods to set the configuration values.
+
+ public ConfigGenerator setScreenHeight(int height) {
+ mScreenHeight = height;
+ return this;
+ }
+
+ public ConfigGenerator setScreenWidth(int width) {
+ mScreenWidth = width;
+ return this;
+ }
+
+ public ConfigGenerator setXdpi(int xdpi) {
+ mXdpi = xdpi;
+ return this;
+ }
+
+ public ConfigGenerator setYdpi(int ydpi) {
+ mYdpi = ydpi;
+ return this;
+ }
+
+ public ConfigGenerator setOrientation(ScreenOrientation orientation) {
+ mOrientation = orientation;
+ return this;
+ }
+
+ public ConfigGenerator setDensity(Density density) {
+ mDensity = density;
+ return this;
+ }
+
+ public ConfigGenerator setRatio(ScreenRatio ratio) {
+ mRatio = ratio;
+ return this;
+ }
+
+ public ConfigGenerator setSize(ScreenSize size) {
+ mSize = size;
+ return this;
+ }
+
+ public ConfigGenerator setKeyboard(Keyboard keyboard) {
+ mKeyboard = keyboard;
+ return this;
+ }
+
+ public ConfigGenerator setTouchScreen(TouchScreen touchScreen) {
+ mTouchScreen = touchScreen;
+ return this;
+ }
+
+ public ConfigGenerator setKeyboardState(KeyboardState state) {
+ mKeyboardState = state;
+ return this;
+ }
+
+ public ConfigGenerator setSoftButtons(boolean softButtons) {
+ mSoftButtons = softButtons;
+ return this;
+ }
+
+ public ConfigGenerator setNavigation(Navigation navigation) {
+ mNavigation = navigation;
+ return this;
+ }
+}
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
new file mode 100644
index 0000000..565e881
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutLibTestCallback.java
@@ -0,0 +1,183 @@
+/*
+ * 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.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;
+import com.android.ide.common.rendering.api.IProjectCallback;
+import com.android.ide.common.rendering.api.ResourceReference;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.resources.ResourceType;
+import com.android.ide.common.resources.IntArrayWrapper;
+import com.android.util.Pair;
+import com.android.utils.ILogger;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Map;
+
+import com.google.android.collect.Maps;
+
+@SuppressWarnings("deprecation") // For Pair
+public class LayoutLibTestCallback extends ClassLoader implements IProjectCallback {
+
+ private static final String PROJECT_CLASSES_LOCATION = "/testApp/MyApplication/build/intermediates/classes/debug/";
+ private static final String PACKAGE_NAME = "com.android.layoutlib.test.myapplication";
+
+ private final Map<Integer, Pair<ResourceType, String>> mProjectResources = Maps.newHashMap();
+ private final Map<IntArrayWrapper, String> mStyleableValueToNameMap = Maps.newHashMap();
+ private final Map<ResourceType, Map<String, Integer>> mResources = Maps.newHashMap();
+ private final Map<String, Class<?>> mClasses = Maps.newHashMap();
+ private final ILogger mLog;
+ private final ActionBarCallback mActionBarCallback = new ActionBarCallback();
+
+ public LayoutLibTestCallback(ILogger logger) {
+ mLog = logger;
+ }
+
+ public void initResources() throws ClassNotFoundException {
+ Class<?> rClass = loadClass(PACKAGE_NAME + ".R");
+ Class<?>[] nestedClasses = rClass.getDeclaredClasses();
+ for (Class<?> resClass : nestedClasses) {
+ final ResourceType resType = ResourceType.getEnum(resClass.getSimpleName());
+
+ if (resType != null) {
+ final Map<String, Integer> resName2Id = Maps.newHashMap();
+ mResources.put(resType, resName2Id);
+
+ for (Field field : resClass.getDeclaredFields()) {
+ final int modifiers = field.getModifiers();
+ if (Modifier.isStatic(modifiers)) { // May not be final in library projects
+ final Class<?> type = field.getType();
+ try {
+ if (type.isArray() && type.getComponentType() == int.class) {
+ mStyleableValueToNameMap.put(
+ new IntArrayWrapper((int[]) field.get(null)),
+ field.getName());
+ } else if (type == int.class) {
+ final Integer value = (Integer) field.get(null);
+ mProjectResources.put(value, Pair.of(resType, field.getName()));
+ resName2Id.put(field.getName(), value);
+ } else {
+ mLog.error(null, "Unknown field type in R class: %1$s", type);
+ }
+ } catch (IllegalAccessException ignored) {
+ mLog.error(ignored, "Malformed R class: %1$s", PACKAGE_NAME + ".R");
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ protected Class<?> findClass(String name) throws ClassNotFoundException {
+ Class<?> aClass = mClasses.get(name);
+ if (aClass != null) {
+ return aClass;
+ }
+ String pathName = PROJECT_CLASSES_LOCATION.concat(name.replace('.', '/')).concat(".class");
+ InputStream classInputStream = getClass().getResourceAsStream(pathName);
+ if (classInputStream == null) {
+ throw new ClassNotFoundException("Unable to find class " + name + " at " + pathName);
+ }
+ byte[] data;
+ try {
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
+ int nRead;
+ data = new byte[16384];
+ while ((nRead = classInputStream.read(data, 0, data.length)) != -1) {
+ buffer.write(data, 0, nRead);
+ }
+ buffer.flush();
+ data = buffer.toByteArray();
+ } catch (IOException e) {
+ // Wrap the exception with ClassNotFoundException so that caller can deal with it.
+ throw new ClassNotFoundException("Unable to load class " + name, e);
+ }
+ aClass = defineClass(name, data, 0, data.length);
+ mClasses.put(name, aClass);
+ return aClass;
+ }
+
+ @Override
+ public Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs)
+ throws Exception {
+ Class<?> viewClass = findClass(name);
+ Constructor<?> viewConstructor = viewClass.getConstructor(constructorSignature);
+ viewConstructor.setAccessible(true);
+ return viewConstructor.newInstance(constructorArgs);
+ }
+
+ @Override
+ public String getNamespace() {
+ return String.format(SdkConstants.NS_CUSTOM_RESOURCES_S,
+ PACKAGE_NAME);
+ }
+
+ @Override
+ public Pair<ResourceType, String> resolveResourceId(int id) {
+ return mProjectResources.get(id);
+ }
+
+ @Override
+ public String resolveResourceId(int[] id) {
+ return mStyleableValueToNameMap.get(new IntArrayWrapper(id));
+ }
+
+ @Override
+ public Integer getResourceId(ResourceType type, String name) {
+ return mResources.get(type).get(name);
+ }
+
+ @Override
+ public ILayoutPullParser getParser(String layoutName) {
+ org.junit.Assert.fail("This method shouldn't be called by this version of LayoutLib.");
+ return null;
+ }
+
+ @Override
+ public ILayoutPullParser getParser(ResourceValue layoutResource) {
+ return new LayoutPullParser(new File(layoutResource.getValue()));
+ }
+
+ @Override
+ public Object getAdapterItemValue(ResourceReference adapterView, Object adapterCookie,
+ ResourceReference itemRef, int fullPosition, int positionPerType,
+ int fullParentPosition, int parentPositionPerType, ResourceReference viewRef,
+ ViewAttribute viewAttribute, Object defaultValue) {
+ return null;
+ }
+
+ @Override
+ public AdapterBinding getAdapterBinding(ResourceReference adapterViewRef, Object adapterCookie,
+ Object viewObject) {
+ return null;
+ }
+
+ @Override
+ public ActionBarCallback getActionBarCallback() {
+ return mActionBarCallback;
+ }
+}
diff --git a/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java
new file mode 100644
index 0000000..c79b662
--- /dev/null
+++ b/tools/layoutlib/bridge/tests/src/com/android/layoutlib/bridge/intensive/setup/LayoutPullParser.java
@@ -0,0 +1,111 @@
+/*
+ * 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.intensive.setup;
+
+import com.android.ide.common.rendering.api.ILayoutPullParser;
+
+import org.kxml2.io.KXmlParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOError;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import static com.android.SdkConstants.ATTR_IGNORE;
+import static com.android.SdkConstants.EXPANDABLE_LIST_VIEW;
+import static com.android.SdkConstants.GRID_VIEW;
+import static com.android.SdkConstants.LIST_VIEW;
+import static com.android.SdkConstants.SPINNER;
+import static com.android.SdkConstants.TOOLS_URI;
+
+public class LayoutPullParser extends KXmlParser implements ILayoutPullParser{
+
+ /**
+ * @param layoutPath Must start with '/' and be relative to test resources.
+ */
+ public LayoutPullParser(String layoutPath) {
+ assert layoutPath.startsWith("/");
+ try {
+ init(getClass().getResourceAsStream(layoutPath));
+ } catch (XmlPullParserException e) {
+ throw new IOError(e);
+ }
+ }
+
+ /**
+ * @param layoutFile Path of the layout xml file on disk.
+ */
+ public LayoutPullParser(File layoutFile) {
+ try {
+ init(new FileInputStream(layoutFile));
+ } catch (XmlPullParserException e) {
+ throw new IOError(e);
+ } catch (FileNotFoundException e) {
+ throw new IOError(e);
+ }
+ }
+
+ private void init(InputStream stream) throws XmlPullParserException {
+ setFeature(FEATURE_PROCESS_NAMESPACES, true);
+ setInput(stream, null);
+ }
+
+ @Override
+ public Object getViewCookie() {
+ // TODO: Implement this properly.
+ String name = super.getName();
+ if (name == null) {
+ return null;
+ }
+
+ // Store tools attributes if this looks like a layout we'll need adapter view
+ // bindings for in the LayoutlibCallback.
+ if (LIST_VIEW.equals(name) || EXPANDABLE_LIST_VIEW.equals(name) || GRID_VIEW.equals(name) || SPINNER.equals(name)) {
+ Map<String, String> map = null;
+ int count = getAttributeCount();
+ for (int i = 0; i < count; i++) {
+ String namespace = getAttributeNamespace(i);
+ if (namespace != null && namespace.equals(TOOLS_URI)) {
+ String attribute = getAttributeName(i);
+ if (attribute.equals(ATTR_IGNORE)) {
+ continue;
+ }
+ if (map == null) {
+ map = new HashMap<String, String>(4);
+ }
+ map.put(attribute, getAttributeValue(i));
+ }
+ }
+
+ return map;
+ }
+
+ return null;
+ }
+
+ @Override
+ @Deprecated
+ public ILayoutPullParser getParser(String layoutName) {
+ // Studio returns null.
+ return null;
+ }
+
+}
diff --git a/tools/layoutlib/create/.classpath b/tools/layoutlib/create/.classpath
index cd8bb0d..25c3b3e 100644
--- a/tools/layoutlib/create/.classpath
+++ b/tools/layoutlib/create/.classpath
@@ -4,6 +4,6 @@
<classpathentry excluding="mock_data/" kind="src" path="tests"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/4"/>
- <classpathentry kind="var" path="ANDROID_SRC/prebuilts/tools/common/asm-tools/asm-4.0.jar" sourcepath="/ANDROID_PLAT/prebuilts/tools/common/asm-tools/src-4.0.zip"/>
+ <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilts/misc/common/asm/asm-4.0.jar" sourcepath="/ANDROID_PLAT_SRC/prebuilts/misc/common/asm/src.zip"/>
<classpathentry kind="output" path="bin"/>
</classpath>
diff --git a/tools/layoutlib/create/Android.mk b/tools/layoutlib/create/Android.mk
index 9bd48ab..e6f0bc3 100644
--- a/tools/layoutlib/create/Android.mk
+++ b/tools/layoutlib/create/Android.mk
@@ -26,3 +26,6 @@ LOCAL_MODULE := layoutlib_create
include $(BUILD_HOST_JAVA_LIBRARY)
+# Build all sub-directories
+include $(call all-makefiles-under,$(LOCAL_PATH))
+
diff --git a/tools/layoutlib/create/README.txt b/tools/layoutlib/create/README.txt
index ef2b185..727b194 100644
--- a/tools/layoutlib/create/README.txt
+++ b/tools/layoutlib/create/README.txt
@@ -4,46 +4,46 @@
- Description -
---------------
-Layoutlib_create generates a JAR library used by the Eclipse graphical layout editor
-to perform layout.
+Layoutlib_create generates a JAR library used by the Eclipse graphical layout editor to perform
+layout.
- Usage -
---------
- ./layoutlib_create path/to/android.jar destination.jar
+ ./layoutlib_create destination.jar path/to/android1.jar path/to/android2.jar
- Design Overview -
-------------------
-Layoutlib_create uses the "android.jar" containing all the Java code used by Android
-as generated by the Android build, right before the classes are converted to a DEX format.
+Layoutlib_create uses a few jars from the framework containing the Java code used by Android as
+generated by the Android build, right before the classes are converted to a DEX format.
-The Android JAR can't be used directly in Eclipse:
-- it contains references to native code (which we want to avoid in Eclipse),
-- some classes need to be overridden, for example all the drawing code that is
- replaced by Java 2D calls in Eclipse.
-- some of the classes that need to be changed are final and/or we need access
- to their private internal state.
+These jars can't be used directly in Eclipse as:
+- they contains references to native code (which we want to avoid in Eclipse),
+- some classes need to be overridden, for example all the drawing code that is replaced by Java 2D
+ calls in Eclipse.
+- some of the classes that need to be changed are final and/or we need access to their private
+ internal state.
Consequently this tool:
- parses the input JAR,
- modifies some of the classes directly using some bytecode manipulation,
- filters some packages and removes those we don't want in the output JAR,
- injects some new classes,
-- generates a modified JAR file that is suitable for the Android plugin
- for Eclipse to perform rendering.
+- generates a modified JAR file that is suitable for the Android plugin for Eclipse to perform
+ rendering.
The ASM library is used to do the bytecode modification using its visitor pattern API.
-The layoutlib_create is *NOT* generic. There is no configuration file. Instead all the
-configuration is done in the main() method and the CreateInfo structure is expected to
-change with the Android platform as new classes are added, changed or removed.
+The layoutlib_create is *NOT* generic. There is no configuration file. Instead all the configuration
+is done in the main() method and the CreateInfo structure is expected to change with the Android
+platform as new classes are added, changed or removed. Some configuration that may be platform
+dependent is also present elsewhere in code.
-The resulting JAR is used by layoutlib_bridge (a.k.a. "the bridge"), also part of the
-platform, that provides all the necessary missing implementation for rendering graphics
-in Eclipse.
+The resulting JAR is used by layoutlib_bridge (a.k.a. "the bridge"), also part of the platform, that
+provides all the necessary missing implementation for rendering graphics in Eclipse.
@@ -58,97 +58,102 @@ The tool works in two phases:
- Analyzer
----------
-The goal of the analyzer is to create a graph of all the classes from the input JAR
-with their dependencies and then only keep the ones we want.
+The goal of the analyzer is to create a graph of all the classes from the input JAR with their
+dependencies and then only keep the ones we want.
-To do that, the analyzer is created with a list of base classes to keep -- everything
-that derives from these is kept. Currently the one such class is android.view.View:
-since we want to render layouts, anything that is sort of a view needs to be kept.
+To do that, the analyzer is created with a list of base classes to keep -- everything that derives
+from these is kept. Currently the one such class is android.view.View: since we want to render
+layouts, anything that is sort of a view needs to be kept.
-The analyzer is also given a list of class names to keep in the output.
-This is done using shell-like glob patterns that filter on the fully-qualified
-class names, for example "android.*.R**" ("*" does not matches dots whilst "**" does,
-and "." and "$" are interpreted as-is).
-In practice we almost but not quite request the inclusion of full packages.
+The analyzer is also given a list of class names to keep in the output. This is done using
+shell-like glob patterns that filter on the fully-qualified class names, for example "android.*.R**"
+("*" does not matches dots whilst "**" does, and "." and "$" are interpreted as-is). In practice we
+almost but not quite request the inclusion of full packages.
-The analyzer is also given a list of classes to exclude. A fake implementation of these
-classes is injected by the Generator.
+The analyzer is also given a list of classes to exclude. A fake implementation of these classes is
+injected by the Generator.
-With this information, the analyzer parses the input zip to find all the classes.
-All classes deriving from the requested bases classes are kept.
-All classes which name matched the glob pattern are kept.
-The analysis then finds all the dependencies of the classes that are to be kept
-using an ASM visitor on the class, the field types, the method types and annotations types.
-Classes that belong to the current JRE are excluded.
+With this information, the analyzer parses the input zip to find all the classes. All classes
+deriving from the requested bases classes are kept. All classes whose name match the glob pattern
+are kept. The analysis then finds all the dependencies of the classes that are to be kept using an
+ASM visitor on the class, the field types, the method types and annotations types. Classes that
+belong to the current JRE are excluded.
-The output of the analyzer is a set of ASM ClassReader instances which are then
-fed to the generator.
+The output of the analyzer is a set of ASM ClassReader instances which are then fed to the
+generator.
- Generator
-----------
-The generator is constructed from a CreateInfo struct that acts as a config file
-and lists:
-- the classes to inject in the output JAR -- these classes are directly implemented
- in layoutlib_create and will be used to interface with the renderer in Eclipse.
+The generator is constructed from a CreateInfo struct that acts as a config file and lists:
+- the classes to inject in the output JAR -- these classes are directly implemented in
+ layoutlib_create and will be used to interface with the renderer in Eclipse.
- specific methods to override (see method stubs details below).
- specific methods for which to delegate calls.
- specific methods to remove based on their return type.
- specific classes to rename.
- specific classes to refactor.
-Each of these are specific strategies we use to be able to modify the Android code
-to fit within the Eclipse renderer. These strategies are explained beow.
+Each of these are specific strategies we use to be able to modify the Android code to fit within the
+Eclipse renderer. These strategies are explained below.
-The core method of the generator is transform(): it takes an input ASM ClassReader
-and modifies it to produce a byte array suitable for the final JAR file.
+The core method of the generator is transform(): it takes an input ASM ClassReader and modifies it
+to produce a byte array suitable for the final JAR file.
The first step of the transformation is to implement the method delegates.
-The TransformClassAdapter is then used to process the potentially renamed class.
-All protected or private classes are market as public.
-All classes are made non-final.
-Interfaces are left as-is.
+The TransformClassAdapter is then used to process the potentially renamed class. All protected or
+private classes are market as public. All classes are made non-final. Interfaces are left as-is.
-If a method has a return type that must be erased, the whole method is skipped.
-Methods are also changed from protected/private to public.
-The code of the methods is then kept as-is, except for native methods which are
-replaced by a stub. Methods that are to be overridden are also replaced by a stub.
+If a method has a return type that must be erased, the whole method is skipped. Methods are also
+changed from protected/private to public. The code of the methods is then kept as-is, except for
+native methods which are replaced by a stub. Methods that are to be overridden are also replaced by
+a stub.
Finally fields are also visited and changed from protected/private to public.
-The next step of the transformation is changing the name of the class in case
-we requested the class to be renamed. This uses the RenameClassAdapter to also rename
-all inner classes and references in methods and types. Note that other classes are
-not transformed and keep referencing the original name.
-
-The class is then fed to RefactorClassAdapter which is like RenameClassAdapter but
-updates the references in all classes. This is used to update the references of classes
-in the java package that were added in the Dalvik VM but are not a part of the standard
-JVM. The existing classes are modified to update all references to these non-standard
-classes. An alternate implementation of these (com.android.tools.layoutlib.java.*) is
-injected.
-
-The ClassAdapters are chained together to achieve the desired output. (Look at section
-2.2.7 Transformation chains in the asm user guide, link in the References.) The order of
-execution of these is:
+The next step of the transformation is changing the name of the class in case we requested the class
+to be renamed. This uses the RenameClassAdapter to also rename all inner classes and references in
+methods and types. Note that other classes are not transformed and keep referencing the original
+name.
+
+The class is then fed to RefactorClassAdapter which is like RenameClassAdapter but updates the
+references in all classes. This is used to update the references of classes in the java package that
+were added in the Dalvik VM but are not a part of the Desktop VM. The existing classes are
+modified to update all references to these non-desktop classes. An alternate implementation of
+these (com.android.tools.layoutlib.java.*) is injected.
+
+RenameClassAdapter and RefactorClassAdapter both inherit from AbstractClassAdapter which changes the
+class version (version of the JDK used to compile the class) to 50 (corresponding to Java 6), if the
+class was originally compiled with Java 7 (version 51). This is because we don't currently generate
+the StackMapTable correctly and Java 7 VM enforces that classes with version greater than 51 have
+valid StackMapTable. As a side benefit of this, we can continue to support Java 6 because Java 7 on
+Mac has horrible font rendering support.
+
+ReplaceMethodCallsAdapter replaces calls to certain methods. This is different from the
+DelegateMethodAdapter since it doesn't preserve the original copy of the method and more importantly
+changes the calls to a method in each class instead of changing the implementation of the method.
+This is useful for methods in the Java namespace where we cannot add delegates. The configuration
+for this is not done through the CreateInfo class, but done in the ReplaceMethodAdapter.
+
+The ClassAdapters are chained together to achieve the desired output. (Look at section 2.2.7
+Transformation chains in the asm user guide, link in the References.) The order of execution of
+these is:
ClassReader -> [DelegateClassAdapter] -> TransformClassAdapter -> [RenameClassAdapter] ->
-RefactorClassAdapter -> ClassWriter
+RefactorClassAdapter -> [ReplaceMethodCallsAdapter] -> ClassWriter
- Method stubs
--------------
-As indicated above, all native and overridden methods are replaced by a stub.
-We don't have the code to replace with in layoutlib_create.
-Instead the StubMethodAdapter replaces the code of the method by a call to
-OverrideMethod.invokeX(). When using the final JAR, the bridge can register
+As indicated above, all native and overridden methods are replaced by a stub. We don't have the
+code to replace with in layoutlib_create. Instead the StubMethodAdapter replaces the code of the
+method by a call to OverrideMethod.invokeX(). When using the final JAR, the bridge can register
listeners from these overridden method calls based on the method signatures.
-The listeners are currently pretty basic: we only pass the signature of the
-method being called, its caller object and a flag indicating whether the
-method was native. We do not currently provide the parameters. The listener
-can however specify the return value of the overridden method.
+The listeners are currently pretty basic: we only pass the signature of the method being called, its
+caller object and a flag indicating whether the method was native. We do not currently provide the
+parameters. The listener can however specify the return value of the overridden method.
This strategy is now obsolete and replaced by the method delegates.
@@ -156,97 +161,89 @@ This strategy is now obsolete and replaced by the method delegates.
- Strategies
------------
-We currently have 6 strategies to deal with overriding the rendering code
-and make it run in Eclipse. Most of these strategies are implemented hand-in-hand
-by the bridge (which runs in Eclipse) and the generator.
+We currently have 6 strategies to deal with overriding the rendering code and make it run in
+Eclipse. Most of these strategies are implemented hand-in-hand by the bridge (which runs in Eclipse)
+and the generator.
1- Class Injection
This is the easiest: we currently inject the following classes:
-- OverrideMethod and its associated MethodListener and MethodAdapter are used
- to intercept calls to some specific methods that are stubbed out and change
- their return value.
-- CreateInfo class, which configured the generator. Not used yet, but could
- in theory help us track what the generator changed.
-- AutoCloseable and Objects are part of Java 7. To enable us to still run on Java 6, new
- classes are injected. The implementation for these classes has been taken from
- Android's libcore (platform/libcore/luni/src/main/java/java/...).
-- Charsets, IntegralToString and UnsafeByteSequence are not part of the standard JAVA VM.
- They are added to the Dalvik VM for performance reasons. An implementation that is very
- close to the original (which is at platform/libcore/luni/src/main/java/...) is injected.
- Since these classees were in part of the java package, where we can't inject classes,
- all references to these have been updated (See strategy 4- Refactoring Classes).
+- OverrideMethod and its associated MethodListener and MethodAdapter are used to intercept calls to
+ some specific methods that are stubbed out and change their return value.
+- CreateInfo class, which configured the generator. Not used yet, but could in theory help us track
+ what the generator changed.
+- AutoCloseable and Objects are part of Java 7. To enable us to still run on Java 6, new classes are
+ injected. The implementation for these classes has been taken from Android's libcore
+ (platform/libcore/luni/src/main/java/java/...).
+- Charsets, IntegralToString and UnsafeByteSequence are not part of the Desktop VM. They are
+ added to the Dalvik VM for performance reasons. An implementation that is very close to the
+ original (which is at platform/libcore/luni/src/main/java/...) is injected. Since these classees
+ were in part of the java package, where we can't inject classes, all references to these have been
+ updated (See strategy 4- Refactoring Classes).
2- Overriding methods
-As explained earlier, the creator doesn't have any replacement code for
-methods to override. Instead it removes the original code and replaces it
-by a call to a specific OveriddeMethod.invokeX(). The bridge then registers
-a listener on the method signature and can provide an implementation.
+As explained earlier, the creator doesn't have any replacement code for methods to override. Instead
+it removes the original code and replaces it by a call to a specific OveriddeMethod.invokeX(). The
+bridge then registers a listener on the method signature and can provide an implementation.
-This strategy is now obsolete and replaced by the method delegates.
-See strategy 5 below.
+This strategy is now obsolete and replaced by the method delegates (See strategy 6- Method
+Delegates).
3- Renaming classes
-This simply changes the name of a class in its definition, as well as all its
-references in internal inner classes and methods.
-Calls from other classes are not modified -- they keep referencing the original
-class name. This allows the bridge to literally replace an implementation.
+This simply changes the name of a class in its definition, as well as all its references in internal
+inner classes and methods. Calls from other classes are not modified -- they keep referencing the
+original class name. This allows the bridge to literally replace an implementation.
-An example will make this easier: android.graphics.Paint is the main drawing
-class that we need to replace. To do so, the generator renames Paint to _original_Paint.
-Later the bridge provides its own replacement version of Paint which will be used
-by the rest of the Android stack. The replacement version of Paint can still use
-(either by inheritance or delegation) all the original non-native code of _original_Paint
-if it so desires.
+An example will make this easier: android.graphics.Paint is the main drawing class that we need to
+replace. To do so, the generator renames Paint to _original_Paint. Later the bridge provides its own
+replacement version of Paint which will be used by the rest of the Android stack. The replacement
+version of Paint can still use (either by inheritance or delegation) all the original non-native
+code of _original_Paint if it so desires.
-Some of the Android classes are basically wrappers over native objects and since
-we don't have the native code in Eclipse, we need to provide a full alternate
-implementation. Sub-classing doesn't work as some native methods are static and
-we don't control object creation.
+Some of the Android classes are basically wrappers over native objects and since we don't have the
+native code in Eclipse, we need to provide a full alternate implementation. Sub-classing doesn't
+work as some native methods are static and we don't control object creation.
This won't rename/replace the inner static methods of a given class.
4- Refactoring classes
-This is very similar to the Renaming classes except that it also updates the reference in
-all classes. This is done for classes which are added to the Dalvik VM for performance
-reasons but are not present in the Standard Java VM. An implementation for these classes
-is also injected.
+This is very similar to the Renaming classes except that it also updates the reference in all
+classes. This is done for classes which are added to the Dalvik VM for performance reasons but are
+not present in the Desktop VM. An implementation for these classes is also injected.
5- Method erasure based on return type
-This is mostly an implementation detail of the bridge: in the Paint class
-mentioned above, some inner static classes are used to pass around
-attributes (e.g. FontMetrics, or the Style enum) and all the original implementation
-is native.
+This is mostly an implementation detail of the bridge: in the Paint class mentioned above, some
+inner static classes are used to pass around attributes (e.g. FontMetrics, or the Style enum) and
+all the original implementation is native.
-In this case we have a strategy that tells the generator that anything returning, for
-example, the inner class Paint$Style in the Paint class should be discarded and the
-bridge will provide its own implementation.
+In this case we have a strategy that tells the generator that anything returning, for example, the
+inner class Paint$Style in the Paint class should be discarded and the bridge will provide its own
+implementation.
6- Method Delegates
-This strategy is used to override method implementations.
-Given a method SomeClass.MethodName(), 1 or 2 methods are generated:
-a- A copy of the original method named SomeClass.MethodName_Original().
- The content is the original method as-is from the reader.
- This step is omitted if the method is native, since it has no Java implementation.
-b- A brand new implementation of SomeClass.MethodName() which calls to a
- non-existing static method named SomeClass_Delegate.MethodName().
- The implementation of this 'delegate' method is done in layoutlib_brigde.
-
-The delegate method is a static method.
-If the original method is non-static, the delegate method receives the original 'this'
-as its first argument. If the original method is an inner non-static method, it also
-receives the inner 'this' as the second argument.
+This strategy is used to override method implementations. Given a method SomeClass.MethodName(), 1
+or 2 methods are generated:
+a- A copy of the original method named SomeClass.MethodName_Original(). The content is the original
+method as-is from the reader. This step is omitted if the method is native, since it has no Java
+implementation.
+b- A brand new implementation of SomeClass.MethodName() which calls to a non-existing static method
+named SomeClass_Delegate.MethodName(). The implementation of this 'delegate' method is done in
+layoutlib_bridge.
+
+The delegate method is a static method. If the original method is non-static, the delegate method
+receives the original 'this' as its first argument. If the original method is an inner non-static
+method, it also receives the inner 'this' as the second argument.
diff --git a/tools/layoutlib/create/create.iml b/tools/layoutlib/create/create.iml
new file mode 100644
index 0000000..b7e8eb3
--- /dev/null
+++ b/tools/layoutlib/create/create.iml
@@ -0,0 +1,18 @@
+<?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" isTestSource="true" />
+ <sourceFolder url="file://$MODULE_DIR$/tests/data" type="java-test-resource" />
+ <sourceFolder url="file://$MODULE_DIR$/tests/mock_data" type="java-test-resource" />
+ <excludeFolder url="file://$MODULE_DIR$/.settings" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ <orderEntry type="library" name="asm-4.0" level="project" />
+ <orderEntry type="library" scope="TEST" name="JUnit4" level="application" />
+ </component>
+</module>
+
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AbstractClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AbstractClassAdapter.java
index b2caa25..a6902a4 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AbstractClassAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AbstractClassAdapter.java
@@ -97,7 +97,7 @@ public abstract class AbstractClassAdapter extends ClassVisitor {
if (type.getSort() == Type.OBJECT) {
String in = type.getInternalName();
String newIn = renameInternalType(in);
- if (newIn != in) {
+ if (!newIn.equals(in)) {
return Type.getType("L" + newIn + ";");
}
} else if (type.getSort() == Type.ARRAY) {
@@ -177,6 +177,17 @@ public abstract class AbstractClassAdapter extends ClassVisitor {
}
}
+ /* Java 7 verifies the StackMapTable of a class if its version number is greater than 50.0.
+ * However, the check is disabled if the class version number is 50.0 or less. Generation
+ * of the StackMapTable requires a rewrite using the tree API of ASM. As a workaround,
+ * we rewrite the version number of the class to be 50.0
+ *
+ * http://bugs.java.com/bugdatabase/view_bug.do?bug_id=6693236
+ */
+ if (version > 50) {
+ version = 50;
+ }
+
super.visit(version, access, name, signature, superName, interfaces);
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
index 9a31705..aa51c46 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmAnalyzer.java
@@ -32,6 +32,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Enumeration;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
@@ -63,7 +64,8 @@ public class AsmAnalyzer {
private final Set<String> mExcludedClasses;
/** Glob patterns of files to keep as is. */
private final String[] mIncludeFileGlobs;
- /** Copy these files into the output as is. */
+ /** Internal names of classes that contain method calls that need to be rewritten. */
+ private final Set<String> mReplaceMethodCallClasses = new HashSet<String>();
/**
* Creates a new analyzer.
@@ -109,6 +111,7 @@ public class AsmAnalyzer {
mGen.setKeep(found);
mGen.setDeps(deps);
mGen.setCopyFiles(filesFound);
+ mGen.setRewriteMethodCallClasses(mReplaceMethodCallClasses);
}
}
@@ -118,7 +121,7 @@ public class AsmAnalyzer {
*
* @param classes The map of class name => ASM ClassReader. Class names are
* in the form "android.view.View".
- * @param fileFound The map of file name => InputStream. The file name is
+ * @param filesFound The map of file name => InputStream. The file name is
* in the form "android/data/dataFile".
*/
void parseZip(List<String> jarPathList, Map<String, ClassReader> classes,
@@ -143,8 +146,8 @@ public class AsmAnalyzer {
String className = classReaderToClassName(cr);
classes.put(className, cr);
} else {
- for (int i = 0; i < includeFilePatterns.length; ++i) {
- if (includeFilePatterns[i].matcher(entry.getName()).matches()) {
+ for (Pattern includeFilePattern : includeFilePatterns) {
+ if (includeFilePattern.matcher(entry.getName()).matches()) {
filesFound.put(entry.getName(), zip.getInputStream(entry));
break;
}
@@ -239,7 +242,8 @@ public class AsmAnalyzer {
for (Entry<String, ClassReader> entry : zipClasses.entrySet()) {
String class_name = entry.getKey();
- if (regexp.matcher(class_name).matches()) {
+ if (regexp.matcher(class_name).matches() &&
+ !mExcludedClasses.contains(getOuterClassName(class_name))) {
findClass(class_name, zipClasses, inOutFound);
}
}
@@ -270,6 +274,9 @@ public class AsmAnalyzer {
*/
void findClassesDerivingFrom(String super_name, Map<String, ClassReader> zipClasses,
Map<String, ClassReader> inOutFound) throws LogAbortException {
+ if (mExcludedClasses.contains(getOuterClassName(super_name))) {
+ return;
+ }
findClass(super_name, zipClasses, inOutFound);
for (Entry<String, ClassReader> entry : zipClasses.entrySet()) {
@@ -321,6 +328,7 @@ public class AsmAnalyzer {
deps, new_deps);
for (ClassReader cr : inOutKeepClasses.values()) {
+ visitor.setClassName(cr.getClassName());
cr.accept(visitor, 0 /* flags */);
}
@@ -337,6 +345,7 @@ public class AsmAnalyzer {
inOutKeepClasses.size(), deps.size());
for (ClassReader cr : temp.values()) {
+ visitor.setClassName(cr.getClassName());
cr.accept(visitor, 0 /* flags */);
}
}
@@ -347,7 +356,13 @@ public class AsmAnalyzer {
return deps;
}
-
+ private String getOuterClassName(String className) {
+ int pos = className.indexOf('$');
+ if (pos > 0) {
+ return className.substring(0, pos);
+ }
+ return className;
+ }
// ----------------------------------
@@ -367,6 +382,8 @@ public class AsmAnalyzer {
/** New classes to keep as-is found by this visitor. */
private final Map<String, ClassReader> mOutKeep;
+ private String mClassName;
+
/**
* Creates a new visitor that will find all the dependencies for the visited class.
* Types which are already in the zipClasses, keepClasses or inDeps are not marked.
@@ -390,6 +407,10 @@ public class AsmAnalyzer {
mOutDeps = outDeps;
}
+ private void setClassName(String className) {
+ mClassName = className;
+ }
+
/**
* Considers the given class name as a dependency.
* If it does, add to the mOutDeps map.
@@ -406,7 +427,7 @@ public class AsmAnalyzer {
mOutKeep.containsKey(className) ||
mInDeps.containsKey(className) ||
mOutDeps.containsKey(className) ||
- mExcludedClasses.contains(getBaseName(className))) {
+ mExcludedClasses.contains(getOuterClassName(className))) {
return;
}
@@ -429,7 +450,7 @@ public class AsmAnalyzer {
// - android classes are added to dependencies
// - non-android classes are added to the list of classes to keep as-is (they don't need
// to be stubbed).
- if (className.indexOf("android") >= 0) { // TODO make configurable
+ if (className.contains("android")) { // TODO make configurable
mOutDeps.put(className, cr);
} else {
mOutKeep.put(className, cr);
@@ -490,14 +511,6 @@ public class AsmAnalyzer {
}
}
- private String getBaseName(String className) {
- int pos = className.indexOf('$');
- if (pos > 0) {
- return className.substring(0, pos);
- }
- return className;
- }
-
// ---------------------------------------------------
// --- ClassVisitor, FieldVisitor
// ---------------------------------------------------
@@ -594,7 +607,7 @@ public class AsmAnalyzer {
// type and exceptions do not use generic types.
considerSignature(signature);
- return new MyMethodVisitor();
+ return new MyMethodVisitor(mClassName);
}
@Override
@@ -614,8 +627,11 @@ public class AsmAnalyzer {
private class MyMethodVisitor extends MethodVisitor {
- public MyMethodVisitor() {
+ private String mOwnerClass;
+
+ public MyMethodVisitor(String ownerClass) {
super(Opcodes.ASM4);
+ mOwnerClass = ownerClass;
}
@@ -632,8 +648,8 @@ public class AsmAnalyzer {
// field instruction
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
- // name is the field's name.
- considerName(name);
+ // owner is the class that declares the field.
+ considerName(owner);
// desc is the field's descriptor (see Type).
considerDesc(desc);
}
@@ -709,6 +725,12 @@ public class AsmAnalyzer {
considerName(owner);
// desc is the method's descriptor (see Type).
considerDesc(desc);
+
+
+ // Check if method needs to replaced by a call to a different method.
+ if (ReplaceMethodCallsAdapter.isReplacementNeeded(owner, name, desc)) {
+ mReplaceMethodCallClasses.add(mOwnerClass);
+ }
}
// instruction multianewarray, whatever that is
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
index 207d8ae..bd6f070 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
@@ -21,7 +21,6 @@ import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import java.io.ByteArrayOutputStream;
-import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
@@ -55,6 +54,8 @@ public class AsmGenerator {
private Map<String, ClassReader> mDeps;
/** All files that are to be copied as-is. */
private Map<String, InputStream> mCopyFiles;
+ /** All classes where certain method calls need to be rewritten. */
+ private Set<String> mReplaceMethodCallsClasses;
/** Counter of number of classes renamed during transform. */
private int mRenameCount;
/** FQCN Names of the classes to rename: map old-FQCN => new-FQCN */
@@ -133,7 +134,7 @@ public class AsmGenerator {
assert i + 1 < n;
String oldFqcn = binaryToInternalClassName(refactorClasses[i]);
String newFqcn = binaryToInternalClassName(refactorClasses[i + 1]);
- mRefactorClasses.put(oldFqcn, newFqcn);;
+ mRefactorClasses.put(oldFqcn, newFqcn);
}
// create the map of renamed class -> return type of method to delete.
@@ -203,44 +204,33 @@ public class AsmGenerator {
mCopyFiles = copyFiles;
}
- /** Gets the map of classes to output as-is, except if they have native methods */
- public Map<String, ClassReader> getKeep() {
- return mKeep;
- }
-
- /** Gets the map of dependencies that must be completely stubbed */
- public Map<String, ClassReader> getDeps() {
- return mDeps;
- }
-
- /** Gets the map of files to output as-is. */
- public Map<String, InputStream> getCopyFiles() {
- return mCopyFiles;
+ public void setRewriteMethodCallClasses(Set<String> rewriteMethodCallClasses) {
+ mReplaceMethodCallsClasses = rewriteMethodCallClasses;
}
/** Generates the final JAR */
- public void generate() throws FileNotFoundException, IOException {
+ public void generate() throws IOException {
TreeMap<String, byte[]> all = new TreeMap<String, byte[]>();
for (Class<?> clazz : mInjectClasses) {
String name = classToEntryPath(clazz);
InputStream is = ClassLoader.getSystemResourceAsStream(name);
ClassReader cr = new ClassReader(is);
- byte[] b = transform(cr, true /* stubNativesOnly */);
+ byte[] b = transform(cr, true);
name = classNameToEntryPath(transformName(cr.getClassName()));
all.put(name, b);
}
for (Entry<String, ClassReader> entry : mDeps.entrySet()) {
ClassReader cr = entry.getValue();
- byte[] b = transform(cr, true /* stubNativesOnly */);
+ byte[] b = transform(cr, true);
String name = classNameToEntryPath(transformName(cr.getClassName()));
all.put(name, b);
}
for (Entry<String, ClassReader> entry : mKeep.entrySet()) {
ClassReader cr = entry.getValue();
- byte[] b = transform(cr, true /* stubNativesOnly */);
+ byte[] b = transform(cr, true);
String name = classNameToEntryPath(transformName(cr.getClassName()));
all.put(name, b);
}
@@ -292,7 +282,7 @@ public class AsmGenerator {
/**
* Utility method to get the JAR entry path from a Class name.
- * e.g. it returns someting like "com/foo/OuterClass$InnerClass1$InnerClass2.class"
+ * e.g. it returns something like "com/foo/OuterClass$InnerClass1$InnerClass2.class"
*/
private String classToEntryPath(Class<?> clazz) {
String name = "";
@@ -329,14 +319,14 @@ public class AsmGenerator {
String newName = transformName(className);
// transformName returns its input argument if there's no need to rename the class
- if (newName != className) {
+ if (!newName.equals(className)) {
mRenameCount++;
// This class is being renamed, so remove it from the list of classes not renamed.
mClassesNotRenamed.remove(className);
}
mLog.debug("Transform %s%s%s%s", className,
- newName == className ? "" : " (renamed to " + newName + ")",
+ newName.equals(className) ? "" : " (renamed to " + newName + ")",
hasNativeMethods ? " -- has natives" : "",
stubNativesOnly ? " -- stub natives only" : "");
@@ -344,15 +334,19 @@ public class AsmGenerator {
// original class reader.
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
- ClassVisitor cv = new RefactorClassAdapter(cw, mRefactorClasses);
- if (newName != className) {
+ ClassVisitor cv = cw;
+
+ if (mReplaceMethodCallsClasses.contains(className)) {
+ cv = new ReplaceMethodCallsAdapter(cv);
+ }
+
+ cv = new RefactorClassAdapter(cv, mRefactorClasses);
+ if (!newName.equals(className)) {
cv = new RenameClassAdapter(cv, className, newName);
}
- cv = new TransformClassAdapter(mLog, mStubMethods,
- mDeleteReturns.get(className),
- newName, cv,
- stubNativesOnly, stubNativesOnly || hasNativeMethods);
+ cv = new TransformClassAdapter(mLog, mStubMethods, mDeleteReturns.get(className),
+ newName, cv, stubNativesOnly);
Set<String> delegateMethods = mDelegateMethods.get(className);
if (delegateMethods != null && !delegateMethods.isEmpty()) {
@@ -365,7 +359,7 @@ public class AsmGenerator {
}
}
- cr.accept(cv, 0 /* flags */);
+ cr.accept(cv, 0);
return cw.toByteArray();
}
@@ -399,7 +393,7 @@ public class AsmGenerator {
*/
boolean hasNativeMethods(ClassReader cr) {
ClassHasNativeVisitor cv = new ClassHasNativeVisitor();
- cr.accept(cv, 0 /* flags */);
+ cr.accept(cv, 0);
return cv.hasNativeMethods();
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 29a5706..83fac85 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -23,6 +23,10 @@ import com.android.tools.layoutlib.java.IntegralToString;
import com.android.tools.layoutlib.java.Objects;
import com.android.tools.layoutlib.java.UnsafeByteSequence;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
/**
* Describes the work to be done by {@link AsmGenerator}.
*/
@@ -98,6 +102,17 @@ public final class CreateInfo implements ICreateInfo {
public String[] getJavaPkgClasses() {
return JAVA_PKG_CLASSES;
}
+
+ public Set<String> getExcludedClasses() {
+ String[] refactoredClasses = getJavaPkgClasses();
+ int count = refactoredClasses.length / 2 + EXCLUDED_CLASSES.length;
+ Set<String> excludedClasses = new HashSet<String>(count);
+ for (int i = 0; i < refactoredClasses.length; i+=2) {
+ excludedClasses.add(refactoredClasses[i]);
+ }
+ excludedClasses.addAll(Arrays.asList(EXCLUDED_CLASSES));
+ return excludedClasses;
+ }
//-----
/**
@@ -125,22 +140,32 @@ public final class CreateInfo implements ICreateInfo {
"android.app.Fragment#instantiate", //(Landroid/content/Context;Ljava/lang/String;Landroid/os/Bundle;)Landroid/app/Fragment;",
"android.content.res.Resources$Theme#obtainStyledAttributes",
"android.content.res.Resources$Theme#resolveAttribute",
+ "android.content.res.Resources$Theme#resolveAttributes",
+ "android.content.res.AssetManager#newTheme",
+ "android.content.res.AssetManager#deleteTheme",
+ "android.content.res.AssetManager#applyThemeStyle",
"android.content.res.TypedArray#getValueAt",
+ "android.content.res.TypedArray#obtain",
"android.graphics.BitmapFactory#finishDecode",
+ "android.graphics.Typeface#getSystemFontConfigLocation",
"android.os.Handler#sendMessageAtTime",
"android.os.HandlerThread#run",
- "android.os.Build#getString",
"android.text.format.DateFormat#is24HourFormat",
+ "android.util.Xml#newPullParser",
"android.view.Choreographer#getRefreshRate",
"android.view.Display#updateDisplayInfoLocked",
+ "android.view.Display#getWindowManager",
"android.view.LayoutInflater#rInflate",
"android.view.LayoutInflater#parseInclude",
"android.view.View#isInEditMode",
"android.view.ViewRootImpl#isInTouchMode",
"android.view.WindowManagerGlobal#getWindowManagerService",
"android.view.inputmethod.InputMethodManager#getInstance",
+ "android.view.MenuInflater#registerMenu",
+ "com.android.internal.view.menu.MenuBuilder#createNewMenuItem",
"com.android.internal.util.XmlUtils#convertValueToInt",
"com.android.internal.textservice.ITextServicesManager$Stub#asInterface",
+ "dalvik.system.VMRuntime#newUnpaddedArray"
};
/**
@@ -163,6 +188,7 @@ public final class CreateInfo implements ICreateInfo {
"android.graphics.DiscretePathEffect",
"android.graphics.DrawFilter",
"android.graphics.EmbossMaskFilter",
+ "android.graphics.FontFamily",
"android.graphics.LayerRasterizer",
"android.graphics.LightingColorFilter",
"android.graphics.LinearGradient",
@@ -186,7 +212,9 @@ public final class CreateInfo implements ICreateInfo {
"android.graphics.Typeface",
"android.graphics.Xfermode",
"android.os.SystemClock",
+ "android.os.SystemProperties",
"android.text.AndroidBidi",
+ "android.text.StaticLayout",
"android.text.format.Time",
"android.view.Display",
"libcore.icu.DateIntervalFormat",
@@ -229,6 +257,11 @@ public final class CreateInfo implements ICreateInfo {
"java.lang.UnsafeByteSequence", "com.android.tools.layoutlib.java.UnsafeByteSequence",
};
+ private final static String[] EXCLUDED_CLASSES =
+ new String[] {
+ "org.kxml2.io.KXmlParser"
+ };
+
/**
* List of classes for which the methods returning them should be deleted.
* The array contains a list of null terminated section starting with the name of the class
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
index 927be97..3d89c68 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateClassAdapter.java
@@ -32,10 +32,10 @@ public class DelegateClassAdapter extends ClassVisitor {
/** Suffix added to original methods. */
private static final String ORIGINAL_SUFFIX = "_Original";
- private static String CONSTRUCTOR = "<init>";
- private static String CLASS_INIT = "<clinit>";
+ private static final String CONSTRUCTOR = "<init>";
+ private static final String CLASS_INIT = "<clinit>";
- public final static String ALL_NATIVES = "<<all_natives>>";
+ public static final String ALL_NATIVES = "<<all_natives>>";
private final String mClassName;
private final Set<String> mDelegateMethods;
@@ -78,19 +78,16 @@ public class DelegateClassAdapter extends ClassVisitor {
mDelegateMethods.contains(name);
if (!useDelegate) {
- // Not creating a delegate for this method, pass it as-is from the reader
- // to the writer.
+ // Not creating a delegate for this method, pass it as-is from the reader to the writer.
return super.visitMethod(access, name, desc, signature, exceptions);
}
- if (useDelegate) {
- if (CONSTRUCTOR.equals(name) || CLASS_INIT.equals(name)) {
- // We don't currently support generating delegates for constructors.
- throw new UnsupportedOperationException(
- String.format(
- "Delegate doesn't support overriding constructor %1$s:%2$s(%3$s)", //$NON-NLS-1$
- mClassName, name, desc));
- }
+ if (CONSTRUCTOR.equals(name) || CLASS_INIT.equals(name)) {
+ // We don't currently support generating delegates for constructors.
+ throw new UnsupportedOperationException(
+ String.format(
+ "Delegate doesn't support overriding constructor %1$s:%2$s(%3$s)", //$NON-NLS-1$
+ mClassName, name, desc));
}
if (isNative) {
@@ -98,8 +95,8 @@ public class DelegateClassAdapter extends ClassVisitor {
access = access & ~Opcodes.ACC_NATIVE;
MethodVisitor mwDelegate = super.visitMethod(access, name, desc, signature, exceptions);
- DelegateMethodAdapter2 a = new DelegateMethodAdapter2(
- mLog, null /*mwOriginal*/, mwDelegate, mClassName, name, desc, isStatic);
+ DelegateMethodAdapter a = new DelegateMethodAdapter(
+ mLog, null, mwDelegate, mClassName, name, desc, isStatic);
// A native has no code to visit, so we need to generate it directly.
a.generateDelegateCode();
@@ -112,22 +109,16 @@ public class DelegateClassAdapter extends ClassVisitor {
// The content is the original method as-is from the reader.
// - A brand new implementation of SomeClass.MethodName() which calls to a
// non-existing method named SomeClass_Delegate.MethodName().
- // The implementation of this 'delegate' method is done in layoutlib_brigde.
+ // The implementation of this 'delegate' method is done in layoutlib_bridge.
int accessDelegate = access;
- // change access to public for the original one
- if (Main.sOptions.generatePublicAccess) {
- access &= ~(Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE);
- access |= Opcodes.ACC_PUBLIC;
- }
MethodVisitor mwOriginal = super.visitMethod(access, name + ORIGINAL_SUFFIX,
desc, signature, exceptions);
MethodVisitor mwDelegate = super.visitMethod(accessDelegate, name,
desc, signature, exceptions);
- DelegateMethodAdapter2 a = new DelegateMethodAdapter2(
+ return new DelegateMethodAdapter(
mLog, mwOriginal, mwDelegate, mClassName, name, desc, isStatic);
- return a;
}
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter2.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
index 0000b22..12690db 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter2.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DelegateMethodAdapter.java
@@ -71,7 +71,7 @@ import java.util.ArrayList;
* Instances of this class are not re-usable.
* The class adapter creates a new instance for each method.
*/
-class DelegateMethodAdapter2 extends MethodVisitor {
+class DelegateMethodAdapter extends MethodVisitor {
/** Suffix added to delegate classes. */
public static final String DELEGATE_SUFFIX = "_Delegate";
@@ -97,10 +97,10 @@ class DelegateMethodAdapter2 extends MethodVisitor {
private Object[] mDelegateLineNumber;
/**
- * Creates a new {@link DelegateMethodAdapter2} that will transform this method
+ * Creates a new {@link DelegateMethodAdapter} that will transform this method
* into a delegate call.
* <p/>
- * See {@link DelegateMethodAdapter2} for more details.
+ * See {@link DelegateMethodAdapter} for more details.
*
* @param log The logger object. Must not be null.
* @param mvOriginal The parent method writer to copy of the original method.
@@ -114,7 +114,7 @@ class DelegateMethodAdapter2 extends MethodVisitor {
* {@link Type#getArgumentTypes(String)})
* @param isStatic True if the method is declared static.
*/
- public DelegateMethodAdapter2(Log log,
+ public DelegateMethodAdapter(Log log,
MethodVisitor mvOriginal,
MethodVisitor mvDelegate,
String className,
@@ -138,7 +138,7 @@ class DelegateMethodAdapter2 extends MethodVisitor {
* (since they have no code to visit).
* <p/>
* Otherwise for non-native methods the {@link DelegateClassAdapter} simply needs to
- * return this instance of {@link DelegateMethodAdapter2} and let the normal visitor pattern
+ * return this instance of {@link DelegateMethodAdapter} and let the normal visitor pattern
* invoke it as part of the {@link ClassReader#accept(ClassVisitor, int)} workflow and then
* this method will be invoked from {@link MethodVisitor#visitEnd()}.
*/
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DependencyFinder.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DependencyFinder.java
index c988c70..7690fcd 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DependencyFinder.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/DependencyFinder.java
@@ -316,9 +316,7 @@ public class DependencyFinder {
// Add it to the dependency set for the currently visited class, as needed.
assert mCurrentDepSet != null;
- if (mCurrentDepSet != null) {
- mCurrentDepSet.add(className);
- }
+ mCurrentDepSet.add(className);
}
/**
@@ -527,7 +525,8 @@ public class DependencyFinder {
// field instruction
@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
- // name is the field's name.
+ // owner is the class that declares the field.
+ considerName(owner);
// desc is the field's descriptor (see Type).
considerDesc(desc);
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
index 9387814..e49a668 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
@@ -16,6 +16,8 @@
package com.android.tools.layoutlib.create;
+import java.util.Set;
+
/**
* Interface describing the work to be done by {@link AsmGenerator}.
*/
@@ -69,4 +71,6 @@ public interface ICreateInfo {
* The list can be empty but must not be null.
*/
public abstract String[] getJavaPkgClasses();
+
+ public abstract Set<String> getExcludedClasses();
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
index a79fba1..cd3c39e 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java
@@ -18,6 +18,7 @@ package com.android.tools.layoutlib.create;
import java.io.IOException;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -49,7 +50,6 @@ import java.util.Set;
public class Main {
public static class Options {
- public boolean generatePublicAccess = true;
public boolean listAllDeps = false;
public boolean listOnlyMissingDeps = false;
}
@@ -64,7 +64,7 @@ public class Main {
String[] osDestJar = { null };
if (!processArgs(log, args, osJarPath, osDestJar)) {
- log.error("Usage: layoutlib_create [-v] [-p] output.jar input.jar ...");
+ log.error("Usage: layoutlib_create [-v] output.jar input.jar ...");
log.error("Usage: layoutlib_create [-v] [--list-deps|--missing-deps] input.jar ...");
System.exit(1);
}
@@ -88,7 +88,7 @@ public class Main {
try {
CreateInfo info = new CreateInfo();
- Set<String> excludeClasses = getExcludedClasses(info);
+ Set<String> excludeClasses = info.getExcludedClasses();
AsmGenerator agen = new AsmGenerator(log, osDestJar, info);
AsmAnalyzer aa = new AsmAnalyzer(log, osJarPath, agen,
@@ -114,6 +114,9 @@ public class Main {
"android.os.*", // for android.os.Handler
"android.database.ContentObserver", // for Digital clock
"com.android.i18n.phonenumbers.*", // for TextView with autolink attribute
+ "android.app.DatePickerDialog", // b.android.com/28318
+ "android.app.TimePickerDialog", // b.android.com/61515
+ "com.android.internal.view.menu.ActionMenu",
},
excludeClasses,
new String[] {
@@ -154,16 +157,6 @@ public class Main {
return 1;
}
- private static Set<String> getExcludedClasses(CreateInfo info) {
- String[] refactoredClasses = info.getJavaPkgClasses();
- Set<String> excludedClasses = new HashSet<String>(refactoredClasses.length);
- for (int i = 0; i < refactoredClasses.length; i+=2) {
- excludedClasses.add(refactoredClasses[i]);
- }
- return excludedClasses;
-
- }
-
private static int listDeps(ArrayList<String> osJarPath, Log log) {
DependencyFinder df = new DependencyFinder(log);
try {
@@ -190,12 +183,9 @@ public class Main {
private static boolean processArgs(Log log, String[] args,
ArrayList<String> osJarPath, String[] osDestJar) {
boolean needs_dest = true;
- for (int i = 0; i < args.length; i++) {
- String s = args[i];
+ for (String s : args) {
if (s.equals("-v")) {
log.setVerbose(true);
- } else if (s.equals("-p")) {
- sOptions.generatePublicAccess = false;
} else if (s.equals("--list-deps")) {
sOptions.listAllDeps = true;
needs_dest = false;
@@ -209,7 +199,7 @@ public class Main {
osJarPath.add(s);
}
} else {
- log.error("Unknow argument: %s", s);
+ log.error("Unknown argument: %s", s);
return false;
}
}
@@ -223,8 +213,6 @@ public class Main {
return false;
}
- sOptions.generatePublicAccess = false;
-
return true;
}
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/OverrideMethod.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/OverrideMethod.java
index a6aff99..4c87b3c 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/OverrideMethod.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/OverrideMethod.java
@@ -36,6 +36,7 @@ public final class OverrideMethod {
* Sets the default listener for all methods not specifically handled.
* Null means to do nothing.
*/
+ @SuppressWarnings("UnusedDeclaration") // Used by Bridge by reflection for debug purposes.
public static void setDefaultListener(MethodListener listener) {
sDefaultListener = listener;
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RenameClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RenameClassAdapter.java
index 661074c..40bd126 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RenameClassAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/RenameClassAdapter.java
@@ -73,7 +73,7 @@ public class RenameClassAdapter extends AbstractClassAdapter {
return mNewName;
}
- if (mOldBase != mOldName && type.equals(mOldBase)) {
+ if (!mOldBase.equals(mOldName) && type.equals(mOldBase)) {
return mNewBase;
}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
new file mode 100644
index 0000000..9c6fbac
--- /dev/null
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/ReplaceMethodCallsAdapter.java
@@ -0,0 +1,173 @@
+/*
+ * 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.tools.layoutlib.create;
+
+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.List;
+import java.util.Locale;
+import java.util.Set;
+
+/**
+ * Replaces calls to certain methods that do not exist in the Desktop VM. Useful for methods in the
+ * "java" package.
+ */
+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<String>(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<MethodReplacer>(2);
+
+ private static final String ANDROID_LOCALE_CLASS =
+ "com/android/layoutlib/bridge/android/AndroidLocale";
+
+ private static final String JAVA_LOCALE_CLASS = "java/util/Locale";
+ private static final Type STRING = Type.getType(String.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) {
+ return "java/lang/System".equals(owner) && "arraycopy".equals(name) &&
+ ARRAYCOPY_DESCRIPTORS.contains(desc);
+ }
+
+ @Override
+ public void replace(int[] opcode, String[] methodInformation) {
+ assert methodInformation.length == 3 && isNeeded(methodInformation[0], methodInformation[1], methodInformation[2])
+ && opcode.length == 1;
+ methodInformation[2] = "(Ljava/lang/Object;ILjava/lang/Object;II)V";
+ }
+ });
+
+ // Case 2: java.util.Locale.toLanguageTag() and java.util.Locale.getScript()
+ METHOD_REPLACERS.add(new MethodReplacer() {
+
+ String LOCALE_TO_STRING = Type.getMethodDescriptor(STRING, Type.getType(Locale.class));
+
+ @Override
+ public boolean isNeeded(String owner, String name, String desc) {
+ return JAVA_LOCALE_CLASS.equals(owner) && "()Ljava/lang/String;".equals(desc) &&
+ ("toLanguageTag".equals(name) || "getScript".equals(name));
+ }
+
+ @Override
+ public void replace(int[] opcode, String[] methodInformation) {
+ assert methodInformation.length == 3 && isNeeded(methodInformation[0], methodInformation[1], methodInformation[2])
+ && opcode.length == 1;
+ opcode[0] = Opcodes.INVOKESTATIC;
+ methodInformation[0] = ANDROID_LOCALE_CLASS;
+ methodInformation[2] = LOCALE_TO_STRING;
+ }
+ });
+
+ // Case 3: java.util.Locale.adjustLanguageCode() or java.util.Locale.forLanguageTag()
+ METHOD_REPLACERS.add(new MethodReplacer() {
+
+ private final String STRING_TO_STRING = Type.getMethodDescriptor(STRING, STRING);
+ private final String STRING_TO_LOCALE = Type.getMethodDescriptor(
+ Type.getType(Locale.class), STRING);
+
+ @Override
+ public boolean isNeeded(String owner, String name, String desc) {
+ return JAVA_LOCALE_CLASS.equals(owner) &&
+ ("adjustLanguageCode".equals(name) && desc.equals(STRING_TO_STRING) ||
+ "forLanguageTag".equals(name) && desc.equals(STRING_TO_LOCALE));
+ }
+
+ @Override
+ public void replace(int[] opcode, String[] methodInformation) {
+ assert methodInformation.length == 3 && isNeeded(methodInformation[0], methodInformation[1], methodInformation[2])
+ && opcode.length == 1;
+ methodInformation[0] = ANDROID_LOCALE_CLASS;
+ }
+ });
+ }
+
+ public static boolean isReplacementNeeded(String owner, String name, String desc) {
+ for (MethodReplacer replacer : METHOD_REPLACERS) {
+ if (replacer.isNeeded(owner, name, desc)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public ReplaceMethodCallsAdapter(ClassVisitor cv) {
+ super(Opcodes.ASM4, cv);
+ }
+
+ @Override
+ public MethodVisitor visitMethod(int access, String name, String desc, String signature,
+ String[] exceptions) {
+ return new MyMethodVisitor(super.visitMethod(access, name, desc, signature, exceptions));
+ }
+
+ private class MyMethodVisitor extends MethodVisitor {
+
+ public MyMethodVisitor(MethodVisitor mv) {
+ super(Opcodes.ASM4, mv);
+ }
+
+ @Override
+ public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+ for (MethodReplacer replacer : METHOD_REPLACERS) {
+ if (replacer.isNeeded(owner, name, desc)) {
+ String[] methodInformation = {owner, name, desc};
+ int[] opcodeOut = {opcode};
+ replacer.replace(opcodeOut, methodInformation);
+ opcode = opcodeOut[0];
+ owner = methodInformation[0];
+ name = methodInformation[1];
+ desc = methodInformation[2];
+ break;
+ }
+ }
+ super.visitMethodInsn(opcode, owner, name, desc);
+ }
+ }
+
+ private interface MethodReplacer {
+ public boolean isNeeded(String owner, String name, String desc);
+
+ /**
+ * This method must update the arrays with the new values of the method attributes -
+ * opcode, owner, name and desc.
+ * @param opcode This array should contain the original value of the opcode. The value is
+ * modified by the method if needed. The size of the array must be 1.
+ *
+ * @param methodInformation This array should contain the original values of the method
+ * attributes - owner, name and desc in that order. The values
+ * may be modified as needed. The size of the array must be 3.
+ */
+ public void replace(int[] opcode, String[] methodInformation);
+ }
+}
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java
index 51e7535..416b73a 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/StubMethodAdapter.java
@@ -29,8 +29,8 @@ import org.objectweb.asm.Type;
*/
class StubMethodAdapter extends MethodVisitor {
- private static String CONSTRUCTOR = "<init>";
- private static String CLASS_INIT = "<clinit>";
+ private static final String CONSTRUCTOR = "<init>";
+ private static final String CLASS_INIT = "<clinit>";
/** The parent method writer */
private MethodVisitor mParentVisitor;
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java
index d45a183..d9ecf98 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/TransformClassAdapter.java
@@ -17,7 +17,6 @@
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 org.objectweb.asm.Type;
@@ -40,19 +39,16 @@ class TransformClassAdapter extends ClassVisitor {
/**
* Creates a new class adapter that will stub some or all methods.
- * @param logger
* @param stubMethods list of method signatures to always stub out
* @param deleteReturns list of types that trigger the deletion of methods returning them.
* @param className The name of the class being modified
* @param cv The parent class writer visitor
* @param stubNativesOnly True if only native methods should be stubbed. False if all
* methods should be stubbed.
- * @param hasNative True if the method has natives, in which case its access should be
- * changed.
*/
public TransformClassAdapter(Log logger, Set<String> stubMethods,
Set<String> deleteReturns, String className, ClassVisitor cv,
- boolean stubNativesOnly, boolean hasNative) {
+ boolean stubNativesOnly) {
super(Opcodes.ASM4, cv);
mLog = logger;
mStubMethods = stubMethods;
@@ -70,11 +66,6 @@ class TransformClassAdapter extends ClassVisitor {
// This class might be being renamed.
name = mClassName;
- // remove protected or private and set as public
- if (Main.sOptions.generatePublicAccess) {
- access = access & ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED);
- access |= Opcodes.ACC_PUBLIC;
- }
// remove final
access = access & ~Opcodes.ACC_FINAL;
// note: leave abstract classes as such
@@ -87,11 +78,6 @@ class TransformClassAdapter extends ClassVisitor {
/* Visits the header of an inner class. */
@Override
public void visitInnerClass(String name, String outerName, String innerName, int access) {
- // remove protected or private and set as public
- if (Main.sOptions.generatePublicAccess) {
- access = access & ~(Opcodes.ACC_PRIVATE | Opcodes.ACC_PROTECTED);
- access |= Opcodes.ACC_PUBLIC;
- }
// remove final
access = access & ~Opcodes.ACC_FINAL;
// note: leave abstract classes as such
@@ -119,12 +105,6 @@ class TransformClassAdapter extends ClassVisitor {
String methodSignature = mClassName.replace('/', '.') + "#" + name;
- // change access to public
- if (Main.sOptions.generatePublicAccess) {
- access &= ~(Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE);
- access |= Opcodes.ACC_PUBLIC;
- }
-
// remove final
access = access & ~Opcodes.ACC_FINAL;
@@ -155,18 +135,6 @@ class TransformClassAdapter extends ClassVisitor {
}
}
- /* Visits a field. Makes it public. */
- @Override
- public FieldVisitor visitField(int access, String name, String desc, String signature,
- Object value) {
- // change access to public
- if (Main.sOptions.generatePublicAccess) {
- access &= ~(Opcodes.ACC_PROTECTED | Opcodes.ACC_PRIVATE);
- access |= Opcodes.ACC_PUBLIC;
- }
- return super.visitField(access, name, desc, signature, value);
- }
-
/**
* Extracts the return {@link Type} of this descriptor.
*/
diff --git a/tools/layoutlib/create/tests/Android.mk b/tools/layoutlib/create/tests/Android.mk
new file mode 100644
index 0000000..c197d57
--- /dev/null
+++ b/tools/layoutlib/create/tests/Android.mk
@@ -0,0 +1,35 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+# Only compile source java files in this lib.
+LOCAL_SRC_FILES := $(call all-java-files-under, com)
+
+LOCAL_JAVA_RESOURCE_DIRS := data mock_data
+
+LOCAL_MODULE := layoutlib-create-tests
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_JAVA_LIBRARIES := layoutlib_create junit
+LOCAL_STATIC_JAVA_LIBRARIES := asm-4.0
+
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+# Copy the jar to DIST_DIR for sdk builds
+$(call dist-for-goals, sdk win_sdk, $(LOCAL_INSTALLED_MODULE))
+
+# Build all sub-directories
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
index 7ec0d38..78e2c48 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmAnalyzerTest.java
@@ -83,6 +83,7 @@ public class AsmAnalyzerTest {
"mock_android.dummy.InnerTest$MyStaticInnerClass",
"mock_android.dummy.InnerTest$NotStaticInner1",
"mock_android.dummy.InnerTest$NotStaticInner2",
+ "mock_android.util.EmptyArray",
"mock_android.view.View",
"mock_android.view.ViewGroup",
"mock_android.view.ViewGroup$LayoutParams",
@@ -217,15 +218,16 @@ public class AsmAnalyzerTest {
TreeMap<String, ClassReader> in_deps = new TreeMap<String, ClassReader>();
TreeMap<String, ClassReader> out_deps = new TreeMap<String, ClassReader>();
- ClassReader cr = mAa.findClass("mock_android.widget.TableLayout", zipClasses, keep);
+ ClassReader cr = mAa.findClass("mock_android.widget.LinearLayout", zipClasses, keep);
DependencyVisitor visitor = mAa.getVisitor(zipClasses, keep, new_keep, in_deps, out_deps);
// get first level dependencies
cr.accept(visitor, 0 /* flags */);
assertArrayEquals(new String[] {
+ "mock_android.util.EmptyArray",
"mock_android.view.ViewGroup",
- "mock_android.widget.TableLayout$LayoutParams",
+ "mock_android.widget.LinearLayout$LayoutParams",
},
out_deps.keySet().toArray());
@@ -255,7 +257,7 @@ public class AsmAnalyzerTest {
assertArrayEquals(new String[] { }, out_deps.keySet().toArray());
assertArrayEquals(new String[] {
- "mock_android.widget.TableLayout",
+ "mock_android.widget.LinearLayout",
}, keep.keySet().toArray());
}
}
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
index 0dbc238..cf91386 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/AsmGeneratorTest.java
@@ -19,6 +19,7 @@ package com.android.tools.layoutlib.create;
import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.After;
@@ -36,6 +37,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Map;
@@ -119,6 +121,11 @@ public class AsmGeneratorTest {
}
@Override
+ public Set<String> getExcludedClasses() {
+ return null;
+ }
+
+ @Override
public String[] getDeleteReturns() {
// methods deleted from their return type.
return new String[0];
@@ -184,6 +191,11 @@ public class AsmGeneratorTest {
}
@Override
+ public Set<String> getExcludedClasses() {
+ return Collections.singleton("java.lang.JavaClass");
+ }
+
+ @Override
public String[] getDeleteReturns() {
// methods deleted from their return type.
return new String[0];
@@ -217,6 +229,80 @@ public class AsmGeneratorTest {
filesFound.keySet().toArray());
}
+ @Test
+ public void testClassExclusion() throws IOException, LogAbortException {
+ ICreateInfo ci = new ICreateInfo() {
+ @Override
+ public Class<?>[] getInjectedClasses() {
+ return new Class<?>[0];
+ }
+
+ @Override
+ public String[] getDelegateMethods() {
+ return new String[0];
+ }
+
+ @Override
+ public String[] getDelegateClassNatives() {
+ return new String[0];
+ }
+
+ @Override
+ public String[] getOverriddenMethods() {
+ // methods to force override
+ return new String[0];
+ }
+
+ @Override
+ public String[] getRenamedClasses() {
+ // classes to rename (so that we can replace them)
+ return new String[0];
+ }
+
+ @Override
+ public String[] getJavaPkgClasses() {
+ // classes to refactor (so that we can replace them)
+ return new String[0];
+ }
+
+ @Override
+ public Set<String> getExcludedClasses() {
+ Set<String> set = new HashSet<String>(2);
+ set.add("mock_android.dummy.InnerTest");
+ set.add("java.lang.JavaClass");
+ return set;
+ }
+
+ @Override
+ public String[] getDeleteReturns() {
+ // methods deleted from their return type.
+ return new String[0];
+ }
+ };
+
+ AsmGenerator agen = new AsmGenerator(mLog, mOsDestJar, ci);
+ Set<String> excludedClasses = ci.getExcludedClasses();
+ AsmAnalyzer aa = new AsmAnalyzer(mLog, mOsJarPath, agen,
+ null, // derived from
+ new String[] { // include classes
+ "**"
+ },
+ excludedClasses,
+ new String[] { /* include files */
+ "mock_android/data/data*"
+ });
+ aa.analyze();
+ agen.generate();
+ Map<String, ClassReader> output = new TreeMap<String, ClassReader>();
+ Map<String, InputStream> filesFound = new TreeMap<String, InputStream>();
+ parseZip(mOsDestJar, output, filesFound);
+ for (String s : output.keySet()) {
+ assertFalse(excludedClasses.contains(s));
+ }
+ assertArrayEquals(new String[] {"mock_android/data/dataFile"},
+ filesFound.keySet().toArray());
+ }
+
private void parseZip(String jarPath,
Map<String, ClassReader> classes,
Map<String, InputStream> filesFound) throws IOException {
diff --git a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
index 6e120ce..648cea4 100644
--- a/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
+++ b/tools/layoutlib/create/tests/com/android/tools/layoutlib/create/DelegateClassAdapterTest.java
@@ -116,9 +116,16 @@ public class DelegateClassAdapterTest {
// Check that the native method does NOT have the new annotation
Method[] m = clazz2.getDeclaredMethods();
- assertEquals("native_instance", m[2].getName());
- assertTrue(Modifier.isNative(m[2].getModifiers()));
- Annotation[] a = m[2].getAnnotations();
+ Method nativeInstanceMethod = null;
+ for (Method method : m) {
+ if ("native_instance".equals(method.getName())) {
+ nativeInstanceMethod = method;
+ break;
+ }
+ }
+ assertNotNull(nativeInstanceMethod);
+ assertTrue(Modifier.isNative(nativeInstanceMethod.getModifiers()));
+ Annotation[] a = nativeInstanceMethod.getAnnotations();
assertEquals(0, a.length);
}
};
@@ -130,7 +137,7 @@ public class DelegateClassAdapterTest {
}
/**
- * {@link DelegateMethodAdapter2} does not support overriding constructors yet,
+ * {@link DelegateMethodAdapter} does not support overriding constructors yet,
* so this should fail with an {@link UnsupportedOperationException}.
*
* Although not tested here, the message of the exception should contain the
@@ -184,9 +191,16 @@ public class DelegateClassAdapterTest {
// Check that the native method now has the new annotation and is not native
Method[] m = clazz2.getDeclaredMethods();
- assertEquals("native_instance", m[2].getName());
- assertFalse(Modifier.isNative(m[2].getModifiers()));
- Annotation[] a = m[2].getAnnotations();
+ Method nativeInstanceMethod = null;
+ for (Method method : m) {
+ if ("native_instance".equals(method.getName())) {
+ nativeInstanceMethod = method;
+ break;
+ }
+ }
+ assertNotNull(nativeInstanceMethod);
+ assertFalse(Modifier.isNative(nativeInstanceMethod.getModifiers()));
+ Annotation[] a = nativeInstanceMethod.getAnnotations();
assertEquals("LayoutlibDelegate", a[0].annotationType().getSimpleName());
}
};
@@ -237,13 +251,8 @@ public class DelegateClassAdapterTest {
assertEquals(4+10+20, callGet(o2, 10, 20));
assertEquals(1+10+20, callGet_Original(o2, 10, 20));
- // The original Outer has a private method that is
- // delegated. We should be able to call both the delegate
- // and the original (which is now public).
- assertEquals("outerPrivateMethod",
- callMethod(o2, "privateMethod_Original", false /*makePublic*/));
-
- // The original method is private, so by default we can't access it
+ // The original Outer has a private method,
+ // so by default we can't access it.
boolean gotIllegalAccessException = false;
try {
callMethod(o2, "privateMethod", false /*makePublic*/);
@@ -251,16 +260,24 @@ public class DelegateClassAdapterTest {
gotIllegalAccessException = true;
}
assertTrue(gotIllegalAccessException);
- // Try again, but now making it accessible
- assertEquals("outerPrivate_Delegate",
- callMethod(o2, "privateMethod", true /*makePublic*/));
+
+ // The private method from original Outer has been
+ // delegated. The delegate generated should have the
+ // same access.
+ gotIllegalAccessException = false;
+ try {
+ assertEquals("outerPrivateMethod",
+ callMethod(o2, "privateMethod_Original", false /*makePublic*/));
+ } catch (IllegalAccessException e) {
+ gotIllegalAccessException = true;
+ }
+ assertTrue(gotIllegalAccessException);
// Check the inner class. Since it's not a static inner class, we need
// to use the hidden constructor that takes the outer class as first parameter.
Class<?> innerClazz2 = loadClass(INNER_CLASS_NAME);
- Constructor<?> innerCons = innerClazz2.getConstructor(
- new Class<?>[] { outerClazz2 });
- Object i2 = innerCons.newInstance(new Object[] { o2 });
+ Constructor<?> innerCons = innerClazz2.getConstructor(outerClazz2);
+ Object i2 = innerCons.newInstance(o2);
assertNotNull(i2);
// The original Inner.get returns 3+10+20,
@@ -344,10 +361,10 @@ public class DelegateClassAdapterTest {
*/
public int callGet(Object instance, int a, long b) throws Exception {
Method m = instance.getClass().getMethod("get",
- new Class<?>[] { int.class, long.class } );
+ int.class, long.class);
- Object result = m.invoke(instance, new Object[] { a, b });
- return ((Integer) result).intValue();
+ Object result = m.invoke(instance, a, b);
+ return (Integer) result;
}
/**
@@ -356,10 +373,10 @@ public class DelegateClassAdapterTest {
*/
public int callGet_Original(Object instance, int a, long b) throws Exception {
Method m = instance.getClass().getMethod("get_Original",
- new Class<?>[] { int.class, long.class } );
+ int.class, long.class);
- Object result = m.invoke(instance, new Object[] { a, b });
- return ((Integer) result).intValue();
+ Object result = m.invoke(instance, a, b);
+ return (Integer) result;
}
/**
@@ -388,10 +405,10 @@ public class DelegateClassAdapterTest {
*/
public int callAdd(Object instance, int a, int b) throws Exception {
Method m = instance.getClass().getMethod("add",
- new Class<?>[] { int.class, int.class });
+ int.class, int.class);
- Object result = m.invoke(instance, new Object[] { a, b });
- return ((Integer) result).intValue();
+ Object result = m.invoke(instance, a, b);
+ return (Integer) result;
}
/**
@@ -401,10 +418,10 @@ public class DelegateClassAdapterTest {
public int callCallNativeInstance(Object instance, int a, double d, Object[] o)
throws Exception {
Method m = instance.getClass().getMethod("callNativeInstance",
- new Class<?>[] { int.class, double.class, Object[].class });
+ int.class, double.class, Object[].class);
- Object result = m.invoke(instance, new Object[] { a, d, o });
- return ((Integer) result).intValue();
+ Object result = m.invoke(instance, a, d, o);
+ return (Integer) result;
}
public abstract void testModifiedInstance() throws Exception;
@@ -442,8 +459,8 @@ public class DelegateClassAdapterTest {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
// next 2 lines do: TraceClassVisitor tcv = new TraceClassVisitor(pw);
- Constructor<?> cons = tcvClass.getConstructor(new Class<?>[] { pw.getClass() });
- Object tcv = cons.newInstance(new Object[] { pw });
+ Constructor<?> cons = tcvClass.getConstructor(pw.getClass());
+ Object tcv = cons.newInstance(pw);
ClassReader cr2 = new ClassReader(bytes);
cr2.accept((ClassVisitor) tcv, 0 /* flags */);
@@ -452,8 +469,7 @@ public class DelegateClassAdapterTest {
}
// Re-throw exception with new message
- RuntimeException ex = new RuntimeException(sb.toString(), t);
- return ex;
+ return new RuntimeException(sb.toString(), t);
} catch (Throwable ignore) {
// In case of problem, just throw the original exception as-is.
return t;
diff --git a/tools/layoutlib/create/tests/data/mock_android.jar b/tools/layoutlib/create/tests/data/mock_android.jar
index 8dd0481..c6ca3c4 100644
--- a/tools/layoutlib/create/tests/data/mock_android.jar
+++ b/tools/layoutlib/create/tests/data/mock_android.jar
Binary files differ
diff --git a/tools/layoutlib/create/tests/mock_data/mock_android/util/EmptyArray.java b/tools/layoutlib/create/tests/mock_data/mock_android/util/EmptyArray.java
new file mode 100644
index 0000000..aaeebf6
--- /dev/null
+++ b/tools/layoutlib/create/tests/mock_data/mock_android/util/EmptyArray.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
+ *
+ * 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 mock_android.util;
+
+import java.lang.JavaClass;
+
+public class EmptyArray {
+
+ public static final Object[] OBJECT = new Object[0];
+}
diff --git a/tools/layoutlib/create/tests/mock_data/mock_android/widget/LinearLayout.java b/tools/layoutlib/create/tests/mock_data/mock_android/widget/LinearLayout.java
index 3870a63..af56c4b 100644
--- a/tools/layoutlib/create/tests/mock_data/mock_android/widget/LinearLayout.java
+++ b/tools/layoutlib/create/tests/mock_data/mock_android/widget/LinearLayout.java
@@ -16,11 +16,13 @@
package mock_android.widget;
+import mock_android.util.EmptyArray;
import mock_android.view.ViewGroup;
public class LinearLayout extends ViewGroup {
- public class LayoutParams extends mock_android.view.ViewGroup.LayoutParams {
+ Object[] mObjects = EmptyArray.OBJECT;
+ public class LayoutParams extends MarginLayoutParams {
}
diff --git a/tools/layoutlib/rename_font/README b/tools/layoutlib/rename_font/README
new file mode 100644
index 0000000..600b756
--- /dev/null
+++ b/tools/layoutlib/rename_font/README
@@ -0,0 +1,9 @@
+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/tools/layoutlib/rename_font/Roboto-Regular.ttf b/tools/layoutlib/rename_font/Roboto-Regular.ttf
new file mode 100644
index 0000000..7469063
--- /dev/null
+++ b/tools/layoutlib/rename_font/Roboto-Regular.ttf
Binary files differ
diff --git a/tools/layoutlib/rename_font/build_font.py b/tools/layoutlib/rename_font/build_font.py
new file mode 100755
index 0000000..c747d92
--- /dev/null
+++ b/tools/layoutlib/rename_font/build_font.py
@@ -0,0 +1,224 @@
+#!/usr/bin/env python
+
+# 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 = ['-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.maxint
+ 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.maxint
+ 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 = fonts_iterator.next()
+ 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 is '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 is 'Regular'
+
+
+def get_version(string):
+ # The string must begin with 'Version n.nn '
+ # to extract n.nn, we return the second entry in the split strings.
+ string = string.strip()
+ if not string.startswith('Version '):
+ raise InvalidFontException('mal-formed font version')
+ return sanitize(string.split()[1])
+
+
+def sanitize(string):
+ return re.sub(r'[^\w-]+', '', string)
+
+if __name__ == '__main__':
+ main(sys.argv[1:])
diff --git a/tools/layoutlib/rename_font/build_font_single.py b/tools/layoutlib/rename_font/build_font_single.py
new file mode 100755
index 0000000..5f7dad9
--- /dev/null
+++ b/tools/layoutlib/rename_font/build_font_single.py
@@ -0,0 +1,208 @@
+#!/usr/bin/env python
+
+# 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) 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
+
+# A list of extensions to process.
+EXTENSIONS = ['.ttf', '.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 = ['-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:
+ # 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_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.maxint
+ 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.maxint
+ 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 = fonts_iterator.next()
+ 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 is '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 is 'Regular'
+
+
+def get_version(string):
+ # The string must begin with 'Version n.nn '
+ # to extract n.nn, we return the second entry in the split strings.
+ string = string.strip()
+ if not string.startswith('Version '):
+ raise InvalidFontException('mal-formed font version')
+ return sanitize(string.split()[1])
+
+
+def sanitize(string):
+ return re.sub(r'[^\w-]+', '', string)
+
+if __name__ == '__main__':
+ main(sys.argv[1:])
diff --git a/tools/layoutlib/rename_font/test.py b/tools/layoutlib/rename_font/test.py
new file mode 100755
index 0000000..2ffddf4
--- /dev/null
+++ b/tools/layoutlib/rename_font/test.py
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+
+"""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()