├── .github
└── workflows
│ └── build_test.yml
├── .gitignore
├── .idea
└── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── RELEASING.md
├── blessedDeps.gradle
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── libs
├── rt.jar
└── tools.jar
├── paris-annotations
├── .gitignore
├── build.gradle
├── gradle.properties
└── src
│ └── main
│ └── java
│ └── com
│ └── airbnb
│ └── paris
│ └── annotations
│ ├── AfterStyle.kt
│ ├── Attr.kt
│ ├── BeforeStyle.kt
│ ├── Fraction.kt
│ ├── GeneratedStyleableClass.kt
│ ├── GeneratedStyleableModule.kt
│ ├── LayoutDimension.kt
│ ├── ParisConfig.java
│ ├── Style.kt
│ ├── Styleable.kt
│ └── StyleableChild.kt
├── paris-processor
├── .gitignore
├── build.gradle
├── gradle.properties
└── src
│ ├── main
│ ├── java
│ │ └── com
│ │ │ └── airbnb
│ │ │ └── paris
│ │ │ └── processor
│ │ │ ├── BaseProcessor.kt
│ │ │ ├── Config.kt
│ │ │ ├── Format.kt
│ │ │ ├── KspProcessorProvider.kt
│ │ │ ├── ParisProcessor.kt
│ │ │ ├── RFinder.kt
│ │ │ ├── StyleablesTree.kt
│ │ │ ├── Timer.kt
│ │ │ ├── android_resource_scanner
│ │ │ ├── AndroidResourceId.kt
│ │ │ ├── JavacResourceScanner.kt
│ │ │ ├── KspResourceScanner.kt
│ │ │ └── ResourceScanner.kt
│ │ │ ├── framework
│ │ │ ├── AndroidClassNames.kt
│ │ │ ├── JavaPoetExtensions.kt
│ │ │ ├── JavaSkyMemoizer.kt
│ │ │ ├── KotlinPoetConverters.kt
│ │ │ ├── KotlinPoetExtensions.kt
│ │ │ ├── Log.kt
│ │ │ ├── Memoizer.kt
│ │ │ ├── SkyExtensions.kt
│ │ │ ├── SkyJavaClass.kt
│ │ │ ├── SkyKotlinFile.kt
│ │ │ └── models
│ │ │ │ ├── SkyMethodModel.kt
│ │ │ │ ├── SkyModel.kt
│ │ │ │ ├── SkyPropertyModel.kt
│ │ │ │ └── SkyStaticPropertyModel.kt
│ │ │ ├── models
│ │ │ ├── AfterStyleInfo.kt
│ │ │ ├── AttrInfo.kt
│ │ │ ├── BaseStyleableInfo.kt
│ │ │ ├── BeforeStyleInfo.kt
│ │ │ ├── StyleInfo.kt
│ │ │ ├── StyleStaticMethodInfo.kt
│ │ │ ├── StyleStaticPropertyInfo.kt
│ │ │ ├── StyleableChildInfo.kt
│ │ │ └── StyleableInfo.kt
│ │ │ ├── utils
│ │ │ ├── ParisProcessorUtils.kt
│ │ │ └── XProcessingUtils.kt
│ │ │ └── writers
│ │ │ ├── BaseStyleBuilderJavaClass.kt
│ │ │ ├── ModuleJavaClass.kt
│ │ │ ├── ParisJavaClass.kt
│ │ │ ├── StyleApplierJavaClass.kt
│ │ │ ├── StyleBuilderJavaClass.kt
│ │ │ └── StyleExtensionsKotlinFile.kt
│ └── resources
│ │ └── META-INF
│ │ └── services
│ │ ├── com.google.devtools.ksp.processing.SymbolProcessorProvider
│ │ └── javax.annotation.processing.Processor
│ └── test
│ ├── java
│ └── KspResourceScannerTest.kt
│ └── kotlin
│ └── com
│ └── airbnb
│ └── paris
│ └── processor
│ └── utils
│ └── ParisProcessorUtilsTest.kt
├── paris-test-lib
├── .gitignore
├── README.md
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── res
│ └── values
│ └── attrs.xml
├── paris-test
├── .gitignore
├── build.gradle
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── airbnb
│ │ └── paris
│ │ └── test
│ │ ├── StyleApplierUtilsTest.kt
│ │ └── ViewStyleBuilderTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── airbnb
│ │ │ └── paris
│ │ │ └── test
│ │ │ ├── MyOtherView.java
│ │ │ ├── PackageConfig.kt
│ │ │ └── R2.java
│ └── res
│ │ ├── color
│ │ └── format_color_state_list.xml
│ │ ├── drawable
│ │ └── format_drawable.xml
│ │ ├── font
│ │ └── format_font.ttf
│ │ └── values
│ │ ├── attrs.xml
│ │ ├── booleans.xml
│ │ ├── formats.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ ├── java
│ └── com
│ │ └── airbnb
│ │ └── paris
│ │ └── test
│ │ ├── CompilationMode.kt
│ │ ├── ParisProcessorTest.kt
│ │ └── ResourceTest.kt
│ └── resources
│ ├── at_style_style_field
│ ├── input
│ │ ├── MyView.java
│ │ └── PackageInfo.java
│ └── output
│ │ ├── MyViewStyleApplier.java
│ │ └── Paris.java
│ ├── attr_requires_api
│ ├── input
│ │ ├── MyView.java
│ │ └── PackageInfo.java
│ └── output
│ │ ├── MyViewStyleApplier.java
│ │ └── Paris.java
│ ├── attr_requires_api_default_value
│ ├── input
│ │ ├── MyView.java
│ │ └── PackageInfo.java
│ └── output
│ │ ├── MyViewStyleApplier.java
│ │ └── Paris.java
│ ├── attrs
│ ├── input
│ │ ├── MyView.java
│ │ └── PackageInfo.java
│ └── output
│ │ ├── MyViewStyleApplier.java
│ │ └── Paris.java
│ ├── attrs_r_class_import_as_type_alias
│ └── input
│ │ ├── MyView.kt
│ │ └── PackageInfo.java
│ ├── attrs_r_class_import_fully_qualified
│ └── input
│ │ ├── MyView.kt
│ │ └── PackageInfo.java
│ ├── default_values
│ ├── input
│ │ ├── MyView.java
│ │ └── PackageInfo.java
│ └── output
│ │ ├── MyViewStyleApplier.java
│ │ └── Paris.java
│ ├── empty_default_style
│ ├── input
│ │ ├── MyViewWithoutStyle.java
│ │ └── PackageInfo.java
│ └── output
│ │ ├── MyViewWithoutStyleStyleApplier.java
│ │ └── Paris.java
│ ├── error_attr_non_res_default_value
│ └── input
│ │ ├── MyView.java
│ │ └── PackageInfo.java
│ ├── error_attr_non_res_value
│ └── input
│ │ ├── MyView.java
│ │ └── PackageInfo.java
│ ├── error_attr_wrong_default_value_type
│ └── input
│ │ ├── MyView.java
│ │ └── PackageInfo.java
│ ├── error_attr_wrong_value_type
│ └── input
│ │ ├── MyView.java
│ │ └── PackageInfo.java
│ ├── error_no_default_style
│ └── input
│ │ ├── MyViewWithoutStyle.java
│ │ └── PackageInfo.java
│ ├── error_non_final_style_field
│ └── input
│ │ ├── MyView.java
│ │ └── PackageInfo.java
│ ├── error_non_static_style_field
│ └── input
│ │ ├── MyView.java
│ │ └── PackageInfo.java
│ ├── error_private_style_field
│ └── input
│ │ ├── MyView.java
│ │ └── PackageInfo.java
│ ├── error_style_field_invalid_type
│ └── input
│ │ ├── MyView.java
│ │ └── PackageInfo.java
│ ├── error_styleable_child_wrong_value_type
│ └── input
│ │ ├── MyView.java
│ │ └── PackageInfo.java
│ ├── error_styleable_outside_package_no_R
│ └── input
│ │ └── MyView.java
│ ├── error_styleable_outside_package_with_attr_and_namespaced_resources
│ └── input
│ │ ├── MyView.java
│ │ └── PackageInfo.java
│ ├── error_two_default_styles
│ └── input
│ │ ├── MyView.java
│ │ └── PackageInfo.java
│ ├── overridden_protected_function
│ └── input
│ │ ├── MyView.kt
│ │ ├── MyViewSuper.kt
│ │ └── PackageInfo.java
│ ├── style_extension_generation
│ ├── input
│ │ ├── MyView.kt
│ │ └── PackageInfo.java
│ └── output
│ │ └── MyViewStyleExtensions.kt
│ ├── style_extension_generation_java
│ ├── input
│ │ ├── MyView.java
│ │ └── PackageInfo.java
│ └── output
│ │ └── MyViewStyleExtensions.kt
│ ├── style_extension_generation_jvm_static
│ ├── input
│ │ ├── MyView.kt
│ │ └── PackageInfo.java
│ └── output
│ │ └── MyViewStyleExtensions.kt
│ ├── style_in_kotlin_companion_object
│ └── input
│ │ ├── MyView.kt
│ │ └── PackageInfo.java
│ ├── styleable_fields
│ ├── input
│ │ ├── MyView.java
│ │ └── PackageInfo.java
│ └── output
│ │ ├── MyViewStyleApplier.java
│ │ └── Paris.java
│ ├── styleable_in_other_module_single_attr
│ ├── input
│ │ ├── MyView.java
│ │ └── PackageInfo.java
│ └── output
│ │ ├── MyViewStyleApplier.java
│ │ └── Paris.java
│ ├── styleable_minimal
│ ├── input
│ │ ├── MyView.java
│ │ └── PackageInfo.java
│ └── output
│ │ ├── MyViewStyleApplier.java
│ │ └── Paris.java
│ ├── styleable_outside_package_single_attr
│ ├── input
│ │ ├── MyView.java
│ │ └── PackageInfo.java
│ └── output
│ │ ├── MyViewStyleApplier.java
│ │ └── Paris.java
│ └── styles
│ ├── input
│ ├── MyView.java
│ └── PackageInfo.java
│ └── output
│ ├── MyViewStyleApplier.java
│ └── Paris.java
├── paris
├── .gitignore
├── build.gradle
├── gradle.properties
└── src
│ ├── androidTest
│ ├── java
│ │ └── com
│ │ │ └── airbnb
│ │ │ └── paris
│ │ │ ├── MapTypedArrayWrapperTest.kt
│ │ │ ├── MultiTypedArrayWrapperTest.kt
│ │ │ ├── StyleApplierTest.kt
│ │ │ ├── StyleApplierUtilsTest.kt
│ │ │ ├── proxies
│ │ │ ├── BaseViewMappings.kt
│ │ │ ├── DeprecatedTextViewProxyTest.kt
│ │ │ ├── DeprecatedTextViewStyleApplierTest.kt
│ │ │ ├── DeprecatedViewStyleApplierTest.kt
│ │ │ ├── GenericViewProxyTests.kt
│ │ │ ├── ImageViewMappings.kt
│ │ │ ├── ImageViewStyleApplierTest.kt
│ │ │ ├── ImageViewStyleApplier_StyleBuilderTest.kt
│ │ │ ├── TextViewMappings.kt
│ │ │ ├── TextViewStyleApplier_StyleBuilderTest.kt
│ │ │ ├── ViewMappings.kt
│ │ │ └── ViewStyleApplier_StyleBuilderTest.kt
│ │ │ └── spannables
│ │ │ ├── SpannableBuilderTest.kt
│ │ │ └── StyleConverterTest.kt
│ └── res
│ │ ├── drawable
│ │ ├── format_drawable.xml
│ │ └── format_drawable_2.xml
│ │ └── values
│ │ ├── booleans.xml
│ │ ├── formats.xml
│ │ ├── styles.xml
│ │ └── text_view_proxy_style_applier_text.xml
│ ├── debug
│ └── res
│ │ ├── font
│ │ └── roboto_regular.ttf
│ │ └── values
│ │ ├── test_common.xml
│ │ ├── test_formats.xml
│ │ ├── test_image_view_style_extensions.xml
│ │ ├── test_text_view_style_applier.xml
│ │ ├── test_text_view_style_extensions.xml
│ │ ├── test_typed_array_typed_array_wrapper.xml
│ │ ├── test_view_group_style_extensions.xml
│ │ ├── test_view_style_builder.xml
│ │ ├── test_view_style_extensions.xml
│ │ ├── test_with_attr_function_view.xml
│ │ ├── test_with_attr_property_setter_view.xml
│ │ ├── test_with_internal_attr_function_view.xml
│ │ ├── test_with_internal_attr_property_setter_view.xml
│ │ ├── test_with_styleable_child_internal_view.xml
│ │ ├── test_with_styleable_child_kotlin_view.xml
│ │ ├── test_with_styleable_child_kotlin_view_style_extensions.xml
│ │ ├── test_with_styleable_child_lateinit_view_style_extensions.xml
│ │ ├── test_with_styleable_child_property_delegate_view.xml
│ │ ├── test_with_styleable_child_property_delegate_view_style_extensions.xml
│ │ ├── test_with_styleable_child_view.xml
│ │ └── test_with_styleable_child_view_style_extensions.xml
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── airbnb
│ │ │ └── paris
│ │ │ ├── ExtendableStyleBuilder.kt
│ │ │ ├── StyleApplier.kt
│ │ │ ├── StyleApplierUtils.kt
│ │ │ ├── StyleBuilder.kt
│ │ │ ├── attribute_values
│ │ │ ├── ColorValue.kt
│ │ │ ├── DpValue.kt
│ │ │ ├── ResourceId.kt
│ │ │ └── Styles.kt
│ │ │ ├── proxies
│ │ │ ├── BaseProxy.kt
│ │ │ ├── ImageViewProxy.kt
│ │ │ ├── Proxy.kt
│ │ │ ├── TextViewProxy.kt
│ │ │ ├── ViewGroupProxy.kt
│ │ │ └── ViewProxy.kt
│ │ │ ├── spannables
│ │ │ ├── SpannableBuilder.kt
│ │ │ └── StyleConverter.kt
│ │ │ ├── styles
│ │ │ ├── AttributeSetStyle.kt
│ │ │ ├── EmptyStyle.kt
│ │ │ ├── MultiStyle.kt
│ │ │ ├── ProgrammaticStyle.kt
│ │ │ ├── ResourceStyle.kt
│ │ │ └── Style.kt
│ │ │ ├── typed_array_wrappers
│ │ │ ├── EmptyTypedArrayWrapper.kt
│ │ │ ├── MapTypedArrayWrapper.kt
│ │ │ ├── MultiTypedArrayWrapper.kt
│ │ │ ├── TypedArrayTypedArrayWrapper.kt
│ │ │ └── TypedArrayWrapper.kt
│ │ │ └── utils
│ │ │ ├── ContextExtensions.kt
│ │ │ ├── IntExtensions.kt
│ │ │ ├── ResourcesExtensions.kt
│ │ │ ├── StyleBuilderFunction.java
│ │ │ └── ViewExtensions.kt
│ └── res
│ │ ├── anim
│ │ └── null_.xml
│ │ ├── color
│ │ └── null_.xml
│ │ ├── drawable
│ │ └── null_.xml
│ │ ├── font
│ │ └── null_.xml
│ │ └── values
│ │ ├── attrs.xml
│ │ ├── ids.xml
│ │ └── strings.xml
│ └── testDebug
│ ├── java
│ └── com
│ │ └── airbnb
│ │ └── paris
│ │ ├── StyleBuilderTest.kt
│ │ ├── proxies
│ │ ├── ImageViewStyleExtensionsTest.kt
│ │ ├── TextViewProxyTest.kt
│ │ ├── TextViewStyleApplierTest.kt
│ │ ├── TextViewStyleBuilderTest.kt
│ │ ├── TextViewStyleExtensionsTest.kt
│ │ ├── ViewGroupProxyTest.kt
│ │ ├── ViewGroupStyleApplierTest.kt
│ │ ├── ViewGroupStyleBuilderTest.kt
│ │ ├── ViewGroupStyleExtensionsTest.kt
│ │ ├── ViewProxyTest.kt
│ │ ├── ViewStyleApplierTest.kt
│ │ ├── ViewStyleBuilderTest.kt
│ │ └── ViewStyleExtensionsTest.kt
│ │ ├── styles
│ │ ├── MultiStyleTest.kt
│ │ ├── ProgrammaticStyleTest.kt
│ │ └── ResourceStyleTest.kt
│ │ ├── typed_array_wrappers
│ │ ├── EmptyTypedArrayWrapperSpec.kt
│ │ ├── MapTypedArrayWrapperTest.kt
│ │ ├── MultiTypedArrayWrapperTest.kt
│ │ └── TypedArrayTypedArrayWrapperTest.kt
│ │ ├── utils
│ │ ├── ShadowResourcesCompat.kt
│ │ └── TypefaceEquals.kt
│ │ └── views
│ │ ├── java
│ │ ├── WithStyleFieldView.java
│ │ ├── WithStyleFieldViewExtensionsTest.kt
│ │ ├── WithStyleableChildExtensionsTest.kt
│ │ └── WithStyleableChildView.java
│ │ └── kotlin
│ │ ├── WithAttrFunctionView.kt
│ │ ├── WithAttrFunctionViewStyleExtensionsTest.kt
│ │ ├── WithAttrPropertySetterView.kt
│ │ ├── WithAttrPropertySetterViewStyleExtensionsTest.kt
│ │ ├── WithInternalAttrFunctionView.kt
│ │ ├── WithInternalAttrFunctionViewStyleExtensionsTest.kt
│ │ ├── WithInternalAttrPropertySetterView.kt
│ │ ├── WithInternalAttrPropertySetterViewStyleExtensionsTest.kt
│ │ ├── WithJvmStaticStylePropertyView.kt
│ │ ├── WithJvmStaticStylePropertyViewExtensionsTest.kt
│ │ ├── WithStyleInternalPropertyView.kt
│ │ ├── WithStyleInternalPropertyViewExtensionsTest.kt
│ │ ├── WithStylePropertyView.kt
│ │ ├── WithStylePropertyViewExtensionsTest.kt
│ │ ├── WithStyleableChildInternalView.kt
│ │ ├── WithStyleableChildInternalViewStyleExtensionsTest.kt
│ │ ├── WithStyleableChildKotlinView.kt
│ │ ├── WithStyleableChildKotlinViewStyleExtensionsTest.kt
│ │ ├── WithStyleableChildLateinitView.kt
│ │ ├── WithStyleableChildLateinitViewStyleExtensionsTest.kt
│ │ ├── WithStyleableChildPropertyDelegateView.kt
│ │ └── WithStyleableChildPropertyDelegateViewStyleExtensionsTest.kt
│ └── resources
│ └── com
│ └── airbnb
│ └── paris
│ └── robolectric.properties
├── sample
├── .gitignore
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── airbnb
│ │ └── paris
│ │ └── sample
│ │ ├── MainActivity.kt
│ │ ├── SectionView.kt
│ │ ├── SpannableActivity.java
│ │ └── TextStyles.kt
│ └── res
│ ├── drawable-mdpi
│ └── eiffel_header.jpg
│ ├── drawable
│ └── ic_order_selected_gray.xml
│ ├── layout
│ ├── activity_main.xml
│ ├── activity_spannable.xml
│ ├── view_marquee.xml
│ └── view_section.xml
│ ├── mipmap-hdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-mdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ ├── mipmap-xxxhdpi
│ ├── ic_launcher.png
│ └── ic_launcher_round.png
│ └── values
│ ├── colors.xml
│ ├── dimens.xml
│ ├── strings.xml
│ ├── styles.xml
│ └── styles_view_section.xml
├── settings.gradle
└── update_processor_tests.rb
/.github/workflows/build_test.yml:
--------------------------------------------------------------------------------
1 | name: Build/Test
2 |
3 | on:
4 | # Trigger on every pull request
5 | pull_request:
6 | jobs:
7 | build-test:
8 | runs-on: macos-11
9 | steps:
10 | - name: Checkout Paris
11 | uses: actions/checkout@v2
12 | - uses: actions/setup-java@v2
13 | name: Setting up Java 11
14 | with:
15 | distribution: liberica
16 | java-version: '11'
17 | - name: Build / Unit tests / Lint
18 | run: "./gradlew check --stacktrace"
19 | - name: Run UI Tests
20 | uses: reactivecircus/android-emulator-runner@v2
21 | with:
22 | emulator-build: 7425822
23 | api-level: 21
24 | target: default
25 | arch: x86_64
26 | emulator-options: -no-skin -no-window
27 | script: ./gradlew connectedDebugAndroidTest --stacktrace
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Gradle files
2 | .gradle
3 | /build
4 |
5 | # Local configuration file (sdk path, etc)
6 | local.properties
7 |
8 | # IntelliJ
9 | *.iml
10 | .idea/*
11 | !.idea/codeStyles
12 |
13 | # Android Studio captures folder
14 | /captures
15 |
16 | # External native build folder generated in Android Studio 2.2 and later
17 | .externalNativeBuild
18 |
19 | # macOS
20 | .DS_Store
21 |
22 | # Vim
23 | *~
24 | .swp
25 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Thank you for considering contributing to Paris!
4 |
5 | ## Testing
6 |
7 | Paris has an extensive suite of tests. Most of them are located in the `paris` module (for Paris classes) or in `paris-test` (for code generation). Due to the nature of this project a good share of the tests are integration tests and have to be run on an emulator or device.
8 |
9 | This command runs all the tests across modules:
10 | ```
11 | ./gradlew test connectedAndroidTest
12 | ```
13 |
14 | Please make sure that all tests are passing before proposing changes, and add or update tests whenever possible.
15 |
16 | If you update the annotation processor tests you may find the `update_processor_test_resources.rb` script very useful for updating the existing tests with your changes.
17 |
18 | ## How to Submit Changes
19 |
20 | First make sure all tests are passing, then create a pull request on Github. Explain why you are proposing the change, what issues it addresses, why you chose particular solutions, etc. The more context and details the easier it will be to review.
21 |
22 | When possible/appropriate changes should include tests and be well documented.
23 |
24 | ## How to Report a Bug or Request an Enhancement
25 |
26 | Create an issue on Github and tag it with `bug` or `enhancement` as appropriate. Please provide as much detail as possible. In the case of bugs that should include an easy way to reproduce the issue.
27 |
--------------------------------------------------------------------------------
/RELEASING.md:
--------------------------------------------------------------------------------
1 | Releasing
2 | ========
3 |
4 | 1. Bump the VERSION_NAME property in `gradle.properties` based on Major.Minor.Patch naming scheme
5 | 2. Update `CHANGELOG.md` for the impending release.
6 | 3. Update the `README.md` with the new version.
7 | 4. `git commit -am "Prepare for release X.Y.Z"` (where X.Y.Z is the version you set in step 1)
8 | 5. `git push`
9 | 6. Create a new release on Github
10 | 1. Tag version `vX.Y.Z`
11 | 2. Release title `vX.Y.Z`
12 | 3. Paste the content from `CHANGELOG.md` as the description
13 | 7. `./gradlew clean uploadArchives --no-daemon --no-parallel`
14 | 8. Visit [Sonatype Nexus](https://oss.sonatype.org/) and promote the artifact.
15 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext {
3 | ANDROID_PLUGIN_VERSION = '7.4.0'
4 | BUTTERKNIFE_VERSION = '10.2.3'
5 | KOTLIN_VERSION = '1.8.10'
6 | // Run "./gradlew dependencyUpdates" to check for updates
7 | VERSIONS_VERSION = '0.42.0'
8 | KSP_VERSION = '1.8.10-1.0.9'
9 | }
10 |
11 | repositories {
12 | google()
13 | gradlePluginPortal()
14 | mavenCentral()
15 | maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
16 | }
17 |
18 | dependencies {
19 | classpath "com.android.tools.build:gradle:$ANDROID_PLUGIN_VERSION"
20 | classpath "com.jakewharton:butterknife-gradle-plugin:$BUTTERKNIFE_VERSION"
21 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$KOTLIN_VERSION"
22 | classpath "com.github.ben-manes:gradle-versions-plugin:$VERSIONS_VERSION"
23 | classpath 'com.vanniktech:gradle-maven-publish-plugin:0.14.2'
24 | // Dokka is needed on classpath for vanniktech publish plugin
25 | classpath "org.jetbrains.dokka:dokka-gradle-plugin:1.7.0"
26 | }
27 | }
28 |
29 | plugins {
30 | id "com.google.devtools.ksp" version "$KSP_VERSION"
31 | }
32 |
33 | apply plugin: "com.github.ben-manes.versions"
34 |
35 | allprojects {
36 | repositories {
37 | google()
38 | mavenCentral()
39 | }
40 |
41 | configurations.all {
42 | exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jre7'
43 | }
44 | }
45 |
46 | subprojects { project ->
47 | apply from: "$rootDir/blessedDeps.gradle"
48 | }
49 |
50 | task clean(type: Delete) {
51 | delete rootProject.buildDir
52 | }
53 |
54 | dependencyUpdates.resolutionStrategy {
55 | componentSelection { rules ->
56 | rules.all { ComponentSelection selection ->
57 | boolean rejected = ['alpha', 'beta', 'rc', 'cr', 'm', 'preview'].any { qualifier ->
58 | selection.candidate.version ==~ /(?i).*[.-]${qualifier}[.\d-]*/
59 | }
60 | if (rejected) {
61 | selection.reject('Release candidate')
62 | }
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | VERSION_NAME=2.0.2
2 | GROUP=com.airbnb.android
3 | POM_DESCRIPTION=Paris is a system for creating and applying styles to views in Android.
4 | POM_URL=https://github.com/airbnb/paris
5 | POM_SCM_URL=https://github.com/airbnb/paris
6 | POM_SCM_CONNECTION=scm:git@github.com:airbnb/paris.git
7 | POM_SCM_DEV_CONNECTION=scm:git@github.com:airbnb/paris.git
8 | POM_LICENSE_NAME=Apache License 2.0
9 | POM_LICENSE_URL=https://github.com/airbnb/paris/blob/master/LICENSE
10 | POM_LICENSE_DIST=repo
11 | POM_DEVELOPER_ID=airbnb
12 | POM_DEVELOPER_NAME=Airbnb
13 | POM_DEVELOPER_EMAIL=android@airbnb.com
14 | POM_INCEPTION_YEAR=2017
15 |
16 | android.useAndroidX=true
17 | org.gradle.parallel=true
18 | org.gradle.incremental=true
19 | org.gradle.configureondemand=false
20 | kotlin.incremental=true
21 | kapt.includeCompileClasspath=false
22 |
23 | # Dokka fails without a larger metaspace https://github.com/Kotlin/dokka/issues/1405
24 | org.gradle.jvmargs=-XX:MaxMetaspaceSize=1g
25 | org.gradle.daemon=true
26 | #org.gradle.debug=true
27 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/airbnb/paris/d8b5edbc56253bcdd0d0c57930d2e91113dd0f37/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Jul 27 13:22:56 CDT 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.1-all.zip
7 |
--------------------------------------------------------------------------------
/libs/rt.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/airbnb/paris/d8b5edbc56253bcdd0d0c57930d2e91113dd0f37/libs/rt.jar
--------------------------------------------------------------------------------
/libs/tools.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/airbnb/paris/d8b5edbc56253bcdd0d0c57930d2e91113dd0f37/libs/tools.jar
--------------------------------------------------------------------------------
/paris-annotations/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/paris-annotations/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'java'
2 | apply plugin: 'kotlin'
3 | apply plugin: "com.vanniktech.maven.publish"
4 |
5 | sourceCompatibility = rootProject.JAVA_SOURCE_VERSION
6 | targetCompatibility = rootProject.JAVA_TARGET_VERSION
7 |
8 | dependencies {
9 | }
10 |
--------------------------------------------------------------------------------
/paris-annotations/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=Paris annotations
2 | POM_ARTIFACT_ID=paris-annotations
3 | POM_PACKAGING=jar
--------------------------------------------------------------------------------
/paris-annotations/src/main/java/com/airbnb/paris/annotations/AfterStyle.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.annotations
2 |
3 | @Target(AnnotationTarget.FUNCTION)
4 | annotation class AfterStyle
5 |
--------------------------------------------------------------------------------
/paris-annotations/src/main/java/com/airbnb/paris/annotations/Attr.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.annotations
2 |
3 | @Target(AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_SETTER)
4 | annotation class Attr(
5 | val value: Int,
6 | val defaultValue: Int = -1
7 | )
8 |
--------------------------------------------------------------------------------
/paris-annotations/src/main/java/com/airbnb/paris/annotations/BeforeStyle.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.annotations
2 |
3 | @Target(AnnotationTarget.FUNCTION)
4 | annotation class BeforeStyle
5 |
--------------------------------------------------------------------------------
/paris-annotations/src/main/java/com/airbnb/paris/annotations/Fraction.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.annotations
2 |
3 | @Target(AnnotationTarget.VALUE_PARAMETER)
4 | annotation class Fraction(
5 | val base: Int = 1,
6 | val pbase: Int = 1
7 | )
8 |
--------------------------------------------------------------------------------
/paris-annotations/src/main/java/com/airbnb/paris/annotations/GeneratedStyleableClass.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.annotations
2 |
3 | import kotlin.reflect.KClass
4 |
5 | /**
6 | * DO NOT USE. This annotation is meant to be used by generated classes only
7 | */
8 | @Target(AnnotationTarget.CLASS)
9 | annotation class GeneratedStyleableClass(val value: KClass<*>)
10 |
--------------------------------------------------------------------------------
/paris-annotations/src/main/java/com/airbnb/paris/annotations/GeneratedStyleableModule.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.annotations
2 |
3 | /**
4 | * DO NOT USE. This annotation is meant to be used by generated classes only
5 | */
6 | @Target(AnnotationTarget.CLASS)
7 | annotation class GeneratedStyleableModule(val value: Array)
8 |
--------------------------------------------------------------------------------
/paris-annotations/src/main/java/com/airbnb/paris/annotations/LayoutDimension.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.annotations
2 |
3 | @Target(AnnotationTarget.VALUE_PARAMETER)
4 | annotation class LayoutDimension
--------------------------------------------------------------------------------
/paris-annotations/src/main/java/com/airbnb/paris/annotations/ParisConfig.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.annotations;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * Place this annotation on a single class or interface within your module to specify configuration options for that module.
10 | */
11 | @Retention(RetentionPolicy.CLASS)
12 | @Target(ElementType.TYPE)
13 | public @interface ParisConfig {
14 |
15 | String defaultStyleNameFormat() default "";
16 |
17 | Class> rClass() default Void.class;
18 |
19 | /**
20 | * This is an experimental gradle flag (android.namespacedRClass=true). Setting to true allows Paris to generate code compatible with R files that
21 | * only have resources from the module the resource was declared in.
22 | */
23 | boolean namespacedResourcesEnabled() default false;
24 |
25 | /**
26 | * By default no Paris class is generated if a module contains no @Styleable classes.
27 | * However, if this is set to true a Paris class will still be generated in that case, using only
28 | * the @Styleables that are discovered on the class path.
29 | */
30 | boolean aggregateStyleablesOnClassPath() default false;
31 | }
32 |
--------------------------------------------------------------------------------
/paris-annotations/src/main/java/com/airbnb/paris/annotations/Style.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.annotations
2 |
3 | import kotlin.annotation.AnnotationTarget.FIELD
4 | import kotlin.annotation.AnnotationTarget.FUNCTION
5 |
6 | @Target(FIELD, FUNCTION)
7 | annotation class Style(val isDefault: Boolean = false)
8 |
--------------------------------------------------------------------------------
/paris-annotations/src/main/java/com/airbnb/paris/annotations/Styleable.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.annotations
2 |
3 | /**
4 | * @param value The name of the styleable resource.
5 | * @param emptyDefaultStyle Set to true if the view does not have a default style.
6 | * Will only be used if [ParisConfig.namespacedResourcesEnabled] is true. Default: false.
7 | */
8 | @Target(AnnotationTarget.CLASS)
9 | annotation class Styleable(val value: String = "", val emptyDefaultStyle: Boolean = false)
10 |
--------------------------------------------------------------------------------
/paris-annotations/src/main/java/com/airbnb/paris/annotations/StyleableChild.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.annotations
2 |
3 | @Target(AnnotationTarget.FIELD, AnnotationTarget.PROPERTY)
4 | annotation class StyleableChild(
5 | val value: Int,
6 | val defaultValue: Int = -1
7 | )
8 |
--------------------------------------------------------------------------------
/paris-processor/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/paris-processor/build.gradle:
--------------------------------------------------------------------------------
1 | import org.gradle.internal.jvm.Jvm
2 |
3 | apply plugin: 'java'
4 | apply plugin: 'kotlin'
5 | apply plugin: 'kotlin-kapt'
6 | apply plugin: "com.vanniktech.maven.publish"
7 |
8 | sourceCompatibility = rootProject.JAVA_SOURCE_VERSION
9 | targetCompatibility = rootProject.JAVA_TARGET_VERSION
10 |
11 | dependencies {
12 | implementation project(':paris-annotations')
13 | implementation deps.ksp
14 | implementation deps.kspImpl
15 | implementation deps.xProcessing
16 | // Compiler needed to resolve resource references in annotations
17 | implementation "org.jetbrains.kotlin:kotlin-compiler-embeddable:$KOTLIN_VERSION"
18 |
19 | implementation deps.androidAnnotations
20 |
21 | compileOnly deps.incapRuntime
22 | kapt deps.incapProcessor
23 |
24 | compileOnly files(rootProject.file("libs/tools.jar"))
25 |
26 | testImplementation deps.xProcessingTesting
27 | testImplementation deps.junit
28 | testImplementation "io.strikt:strikt-core:0.34.1"
29 | testImplementation deps.kotlinTest
30 | }
31 |
32 | tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile) {
33 | kotlinOptions {
34 | jvmTarget = "1.8"
35 | freeCompilerArgs += "-Xopt-in=kotlin.RequiresOptIn"
36 | freeCompilerArgs += "-Xopt-in=kotlin.contracts.ExperimentalContracts"
37 | freeCompilerArgs += "-Xopt-in=androidx.room.compiler.processing.ExperimentalProcessingApi"
38 | freeCompilerArgs += "-Xopt-in=com.google.devtools.ksp.KspExperimental"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/paris-processor/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=Paris processor
2 | POM_ARTIFACT_ID=paris-processor
3 | POM_PACKAGING=jar
--------------------------------------------------------------------------------
/paris-processor/src/main/java/com/airbnb/paris/processor/Config.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.processor
2 |
3 | import com.airbnb.paris.processor.framework.className
4 |
5 | internal const val PARIS_PACKAGE_NAME = "com.airbnb.paris"
6 | internal const val PARIS_MODULES_PACKAGE_NAME = "com.airbnb.paris.modules"
7 | internal const val PARIS_KOTLIN_EXTENSIONS_PACKAGE_NAME = "com.airbnb.paris.extensions"
8 |
9 | internal const val PARIS_SIMPLE_CLASS_NAME = "Paris"
10 | internal const val STYLE_APPLIER_SIMPLE_CLASS_NAME_FORMAT = "%sStyleApplier"
11 | internal const val MODULE_SIMPLE_CLASS_NAME_FORMAT = "GeneratedModule_%s"
12 | internal const val EXTENSIONS_FILE_NAME_FORMAT = "%sStyleExtensions"
13 |
14 | internal val STYLE_CLASS_NAME = "$PARIS_PACKAGE_NAME.styles.Style".className()
15 | internal val STYLE_APPLIER_CLASS_NAME = "$PARIS_PACKAGE_NAME.StyleApplier".className()
16 | internal val STYLE_BUILDER_CLASS_NAME = "$PARIS_PACKAGE_NAME.StyleBuilder".className()
17 | internal val EXTENDABLE_STYLE_BUILDER_CLASS_NAME = "$PARIS_PACKAGE_NAME.ExtendableStyleBuilder".className()
18 | internal val STYLE_APPLIER_UTILS_CLASS_NAME = "$PARIS_PACKAGE_NAME.StyleApplierUtils".className()
19 | internal val TYPED_ARRAY_WRAPPER_CLASS_NAME = "$PARIS_PACKAGE_NAME.typed_array_wrappers.TypedArrayWrapper".className()
20 | internal val STYLE_BUILDER_FUNCTION_CLASS_NAME = "$PARIS_PACKAGE_NAME.utils.StyleBuilderFunction".className()
21 | internal val CONTEXT_EXTENSIONS_CLASS_NAME = "$PARIS_PACKAGE_NAME.utils.ContextExtensionsKt".className()
22 | internal val RESOURCES_EXTENSIONS_CLASS_NAME = "$PARIS_PACKAGE_NAME.utils.ResourcesExtensionsKt".className()
23 | internal val SPANNABLE_BUILDER_CLASS_NAME = "$PARIS_PACKAGE_NAME.spannables.SpannableBuilder".className()
24 | internal val PROXY_CLASS_NAME = "$PARIS_PACKAGE_NAME.proxies.Proxy".className()
25 |
--------------------------------------------------------------------------------
/paris-processor/src/main/java/com/airbnb/paris/processor/KspProcessorProvider.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.processor
2 |
3 | import com.google.devtools.ksp.processing.SymbolProcessor
4 | import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
5 | import com.google.devtools.ksp.processing.SymbolProcessorProvider
6 |
7 | class ParisProcessorProvider : SymbolProcessorProvider {
8 | override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
9 | return ParisProcessor(environment)
10 | }
11 | }
--------------------------------------------------------------------------------
/paris-processor/src/main/java/com/airbnb/paris/processor/StyleablesTree.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.processor
2 |
3 | import androidx.room.compiler.processing.XElement
4 | import androidx.room.compiler.processing.XTypeElement
5 | import com.airbnb.paris.processor.models.BaseStyleableInfo
6 | import com.squareup.javapoet.ClassName
7 |
8 | internal class StyleablesTree(
9 | val processor: ParisProcessor,
10 | private val styleablesInfo: List
11 | ) {
12 |
13 | // This is a map of the View class qualified name to the StyleApplier class details
14 | // eg. "android.view.View" -> "com.airbnb.paris.ViewStyleApplier".className()
15 | private val viewQualifiedNameToStyleApplierClassName = mutableMapOf()
16 |
17 | /**
18 | * Traverses the class hierarchy of the given View type to find and return the first
19 | * corresponding style applier
20 | */
21 | internal fun findStyleApplier(viewTypeElement: XTypeElement, errorContext: (() -> String)? = null): StyleApplierDetails {
22 | return findStyleApplierRecursive(viewTypeElement)
23 | ?: error("Could not find style applier for ${viewTypeElement.qualifiedName} ${viewTypeElement.type}. " +
24 | errorContext?.invoke()?.let { "$it. " }.orEmpty() +
25 | "Available types are ${styleablesInfo.map { it.viewElementType }}")
26 | }
27 |
28 | private fun findStyleApplierRecursive(viewTypeElement: XTypeElement): StyleApplierDetails? {
29 | return viewQualifiedNameToStyleApplierClassName.getOrPut(viewTypeElement) {
30 |
31 | val type = viewTypeElement.type
32 | // Check to see if the view type is handled by a styleable class
33 | val styleableInfo = styleablesInfo.find { type.isSameType(it.viewElementType) }
34 | if (styleableInfo != null) {
35 | StyleApplierDetails(
36 | annotatedElement = styleableInfo.annotatedElement,
37 | className = styleableInfo.styleApplierClassName
38 | )
39 | } else {
40 | val superType = viewTypeElement.superType?.typeElement ?: return@getOrPut null
41 | findStyleApplier(superType)
42 | }
43 | }
44 | }
45 | }
46 |
47 | data class StyleApplierDetails(
48 | val annotatedElement: XElement,
49 | val className: ClassName
50 | )
51 |
--------------------------------------------------------------------------------
/paris-processor/src/main/java/com/airbnb/paris/processor/Timer.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.processor
2 |
3 | import androidx.room.compiler.processing.XMessager
4 | import javax.tools.Diagnostic
5 | import kotlin.math.pow
6 | import kotlin.math.roundToInt
7 |
8 | class Timer(val name: String) {
9 | private val timingSteps = mutableListOf()
10 | private var startNanos: Long? = null
11 | private var lastTimingNanos: Long? = null
12 |
13 |
14 | fun start() {
15 | timingSteps.clear()
16 | startNanos = System.nanoTime()
17 | lastTimingNanos = startNanos
18 | }
19 |
20 | fun markStepCompleted(stepDescription: String) {
21 | val nowNanos = System.nanoTime()
22 | val lastNanos = lastTimingNanos ?: error("Timer was not started")
23 | lastTimingNanos = nowNanos
24 |
25 | timingSteps.add(TimingStep(nowNanos - lastNanos, stepDescription))
26 | }
27 |
28 | fun finishAndPrint(messager: XMessager) {
29 | val start = startNanos ?: error("Timer was not started")
30 | val message = buildString {
31 | appendLine("$name finished in ${formatNanos(System.nanoTime() - start)}")
32 | timingSteps.forEach { step ->
33 | appendLine(" - ${step.description} (${formatNanos(step.durationNanos)})")
34 | }
35 | }
36 |
37 | messager.printMessage(Diagnostic.Kind.NOTE, message)
38 | }
39 |
40 | private class TimingStep(val durationNanos: Long, val description: String)
41 |
42 | private fun formatNanos(nanos: Long): String {
43 | val diffMs = nanos.div(1_000_000.0).roundTo(3)
44 | return "$diffMs ms"
45 | }
46 |
47 | private fun Double.roundTo(numFractionDigits: Int): Double {
48 | val factor = 10.0.pow(numFractionDigits.toDouble())
49 | return (this * factor).roundToInt() / factor
50 | }
51 | }
--------------------------------------------------------------------------------
/paris-processor/src/main/java/com/airbnb/paris/processor/android_resource_scanner/AndroidResourceId.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.processor.android_resource_scanner
2 |
3 | import com.airbnb.paris.processor.framework.AndroidClassNames
4 | import com.airbnb.paris.processor.framework.JavaCodeBlock
5 | import com.airbnb.paris.processor.framework.KotlinCodeBlock
6 | import com.airbnb.paris.processor.framework.toKPoet
7 | import com.squareup.javapoet.ClassName
8 |
9 | /**
10 | * @param className Like com.example.R.styleable
11 | * @param resourceName Like title_view
12 | */
13 | class AndroidResourceId(val value: Int, val className: ClassName, val resourceName: String) {
14 |
15 | val rClassName: ClassName = className.topLevelClassName()
16 |
17 | val code: JavaCodeBlock = if (rClassName == AndroidClassNames.R) {
18 | JavaCodeBlock.of("\$L.\$N", className, resourceName)
19 | } else {
20 | JavaCodeBlock.of("\$T.\$N", className, resourceName)
21 | }
22 |
23 | val kotlinCode: KotlinCodeBlock = if (rClassName == AndroidClassNames.R) {
24 | KotlinCodeBlock.of("%L.%N", className.toKPoet(), resourceName)
25 | } else {
26 | KotlinCodeBlock.of("%T.%N", className.toKPoet(), resourceName)
27 | }
28 |
29 | override fun equals(other: Any?): Boolean {
30 | return other is AndroidResourceId && value == other.value
31 | }
32 |
33 | override fun hashCode(): Int {
34 | return value
35 | }
36 |
37 | override fun toString(): String {
38 | throw UnsupportedOperationException("Please use value or code explicitly")
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/paris-processor/src/main/java/com/airbnb/paris/processor/android_resource_scanner/ResourceScanner.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.processor.android_resource_scanner
2 |
3 | import androidx.room.compiler.processing.XElement
4 | import kotlin.reflect.KClass
5 |
6 | interface ResourceScanner {
7 | /**
8 | * Returns the [AndroidResourceId] that is used as an annotation value of the given [XElement]
9 | */
10 | fun getId(
11 | annotation: KClass,
12 | element: XElement,
13 | value: Int
14 | ): AndroidResourceId?
15 | }
--------------------------------------------------------------------------------
/paris-processor/src/main/java/com/airbnb/paris/processor/framework/AndroidClassNames.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.processor.framework
2 |
3 | internal object AndroidClassNames {
4 |
5 | val ATTRIBUTE_SET = "android.util.AttributeSet".className()
6 | val BUILD = "android.os.Build".className()
7 | val R = "android.R".className()
8 | val CONTEXT = "android.content.Context".className()
9 | val RESOURCES = "android.content.res.Resources".className()
10 | val VIEW = "android.view.View".className()
11 | val ANY_RES = "androidx.annotation.AnyRes".className()
12 | val ARRAY_RES = "androidx.annotation.ArrayRes".className()
13 | val BOOL_RES = "androidx.annotation.BoolRes".className()
14 | val COLOR_INT = "androidx.annotation.ColorInt".className()
15 | val COLOR_RES = "androidx.annotation.ColorRes".className()
16 | val DIMEN_RES = "androidx.annotation.DimenRes".className()
17 | val DIMENSION = "androidx.annotation.Dimension".className()
18 | val DRAWABLE_RES = "androidx.annotation.DrawableRes".className()
19 | val FONT_RES = "androidx.annotation.FontRes".className()
20 | val FRACTION_RES = "androidx.annotation.FractionRes".className()
21 | val INTEGER_RES = "androidx.annotation.IntegerRes".className()
22 | val NULLABLE = "androidx.annotation.Nullable".className()
23 | val PX = "androidx.annotation.Px".className()
24 | val STRING_RES = "androidx.annotation.StringRes".className()
25 | val STYLE_RES = "androidx.annotation.StyleRes".className()
26 | val UI_THREAD = "androidx.annotation.UiThread".className()
27 | }
28 |
--------------------------------------------------------------------------------
/paris-processor/src/main/java/com/airbnb/paris/processor/framework/JavaSkyMemoizer.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.processor.framework
2 |
3 | import androidx.room.compiler.processing.XType
4 | import com.airbnb.paris.processor.BaseProcessor
5 |
6 | open class JavaSkyMemoizer(val processor: BaseProcessor) {
7 |
8 | val androidViewClassTypeX: XType by lazy {
9 | processor.environment.requireType(AndroidClassNames.VIEW)
10 | }
11 | }
--------------------------------------------------------------------------------
/paris-processor/src/main/java/com/airbnb/paris/processor/framework/Log.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.processor.framework
2 |
3 | import androidx.room.compiler.processing.XElement
4 |
5 |
6 | class Message(val severity: Severity, val message: String, val element: XElement?) {
7 |
8 | enum class Severity {
9 | Note, Warning, Error
10 | }
11 | }
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/paris-processor/src/main/java/com/airbnb/paris/processor/framework/Memoizer.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.processor.framework
2 |
3 | import androidx.room.compiler.processing.XRawType
4 | import androidx.room.compiler.processing.XType
5 | import androidx.room.compiler.processing.XTypeElement
6 | import com.airbnb.paris.processor.PROXY_CLASS_NAME
7 | import com.airbnb.paris.processor.ParisProcessor
8 | import com.airbnb.paris.processor.STYLE_CLASS_NAME
9 |
10 | class Memoizer(processor: ParisProcessor) : JavaSkyMemoizer(processor) {
11 |
12 | val proxyClassType: XType by lazy { processor.environment.requireType(PROXY_CLASS_NAME) }
13 |
14 | val styleClassTypeX: XType by lazy { processor.environment.requireType(STYLE_CLASS_NAME) }
15 |
16 | val rStyleTypeElementX: XTypeElement? by lazy {
17 | val rElement = processor.RElement ?: error("R Class not found")
18 | processor.environment.findType("${rElement.qualifiedName}.style")?.typeElement
19 | }
20 | }
--------------------------------------------------------------------------------
/paris-processor/src/main/java/com/airbnb/paris/processor/framework/SkyJavaClass.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.processor.framework
2 |
3 | import androidx.room.compiler.processing.XElement
4 | import androidx.room.compiler.processing.XFiler
5 | import androidx.room.compiler.processing.addOriginatingElement
6 | import com.airbnb.paris.processor.BaseProcessor
7 | import com.squareup.javapoet.JavaFile
8 | import com.squareup.javapoet.TypeSpec
9 |
10 | internal abstract class SkyJavaClass(val processor: BaseProcessor) {
11 |
12 | protected abstract val packageName: String
13 | protected abstract val name: String
14 | protected abstract val block: TypeSpec.Builder.() -> Unit
15 | protected abstract val originatingElements: List
16 |
17 | fun build(): TypeSpec {
18 | val builder = TypeSpec.classBuilder(name)
19 | originatingElements.forEach {
20 | builder.addOriginatingElement(it)
21 | }
22 | builder.block()
23 | return builder.build()
24 | }
25 |
26 | fun write(mode: XFiler.Mode = XFiler.Mode.Aggregating) {
27 | val javaFile = JavaFile.builder(packageName, build()).build()
28 | processor.filer.write(javaFile, mode)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/paris-processor/src/main/java/com/airbnb/paris/processor/framework/SkyKotlinFile.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.processor.framework
2 |
3 | import androidx.room.compiler.processing.XFiler
4 | import com.airbnb.paris.processor.BaseProcessor
5 | import com.squareup.kotlinpoet.FileSpec
6 |
7 |
8 | internal abstract class SkyKotlinFile( val processor: BaseProcessor) {
9 |
10 | protected abstract val packageName: String
11 | protected abstract val name: String
12 | protected abstract val block: FileSpec.Builder.() -> Unit
13 |
14 | fun build(): FileSpec {
15 | return FileSpec.builder(packageName, name).run {
16 | block()
17 | build()
18 | }
19 | }
20 |
21 | /**
22 | * If this module is being processed with kapt then the file is written, otherwise this is a no-op.
23 | */
24 | fun write(mode: XFiler.Mode = XFiler.Mode.Aggregating) {
25 | processor.filer.write(build(), mode)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/paris-processor/src/main/java/com/airbnb/paris/processor/framework/models/SkyMethodModel.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.processor.framework.models
2 |
3 | import androidx.room.compiler.processing.XElement
4 | import androidx.room.compiler.processing.XMethodElement
5 | import androidx.room.compiler.processing.XTypeElement
6 | import androidx.room.compiler.processing.isMethod
7 | import com.airbnb.paris.processor.BaseProcessor
8 |
9 | abstract class SkyMethodModel private constructor(
10 | val enclosingElement: XTypeElement,
11 | val element: XMethodElement,
12 | ) : SkyModel {
13 | val jvmName: String get() = element.jvmName
14 |
15 | protected constructor(element: XMethodElement) : this(
16 | element.enclosingElement as XTypeElement,
17 | element
18 | )
19 | }
20 |
21 | typealias SkyStaticMethodModel = SkyMethodModel
22 |
23 | abstract class SkyMethodModelFactory(
24 | processor: BaseProcessor,
25 | annotationClass: Class
26 | ) : JavaSkyModelFactory(processor, annotationClass) {
27 |
28 | override fun filter(element: XElement): Boolean = element.isMethod()
29 | }
30 |
31 | typealias SkyStaticMethodModelFactory = SkyMethodModelFactory
32 |
--------------------------------------------------------------------------------
/paris-processor/src/main/java/com/airbnb/paris/processor/framework/models/SkyModel.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.processor.framework.models
2 |
3 | import androidx.room.compiler.processing.XElement
4 | import androidx.room.compiler.processing.XRoundEnv
5 | import com.airbnb.paris.processor.BaseProcessor
6 |
7 | interface SkyModel
8 |
9 | abstract class JavaSkyModelFactory(
10 | val processor: BaseProcessor,
11 | private val annotationClass: Class
12 | ) {
13 |
14 | var models = emptyList()
15 | private set
16 |
17 | var latest = emptyList()
18 | private set
19 |
20 | fun process(roundEnv: XRoundEnv) {
21 | roundEnv.getElementsAnnotatedWith(annotationClass.canonicalName)
22 | .filter(::filter)
23 | .mapNotNull {
24 | @Suppress("UNCHECKED_CAST")
25 | elementToModel(it as E)
26 | }
27 | .let {
28 | models += it
29 | latest = it
30 | }
31 | }
32 |
33 | open fun filter(element: XElement): Boolean = true
34 |
35 | abstract fun elementToModel(element: E): T?
36 | }
37 |
--------------------------------------------------------------------------------
/paris-processor/src/main/java/com/airbnb/paris/processor/models/AfterStyleInfo.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.processor.models
2 |
3 | import androidx.room.compiler.processing.XMethodElement
4 | import com.airbnb.paris.annotations.AfterStyle
5 | import com.airbnb.paris.processor.ParisProcessor
6 | import com.airbnb.paris.processor.STYLE_CLASS_NAME
7 | import com.airbnb.paris.processor.framework.models.SkyMethodModel
8 | import com.airbnb.paris.processor.framework.models.SkyMethodModelFactory
9 | import com.airbnb.paris.processor.utils.isSameTypeName
10 |
11 | internal class AfterStyleInfoExtractor(val parisProcessor: ParisProcessor) : SkyMethodModelFactory(parisProcessor, AfterStyle::class.java) {
12 |
13 | override fun elementToModel(element: XMethodElement): AfterStyleInfo? {
14 |
15 | if (element.isPrivate() || element.isProtected()) {
16 | parisProcessor.logError(element) {
17 | "Methods annotated with @AfterStyle can't be private or protected."
18 | }
19 | return null
20 | }
21 |
22 | val parameterType = element.parameters.firstOrNull()?.type
23 |
24 | if (parameterType == null || !parameterType.isSameTypeName(STYLE_CLASS_NAME)) {
25 | parisProcessor.logError(element) {
26 | "Methods annotated with @AfterStyle must have a single Style parameter."
27 | }
28 | return null
29 | }
30 |
31 | return AfterStyleInfo(element)
32 | }
33 | }
34 |
35 | internal class AfterStyleInfo(element: XMethodElement) : SkyMethodModel(element)
36 |
--------------------------------------------------------------------------------
/paris-processor/src/main/java/com/airbnb/paris/processor/models/BeforeStyleInfo.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.processor.models
2 |
3 | import androidx.room.compiler.processing.XMethodElement
4 | import com.airbnb.paris.annotations.BeforeStyle
5 | import com.airbnb.paris.processor.ParisProcessor
6 | import com.airbnb.paris.processor.framework.models.SkyMethodModel
7 | import com.airbnb.paris.processor.framework.models.SkyMethodModelFactory
8 |
9 | internal class BeforeStyleInfoExtractor(val parisProcessor: ParisProcessor) : SkyMethodModelFactory(parisProcessor, BeforeStyle::class.java) {
10 |
11 | override fun elementToModel(element: XMethodElement): BeforeStyleInfo? {
12 | if (element.isPrivate() || element.isProtected()) {
13 | parisProcessor.logError(element) {
14 | "Methods annotated with @BeforeStyle can't be private or protected."
15 | }
16 | return null
17 | }
18 |
19 |
20 | val parameterType = element.parameters.firstOrNull()?.type
21 | // TODO: 2/21/21 BeforeStyle doesn't seem tested in the project?!
22 | if (parameterType == null || parisProcessor.memoizer.styleClassTypeX.isAssignableFrom(parameterType)) {
23 | parisProcessor.logError(element) {
24 | "Methods annotated with @BeforeStyle must have a single Style parameter."
25 | }
26 | return null
27 | }
28 |
29 | return BeforeStyleInfo(element)
30 | }
31 | }
32 |
33 | internal class BeforeStyleInfo(element: XMethodElement) : SkyMethodModel(element)
34 |
35 |
--------------------------------------------------------------------------------
/paris-processor/src/main/java/com/airbnb/paris/processor/models/StyleStaticMethodInfo.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.processor.models
2 |
3 | import androidx.room.compiler.processing.XMethodElement
4 | import com.airbnb.paris.annotations.Style
5 | import com.airbnb.paris.processor.ParisProcessor
6 | import com.airbnb.paris.processor.framework.JavaCodeBlock
7 | import com.airbnb.paris.processor.framework.KotlinCodeBlock
8 | import com.airbnb.paris.processor.framework.models.SkyStaticMethodModel
9 | import com.airbnb.paris.processor.framework.models.SkyStaticMethodModelFactory
10 | import com.airbnb.paris.processor.framework.toKPoet
11 | import com.airbnb.paris.processor.utils.ParisProcessorUtils
12 |
13 | internal class StyleStaticMethodInfoExtractor(val parisProcessor: ParisProcessor) :
14 | SkyStaticMethodModelFactory(parisProcessor, Style::class.java) {
15 |
16 | override fun elementToModel(element: XMethodElement): StyleStaticMethodInfo? {
17 | // TODO Get Javadoc from field/method and add it to the generated methods
18 |
19 | if (!element.isStatic() || element.isPrivate() || element.isProtected()) {
20 | parisProcessor.logError(element) {
21 | "Methods annotated with @Style must be static and can't be private or protected."
22 | }
23 | return null
24 | }
25 |
26 | val style = element.getAnnotation(Style::class)
27 | val isDefault = style!!.value.isDefault
28 |
29 | val enclosingElement = element.enclosingElement
30 |
31 | val elementName = element.name
32 |
33 | val formattedName = ParisProcessorUtils.reformatStyleFieldOrMethodName(elementName)
34 |
35 | val targetType = element.parameters[0].type.typeName
36 |
37 | val javadoc = JavaCodeBlock.of("@see \$T#\$N(\$T)\n", enclosingElement.className, elementName, targetType)
38 | val kdoc = KotlinCodeBlock.of("@see %T.%N\n", enclosingElement.className.toKPoet(), elementName)
39 |
40 | return StyleStaticMethodInfo(
41 | element,
42 | elementName,
43 | formattedName,
44 | javadoc,
45 | kdoc,
46 | isDefault
47 | )
48 | }
49 | }
50 |
51 | internal class StyleStaticMethodInfo(
52 | element: XMethodElement,
53 | override val elementName: String,
54 | override val formattedName: String,
55 | override val javadoc: JavaCodeBlock,
56 | override val kdoc: KotlinCodeBlock,
57 | override val isDefault: Boolean = false
58 | ) : SkyStaticMethodModel(element), StyleInfo
59 |
--------------------------------------------------------------------------------
/paris-processor/src/main/java/com/airbnb/paris/processor/utils/ParisProcessorUtils.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.processor.utils
2 |
3 | // This is purposefully left public to facilitate integrations with Paris
4 | // TODO BREAKING Refactor this to be an object with @JvmStatic functions
5 | class ParisProcessorUtils {
6 |
7 | companion object {
8 | /**
9 | * Format the name of a @Style annotated field or method to match what the style applier and
10 | * builder will use.
11 | *
12 | * "Style" suffixes are removed to make it possible to use in field or method names while
13 | * avoiding the redundancy of having it be part of style builder methods.
14 | *
15 | * Examples:
16 | * MY_RED -> MyRed
17 | * myRed -> MyRed
18 | * MY_RED_STYLE -> MyRed
19 | * myRedStyle -> MyRed
20 | */
21 | @JvmStatic
22 | fun reformatStyleFieldOrMethodName(name: String): String {
23 | // Converts any name to CamelCase
24 | val isNameAllCaps = name.all { it.isUpperCase() || !it.isLetter() }
25 | return name
26 | .foldRightIndexed("") { index, c, acc ->
27 | if (c == '_') {
28 | acc
29 | } else {
30 | if (index == 0) {
31 | c.uppercaseChar() + acc
32 | } else if (name[index - 1] != '_') {
33 | if (isNameAllCaps) {
34 | c.lowercaseChar() + acc
35 | } else {
36 | c + acc
37 | }
38 | } else {
39 | c.uppercaseChar() + acc
40 | }
41 | }
42 | }
43 | .let {
44 | if (it != "Style") {
45 | it.removeSuffix("Style")
46 | } else {
47 | it
48 | }
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/paris-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider:
--------------------------------------------------------------------------------
1 | com.airbnb.paris.processor.ParisProcessorProvider
--------------------------------------------------------------------------------
/paris-processor/src/main/resources/META-INF/services/javax.annotation.processing.Processor:
--------------------------------------------------------------------------------
1 | com.airbnb.paris.processor.ParisProcessor
2 |
--------------------------------------------------------------------------------
/paris-processor/src/test/java/KspResourceScannerTest.kt:
--------------------------------------------------------------------------------
1 | import com.airbnb.paris.processor.android_resource_scanner.KspResourceScanner
2 | import org.junit.Test
3 | import strikt.api.expectThat
4 | import strikt.assertions.isEqualTo
5 |
6 | class KspResourceScannerTest {
7 | @Test
8 | fun findMatchingImportPackage_TypeAlias() {
9 | val import = KspResourceScanner.findMatchingImportPackage(
10 | importedNames = listOf("com.airbnb.paris.test.R2 as typeAliasedR"),
11 | annotationReference = "typeAliasedR.layout.my_layout",
12 | annotationReferencePrefix = "typeAliasedR",
13 | packageName = "com.airbnb.paris"
14 | )
15 |
16 | expectThat(import.fullyQualifiedReference).isEqualTo("com.airbnb.paris.test.R2.layout.my_layout")
17 | }
18 |
19 | @Test
20 | fun findMatchingImportPackage_TypeAliasDoesNotMatch() {
21 | val import = KspResourceScanner.findMatchingImportPackage(
22 | importedNames = listOf("com.airbnb.paris.test.R2 as typeAliasedR2"),
23 | annotationReference = "typeAliasedR.layout.my_layout",
24 | annotationReferencePrefix = "typeAliasedR",
25 | packageName = "com.airbnb.paris"
26 | )
27 |
28 | // falls back to annotation reference, since import should not match
29 | expectThat(import.fullyQualifiedReference).isEqualTo("typeAliasedR.layout.my_layout")
30 | }
31 |
32 | @Test
33 | fun findMatchingImportPackage_fullyStaticImport() {
34 | val import = KspResourceScanner.findMatchingImportPackage(
35 | importedNames = listOf("com.airbnb.n2.comp.designsystem.hostdls.R2.styleable.n2_CarouselCheckedActionCard_n2_layoutStyle"),
36 | annotationReference = "n2_CarouselCheckedActionCard_n2_layoutStyle",
37 | annotationReferencePrefix = "n2_CarouselCheckedActionCard_n2_layoutStyle",
38 | packageName = "com.airbnb.n2.comp.designsystem.hostdls"
39 | )
40 |
41 | // falls back to annotation reference, since import should not match
42 | expectThat(import.fullyQualifiedReference).isEqualTo("com.airbnb.n2.comp.designsystem.hostdls.R2.styleable.n2_CarouselCheckedActionCard_n2_layoutStyle")
43 | }
44 |
45 | }
--------------------------------------------------------------------------------
/paris-processor/src/test/kotlin/com/airbnb/paris/processor/utils/ParisProcessorUtilsTest.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.processor.utils
2 |
3 | import com.airbnb.paris.processor.utils.ParisProcessorUtils.Companion.reformatStyleFieldOrMethodName
4 | import io.kotlintest.matchers.shouldBe
5 | import io.kotlintest.specs.StringSpec
6 |
7 | class ParisProcessorUtilsTest : StringSpec({
8 |
9 | "upper snake case style names should be converted to upper camel" {
10 | reformatStyleFieldOrMethodName("MY_RED") shouldBe "MyRed"
11 | }
12 |
13 | "lower camel case style names should be converted to upper camel" {
14 | reformatStyleFieldOrMethodName("myRed") shouldBe "MyRed"
15 | }
16 |
17 | "upper camel case style names shouldn't change" {
18 | reformatStyleFieldOrMethodName("MyRed") shouldBe "MyRed"
19 | }
20 |
21 | "Style suffix should be removed from style names" {
22 | reformatStyleFieldOrMethodName("MY_RED_STYLE") shouldBe "MyRed"
23 | reformatStyleFieldOrMethodName("myRedStyle") shouldBe "MyRed"
24 | reformatStyleFieldOrMethodName("MyRedStyle") shouldBe "MyRed"
25 | }
26 |
27 | "style whose whole name is style/Style/STYLE doesn't change" {
28 | reformatStyleFieldOrMethodName("style") shouldBe "Style"
29 | reformatStyleFieldOrMethodName("Style") shouldBe "Style"
30 | reformatStyleFieldOrMethodName("STYLE") shouldBe "Style"
31 | }
32 | })
33 |
--------------------------------------------------------------------------------
/paris-test-lib/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/paris-test-lib/README.md:
--------------------------------------------------------------------------------
1 | This module is used to test accessing resources from another module. In particular, with namespaced resources turned on Paris must generate
2 | references to R files that contain the resource definition.
--------------------------------------------------------------------------------
/paris-test-lib/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-kapt'
4 | apply plugin: 'com.jakewharton.butterknife'
5 |
6 | android {
7 | compileSdkVersion rootProject.COMPILE_SDK_VERSION
8 |
9 | defaultConfig {
10 | minSdkVersion rootProject.MIN_SDK_VERSION
11 | targetSdkVersion rootProject.TARGET_SDK_VERSION
12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
13 | }
14 |
15 | compileOptions {
16 | sourceCompatibility rootProject.JAVA_SOURCE_VERSION
17 | targetCompatibility rootProject.JAVA_TARGET_VERSION
18 | }
19 |
20 | kotlinOptions {
21 | jvmTarget = '1.8'
22 | }
23 | }
24 |
25 | dependencies {
26 | implementation project(':paris')
27 |
28 | implementation deps.appcompat
29 |
30 | kapt project(':paris-processor')
31 | }
32 |
--------------------------------------------------------------------------------
/paris-test-lib/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/paris-test-lib/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/paris-test/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/paris-test/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'kotlin-android'
3 | apply plugin: 'kotlin-kapt'
4 |
5 | android {
6 | compileSdkVersion rootProject.COMPILE_SDK_VERSION
7 |
8 | defaultConfig {
9 | minSdkVersion rootProject.MIN_SDK_VERSION
10 | targetSdkVersion rootProject.TARGET_SDK_VERSION
11 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
12 | }
13 |
14 | testOptions {
15 | unitTests {
16 | includeAndroidResources = true
17 | }
18 | }
19 |
20 | compileOptions {
21 | sourceCompatibility rootProject.JAVA_SOURCE_VERSION
22 | targetCompatibility rootProject.JAVA_TARGET_VERSION
23 | }
24 |
25 | kotlinOptions {
26 | jvmTarget = '1.8'
27 | }
28 | }
29 |
30 | sourceCompatibility = rootProject.JAVA_SOURCE_VERSION
31 | targetCompatibility = rootProject.JAVA_TARGET_VERSION
32 |
33 | dependencies {
34 | implementation project(':paris')
35 | implementation project(':paris-test-lib')
36 | implementation "com.google.devtools.ksp:symbol-processing-api:$KSP_VERSION"
37 |
38 | implementation deps.appcompat
39 |
40 | kapt project(':paris-processor')
41 |
42 | testImplementation project(':paris-processor')
43 |
44 | testImplementation files(rootProject.file("libs/rt.jar"))
45 | testImplementation files(rootProject.file("libs/tools.jar"))
46 |
47 | testImplementation deps.junit
48 | testImplementation deps.testingCompile
49 | testImplementation deps.kotlinCompileTesting
50 | testImplementation "io.github.java-diff-utils:java-diff-utils:4.5"
51 | testImplementation "io.strikt:strikt-core:0.31.0"
52 |
53 | androidTestImplementation deps.espresso
54 | }
55 |
56 | // Java files in the "resources" folder are not included in the build for some reason (seems like source files are skipped?)
57 | // This files are needed to test the annotation processor, so we manually copy them into the build.
58 | task('copyDebugTestResources', type: Copy) {
59 | from("${projectDir}/src/test/resources")
60 | into("${buildDir}/intermediates/sourceFolderJavaResources/debug")
61 | }
62 |
63 | task('copyReleaseTestResources', type: Copy) {
64 | from("${projectDir}/src/test/resources")
65 | into("${buildDir}/intermediates/sourceFolderJavaResources/release")
66 | }
67 |
68 | preBuild.dependsOn copyReleaseTestResources
69 | preBuild.dependsOn copyDebugTestResources
70 |
--------------------------------------------------------------------------------
/paris-test/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/paris-test/src/main/java/com/airbnb/paris/test/MyOtherView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.widget.TextView;
7 |
8 | import com.airbnb.paris.annotations.Attr;
9 | import com.airbnb.paris.annotations.Styleable;
10 | import com.airbnb.paris.annotations.StyleableChild;
11 |
12 | @Styleable("MyView")
13 | public class MyOtherView extends View {
14 |
15 | @StyleableChild(R2.styleable.MyView_titleStyle)
16 | public TextView title;
17 |
18 | public MyOtherView(Context context) {
19 | super(context);
20 | init();
21 | }
22 |
23 | public MyOtherView(Context context, AttributeSet attrs) {
24 | super(context, attrs);
25 | init();
26 | }
27 |
28 | public MyOtherView(Context context, AttributeSet attrs, int defStyle) {
29 | super(context, attrs, defStyle);
30 | init();
31 | }
32 |
33 | private void init() {
34 | title = new TextView(getContext());
35 | }
36 |
37 | @Attr(value = R2.styleable.MyView_active, defaultValue = R2.bool.active)
38 | public void setActive(boolean active) {
39 | // Nothing to do
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/paris-test/src/main/java/com/airbnb/paris/test/PackageConfig.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test
2 |
3 | import com.airbnb.paris.annotations.ParisConfig
4 |
5 | @ParisConfig(rClass = R::class)
6 | class PackageConfig
--------------------------------------------------------------------------------
/paris-test/src/main/res/color/format_color_state_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/paris-test/src/main/res/drawable/format_drawable.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/paris-test/src/main/res/font/format_font.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/airbnb/paris/d8b5edbc56253bcdd0d0c57930d2e91113dd0f37/paris-test/src/main/res/font/format_font.ttf
--------------------------------------------------------------------------------
/paris-test/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/paris-test/src/main/res/values/booleans.xml:
--------------------------------------------------------------------------------
1 |
2 | true
3 |
4 |
--------------------------------------------------------------------------------
/paris-test/src/main/res/values/formats.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 | true
31 | #FF0000
32 | 17dp
33 | 0
34 | 0x01
35 | - 1.2
36 | 5%
37 | 42
38 | Hello
39 | Hello
40 |
41 | - Tour Eiffel
42 | - Le Louvre
43 | - Arc de Triomphe
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/paris-test/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Paris Test
3 |
4 |
--------------------------------------------------------------------------------
/paris-test/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
15 |
16 |
20 |
21 |
25 |
26 |
29 |
30 |
33 |
34 |
37 |
38 |
41 |
42 |
45 |
46 |
49 |
50 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
--------------------------------------------------------------------------------
/paris-test/src/test/java/com/airbnb/paris/test/CompilationMode.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test
2 |
3 | enum class CompilationMode(val testKapt: Boolean, val testKSP: Boolean) {
4 | KSP(testKapt = false, testKSP = true),
5 | KAPT(testKapt = true, testKSP = false),
6 | ALL(testKapt = true, testKSP = true)
7 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/at_style_style_field/input/MyView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.widget.TextView;
7 |
8 | import com.airbnb.paris.annotations.Attr;
9 | import com.airbnb.paris.annotations.Style;
10 | import com.airbnb.paris.annotations.Styleable;
11 | import com.airbnb.paris.test.MyViewStyleApplier;
12 |
13 | @Styleable("MyView")
14 | public class MyView extends View {
15 |
16 | @Style
17 | static final com.airbnb.paris.styles.Style myStyle = new MyViewStyleApplier.StyleBuilder().build();
18 |
19 | public MyView(Context context) {
20 | super(context);
21 | }
22 |
23 | public MyView(Context context, AttributeSet attrs) {
24 | super(context, attrs);
25 | }
26 |
27 | public MyView(Context context, AttributeSet attrs, int defStyle) {
28 | super(context, attrs, defStyle);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/at_style_style_field/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/attr_requires_api/input/MyView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import android.content.Context;
4 | import android.content.res.ColorStateList;
5 | import android.graphics.Typeface;
6 | import android.graphics.drawable.Drawable;
7 | import android.os.Build;
8 | import androidx.annotation.AnyRes;
9 | import androidx.annotation.BoolRes;
10 | import androidx.annotation.ColorInt;
11 | import androidx.annotation.Px;
12 | import androidx.annotation.RequiresApi;
13 | import android.util.AttributeSet;
14 | import android.view.View;
15 |
16 | import com.airbnb.paris.annotations.Attr;
17 | import com.airbnb.paris.annotations.Fraction;
18 | import com.airbnb.paris.annotations.LayoutDimension;
19 | import com.airbnb.paris.annotations.Styleable;
20 |
21 | @Styleable("Formats")
22 | public class MyView extends View {
23 |
24 | public MyView(Context context) {
25 | super(context);
26 | }
27 |
28 | public MyView(Context context, AttributeSet attrs) {
29 | super(context, attrs);
30 | }
31 |
32 | public MyView(Context context, AttributeSet attrs, int defStyle) {
33 | super(context, attrs, defStyle);
34 | }
35 |
36 | @Attr(R2.styleable.Formats_formatBoolean)
37 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
38 | public void formatBoolean(boolean value) {}
39 | }
40 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/attr_requires_api/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/attr_requires_api_default_value/input/MyView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import android.content.Context;
4 | import android.content.res.ColorStateList;
5 | import android.graphics.Typeface;
6 | import android.graphics.drawable.Drawable;
7 | import android.os.Build;
8 | import androidx.annotation.AnyRes;
9 | import androidx.annotation.BoolRes;
10 | import androidx.annotation.ColorInt;
11 | import androidx.annotation.Px;
12 | import androidx.annotation.RequiresApi;
13 | import android.util.AttributeSet;
14 | import android.view.View;
15 |
16 | import com.airbnb.paris.annotations.Attr;
17 | import com.airbnb.paris.annotations.Fraction;
18 | import com.airbnb.paris.annotations.LayoutDimension;
19 | import com.airbnb.paris.annotations.Styleable;
20 |
21 | @Styleable("Formats")
22 | public class MyView extends View {
23 |
24 | public MyView(Context context) {
25 | super(context);
26 | }
27 |
28 | public MyView(Context context, AttributeSet attrs) {
29 | super(context, attrs);
30 | }
31 |
32 | public MyView(Context context, AttributeSet attrs, int defStyle) {
33 | super(context, attrs, defStyle);
34 | }
35 |
36 | @Attr(value = R2.styleable.Formats_formatBoolean, defaultValue = R2.bool.format_boolean)
37 | @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
38 | public void formatBoolean(boolean value) {}
39 | }
40 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/attr_requires_api_default_value/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/attrs/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/attrs_r_class_import_as_type_alias/input/MyView.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import androidx.annotation.ColorInt
7 | import com.airbnb.paris.annotations.Attr
8 | import com.airbnb.paris.annotations.Style
9 | import com.airbnb.paris.annotations.Styleable
10 | import com.airbnb.paris.extensions.myViewStyle
11 | import com.airbnb.paris.test.R2 as typeAliasedR
12 |
13 | @Styleable("Formats")
14 | class MyView @JvmOverloads constructor(
15 | context: Context,
16 | attrs: AttributeSet? = null,
17 | defStyle: Int = 0
18 | ) : View(context, attrs, defStyle) {
19 |
20 | // Makes the method public
21 | @Attr(typeAliasedR.styleable.Formats_formatBoolean)
22 | fun showDivider(show: Boolean) {
23 |
24 | }
25 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/attrs_r_class_import_as_type_alias/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/attrs_r_class_import_fully_qualified/input/MyView.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import androidx.annotation.ColorInt
7 | import com.airbnb.paris.annotations.Attr
8 | import com.airbnb.paris.annotations.Style
9 | import com.airbnb.paris.annotations.Styleable
10 | import com.airbnb.paris.extensions.myViewStyle
11 | import com.airbnb.paris.test.R2.styleable.Formats_formatBoolean
12 |
13 | @Styleable("Formats")
14 | class MyView @JvmOverloads constructor(
15 | context: Context,
16 | attrs: AttributeSet? = null,
17 | defStyle: Int = 0
18 | ) : View(context, attrs, defStyle) {
19 |
20 | // Makes the method public
21 | @Attr(Formats_formatBoolean)
22 | fun showDivider(show: Boolean) {
23 |
24 | }
25 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/attrs_r_class_import_fully_qualified/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/default_values/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/empty_default_style/input/MyViewWithoutStyle.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 |
7 | import com.airbnb.paris.annotations.Attr;
8 | import com.airbnb.paris.annotations.Styleable;
9 |
10 | @Styleable(emptyDefaultStyle = true)
11 | public class MyViewWithoutStyle extends View {
12 |
13 | public MyViewWithoutStyle(Context context) {
14 | super(context);
15 | }
16 |
17 | public MyViewWithoutStyle(Context context, AttributeSet attrs) {
18 | super(context, attrs);
19 | }
20 |
21 | public MyViewWithoutStyle(Context context, AttributeSet attrs, int defStyle) {
22 | super(context, attrs, defStyle);
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/empty_default_style/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test.styleable_in_other_module;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class, namespacedResourcesEnabled = true)
6 | class PackageInfo {
7 |
8 | }
9 |
10 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/empty_default_style/output/MyViewWithoutStyleStyleApplier.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import android.content.Context;
4 | import android.view.ViewStyleApplier;
5 | import androidx.annotation.UiThread;
6 | import com.airbnb.paris.StyleApplier;
7 | import com.airbnb.paris.styles.Style;
8 | import java.lang.Override;
9 |
10 | @UiThread
11 | public final class MyViewWithoutStyleStyleApplier extends StyleApplier {
12 | public MyViewWithoutStyleStyleApplier(MyViewWithoutStyle view) {
13 | super(view);
14 | }
15 |
16 | @Override
17 | protected void applyParent(Style style) {
18 | ViewStyleApplier applier = new ViewStyleApplier(getView());
19 | applier.setDebugListener(getDebugListener());
20 | applier.apply(style);
21 | }
22 |
23 | public StyleBuilder builder() {
24 | return new StyleBuilder(this);
25 | }
26 |
27 | /**
28 | * Empty style.
29 | */
30 | public void applyDefault() {
31 | }
32 |
33 | /**
34 | * For debugging
35 | */
36 | public static void assertStylesContainSameAttributes(Context context) {
37 | }
38 |
39 | public abstract static class BaseStyleBuilder, A extends StyleApplier, ?>> extends ViewStyleApplier.BaseStyleBuilder {
40 | public BaseStyleBuilder(A applier) {
41 | super(applier);
42 | }
43 |
44 | public BaseStyleBuilder() {
45 | }
46 |
47 | public B applyTo(MyViewWithoutStyle view) {
48 | new MyViewWithoutStyleStyleApplier(view).apply(build());
49 | return (B) this;
50 | }
51 | }
52 |
53 | @UiThread
54 | public static final class StyleBuilder extends BaseStyleBuilder {
55 | public StyleBuilder(MyViewWithoutStyleStyleApplier applier) {
56 | super(applier);
57 | }
58 |
59 | public StyleBuilder() {
60 | }
61 |
62 | /**
63 | * Empty style.
64 | */
65 | public StyleBuilder addDefault() {
66 | return this;
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_attr_non_res_default_value/input/MyView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.widget.TextView;
7 |
8 | import com.airbnb.paris.annotations.Attr;
9 | import com.airbnb.paris.annotations.Styleable;
10 |
11 | @Styleable("MyView")
12 | public class MyView extends View {
13 |
14 | public MyView(Context context) {
15 | super(context);
16 | }
17 |
18 | public MyView(Context context, AttributeSet attrs) {
19 | super(context, attrs);
20 | }
21 |
22 | public MyView(Context context, AttributeSet attrs, int defStyle) {
23 | super(context, attrs, defStyle);
24 | }
25 |
26 | // This generates an error
27 | @Attr(value = R2.styleable.MyView_title, defaultValue = 5)
28 | public void setTitle(CharSequence title) {}
29 | }
30 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_attr_non_res_default_value/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_attr_non_res_value/input/MyView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.widget.TextView;
7 |
8 | import com.airbnb.paris.annotations.Attr;
9 | import com.airbnb.paris.annotations.Styleable;
10 |
11 | @Styleable("MyView")
12 | public class MyView extends View {
13 |
14 | public MyView(Context context) {
15 | super(context);
16 | }
17 |
18 | public MyView(Context context, AttributeSet attrs) {
19 | super(context, attrs);
20 | }
21 |
22 | public MyView(Context context, AttributeSet attrs, int defStyle) {
23 | super(context, attrs, defStyle);
24 | }
25 |
26 | // This generates an error
27 | @Attr(5)
28 | public void setTitle(CharSequence title) {}
29 | }
30 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_attr_non_res_value/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_attr_wrong_default_value_type/input/MyView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.widget.TextView;
7 |
8 | import com.airbnb.paris.annotations.Attr;
9 | import com.airbnb.paris.annotations.Styleable;
10 |
11 | @Styleable("MyView")
12 | public class MyView extends View {
13 |
14 | public MyView(Context context) {
15 | super(context);
16 | }
17 |
18 | public MyView(Context context, AttributeSet attrs) {
19 | super(context, attrs);
20 | }
21 |
22 | public MyView(Context context, AttributeSet attrs, int defStyle) {
23 | super(context, attrs, defStyle);
24 | }
25 |
26 | // This generates an error
27 | @Attr(value = R2.styleable.MyView_title, defaultValue = R2.string.fjdksalfjdsakjfwefe)
28 | public void setTitle(CharSequence title) {}
29 | }
30 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_attr_wrong_default_value_type/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_attr_wrong_value_type/input/MyView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.widget.TextView;
7 |
8 | import com.airbnb.paris.annotations.Attr;
9 | import com.airbnb.paris.annotations.Styleable;
10 |
11 | @Styleable("MyView")
12 | public class MyView extends View {
13 |
14 | public MyView(Context context) {
15 | super(context);
16 | }
17 |
18 | public MyView(Context context, AttributeSet attrs) {
19 | super(context, attrs);
20 | }
21 |
22 | public MyView(Context context, AttributeSet attrs, int defStyle) {
23 | super(context, attrs, defStyle);
24 | }
25 |
26 | // This generates an error
27 | @Attr(R2.styleable.this_doesnt_exist)
28 | public void setTitle(CharSequence title) {}
29 | }
30 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_attr_wrong_value_type/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_no_default_style/input/MyViewWithoutStyle.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 |
7 | import com.airbnb.paris.annotations.Attr;
8 | import com.airbnb.paris.annotations.Styleable;
9 |
10 | @Styleable
11 | public class MyViewWithoutStyle extends View {
12 |
13 | public MyViewWithoutStyle(Context context) {
14 | super(context);
15 | }
16 |
17 | public MyViewWithoutStyle(Context context, AttributeSet attrs) {
18 | super(context, attrs);
19 | }
20 |
21 | public MyViewWithoutStyle(Context context, AttributeSet attrs, int defStyle) {
22 | super(context, attrs, defStyle);
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_no_default_style/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test.styleable_in_other_module;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class, namespacedResourcesEnabled = true)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_non_final_style_field/input/MyView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.widget.TextView;
7 |
8 | import com.airbnb.paris.annotations.Attr;
9 | import com.airbnb.paris.annotations.Style;
10 | import com.airbnb.paris.annotations.Styleable;
11 | import com.airbnb.paris.test.MyViewStyleApplier;
12 |
13 | @Styleable("MyView")
14 | public class MyView extends View {
15 |
16 | @Style
17 | static com.airbnb.paris.styles.Style myStyle = new MyViewStyleApplier.StyleBuilder().build();
18 |
19 | public MyView(Context context) {
20 | super(context);
21 | }
22 |
23 | public MyView(Context context, AttributeSet attrs) {
24 | super(context, attrs);
25 | }
26 |
27 | public MyView(Context context, AttributeSet attrs, int defStyle) {
28 | super(context, attrs, defStyle);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_non_final_style_field/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_non_static_style_field/input/MyView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.widget.TextView;
7 |
8 | import com.airbnb.paris.annotations.Attr;
9 | import com.airbnb.paris.annotations.Style;
10 | import com.airbnb.paris.annotations.Styleable;
11 | import com.airbnb.paris.test.MyViewStyleApplier;
12 |
13 | @Styleable("MyView")
14 | public class MyView extends View {
15 |
16 | @Style
17 | final com.airbnb.paris.styles.Style myStyle = new MyViewStyleApplier.StyleBuilder().build();
18 |
19 | public MyView(Context context) {
20 | super(context);
21 | }
22 |
23 | public MyView(Context context, AttributeSet attrs) {
24 | super(context, attrs);
25 | }
26 |
27 | public MyView(Context context, AttributeSet attrs, int defStyle) {
28 | super(context, attrs, defStyle);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_non_static_style_field/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_private_style_field/input/MyView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.widget.TextView;
7 |
8 | import com.airbnb.paris.annotations.Attr;
9 | import com.airbnb.paris.annotations.Style;
10 | import com.airbnb.paris.annotations.Styleable;
11 | import com.airbnb.paris.test.MyViewStyleApplier;
12 |
13 | @Styleable("MyView")
14 | public class MyView extends View {
15 |
16 | @Style
17 | private static final com.airbnb.paris.styles.Style myStyle = new MyViewStyleApplier.StyleBuilder().build();
18 |
19 | public MyView(Context context) {
20 | super(context);
21 | }
22 |
23 | public MyView(Context context, AttributeSet attrs) {
24 | super(context, attrs);
25 | }
26 |
27 | public MyView(Context context, AttributeSet attrs, int defStyle) {
28 | super(context, attrs, defStyle);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_private_style_field/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_style_field_invalid_type/input/MyView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.widget.TextView;
7 |
8 | import com.airbnb.paris.annotations.Attr;
9 | import com.airbnb.paris.annotations.Style;
10 | import com.airbnb.paris.annotations.Styleable;
11 | import com.airbnb.paris.test.MyViewStyleApplier;
12 |
13 | @Styleable("MyView")
14 | public class MyView extends View {
15 |
16 | @Style
17 | static final Object myStyle = new MyViewStyleApplier.StyleBuilder().build();
18 |
19 | public MyView(Context context) {
20 | super(context);
21 | }
22 |
23 | public MyView(Context context, AttributeSet attrs) {
24 | super(context, attrs);
25 | }
26 |
27 | public MyView(Context context, AttributeSet attrs, int defStyle) {
28 | super(context, attrs, defStyle);
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_style_field_invalid_type/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_styleable_child_wrong_value_type/input/MyView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.widget.TextView;
7 |
8 | import com.airbnb.paris.annotations.Styleable;
9 | import com.airbnb.paris.annotations.StyleableChild;
10 |
11 | @Styleable("MyView")
12 | public class MyView extends View {
13 |
14 | // This generates an error
15 | @StyleableChild(R2.styleable.this_doesnt_exist)
16 | TextView title;
17 |
18 | public MyView(Context context) {
19 | super(context);
20 | }
21 |
22 | public MyView(Context context, AttributeSet attrs) {
23 | super(context, attrs);
24 | }
25 |
26 | public MyView(Context context, AttributeSet attrs, int defStyle) {
27 | super(context, attrs, defStyle);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_styleable_child_wrong_value_type/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_styleable_outside_package_no_R/input/MyView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.other;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 |
7 | import com.airbnb.paris.annotations.Styleable;
8 |
9 | @Styleable
10 | public class MyView extends View {
11 |
12 | public MyView(Context context) {
13 | super(context);
14 | }
15 |
16 | public MyView(Context context, AttributeSet attrs) {
17 | super(context, attrs);
18 | }
19 |
20 | public MyView(Context context, AttributeSet attrs, int defStyle) {
21 | super(context, attrs, defStyle);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_styleable_outside_package_with_attr_and_namespaced_resources/input/MyView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.other;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 |
7 | import com.airbnb.paris.annotations.Attr;
8 | import com.airbnb.paris.annotations.Style;
9 | import com.airbnb.paris.annotations.Styleable;
10 | import com.airbnb.paris.test.R;
11 |
12 | @Styleable("MyLibView")
13 | public class MyView extends View {
14 |
15 | public MyView(Context context) {
16 | super(context);
17 | }
18 |
19 | public MyView(Context context, AttributeSet attrs) {
20 | super(context, attrs);
21 | }
22 |
23 | public MyView(Context context, AttributeSet attrs, int defStyle) {
24 | super(context, attrs, defStyle);
25 | }
26 |
27 | @Attr(com.airbnb.paris.test.lib.R2.styleable.MyLibView_title)
28 | public void setTitle(String title) {
29 |
30 | }
31 |
32 | @Style static final int DEFAULT_STYLE = R.style.MyView_Blue;
33 | }
34 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_styleable_outside_package_with_attr_and_namespaced_resources/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test.styleableoutsidepackagewithattrandnamespacedresources;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(namespacedResourcesEnabled = true)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_two_default_styles/input/MyView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.widget.TextView;
7 |
8 | import com.airbnb.paris.annotations.Attr;
9 | import com.airbnb.paris.annotations.Style;
10 | import com.airbnb.paris.annotations.Styleable;
11 | import com.airbnb.paris.test.MyViewStyleApplier;
12 |
13 | @Styleable("MyView")
14 | public class MyView extends View {
15 |
16 | @Style
17 | static final com.airbnb.paris.styles.Style defaultStyle = new MyViewStyleApplier.StyleBuilder().build();
18 |
19 | @Style(isDefault = true)
20 | static final com.airbnb.paris.styles.Style myStyle = new MyViewStyleApplier.StyleBuilder().build();
21 |
22 | public MyView(Context context) {
23 | super(context);
24 | }
25 |
26 | public MyView(Context context, AttributeSet attrs) {
27 | super(context, attrs);
28 | }
29 |
30 | public MyView(Context context, AttributeSet attrs, int defStyle) {
31 | super(context, attrs, defStyle);
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/error_two_default_styles/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/overridden_protected_function/input/MyView.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import androidx.annotation.ColorInt
7 | import com.airbnb.paris.annotations.Attr
8 | import com.airbnb.paris.annotations.Style
9 | import com.airbnb.paris.annotations.Styleable
10 | import com.airbnb.paris.extensions.myViewStyle
11 |
12 | @Styleable("Formats")
13 | class MyView @JvmOverloads constructor(
14 | context: Context,
15 | attrs: AttributeSet? = null,
16 | defStyle: Int = 0
17 | ) : MyViewSuper(context, attrs, defStyle) {
18 |
19 | // Makes the method public
20 | @Attr(R2.styleable.Formats_formatBoolean)
21 | public override fun showDivider(show: Boolean) {
22 |
23 | }
24 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/overridden_protected_function/input/MyViewSuper.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import androidx.annotation.ColorInt
7 | import com.airbnb.paris.annotations.Attr
8 | import com.airbnb.paris.annotations.Style
9 | import com.airbnb.paris.annotations.Styleable
10 | import com.airbnb.paris.extensions.myViewStyle
11 |
12 | @Styleable("Formats")
13 | abstract class MyViewSuper @JvmOverloads constructor(
14 | context: Context,
15 | attrs: AttributeSet? = null,
16 | defStyle: Int = 0
17 | ) : View(context, attrs, defStyle) {
18 |
19 | @Attr(value = R2.styleable.Formats_formatBoolean2)
20 | fun setInverse(inverse: Boolean) {
21 | }
22 |
23 | protected open fun showDivider(show: Boolean) {
24 | }
25 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/overridden_protected_function/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/style_extension_generation/input/MyView.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import com.airbnb.paris.annotations.Style
7 | import com.airbnb.paris.annotations.Styleable
8 | import com.airbnb.paris.extensions.myViewStyle
9 |
10 | @Styleable
11 | class MyView @JvmOverloads constructor(
12 | context: Context,
13 | attrs: AttributeSet? = null,
14 | defStyle: Int = 0
15 | ) : View(context, attrs, defStyle) {
16 |
17 | companion object {
18 | @Style
19 | val testStyle = myViewStyle {}
20 | }
21 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/style_extension_generation/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/style_extension_generation/output/MyViewStyleExtensions.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress(
2 | "DEPRECATION",
3 | "Detekt.MaxLineLength",
4 | )
5 |
6 | package com.airbnb.paris.extensions
7 |
8 | import android.util.AttributeSet
9 | import androidx.`annotation`.StyleRes
10 | import com.airbnb.paris.ExtendableStyleBuilder
11 | import com.airbnb.paris.styles.Style
12 | import com.airbnb.paris.test.MyView
13 | import com.airbnb.paris.test.MyViewStyleApplier
14 | import kotlin.Int
15 | import kotlin.Suppress
16 | import kotlin.Unit
17 |
18 | public fun MyView.style(style: Style): Unit {
19 | MyViewStyleApplier(this).apply(style)
20 | }
21 |
22 | public fun MyView.style(@StyleRes styleRes: Int): Unit {
23 | MyViewStyleApplier(this).apply(styleRes)
24 | }
25 |
26 | public fun MyView.style(attrs: AttributeSet?): Unit {
27 | MyViewStyleApplier(this).apply(attrs)
28 | }
29 |
30 | public inline fun MyView.style(builder: ExtendableStyleBuilder.() -> Unit): Unit {
31 | MyViewStyleApplier(this).apply(ExtendableStyleBuilder().apply(builder).build())
32 | }
33 |
34 | /**
35 | * @see MyView.testStyle
36 | */
37 | public fun ExtendableStyleBuilder.addTest(): Unit {
38 | add(MyViewStyleApplier.StyleBuilder().addTest().build())
39 | }
40 |
41 | /**
42 | * Empty style.
43 | */
44 | public fun ExtendableStyleBuilder.addDefault(): Unit {
45 | add(MyViewStyleApplier.StyleBuilder().addDefault().build())
46 | }
47 |
48 | public inline fun myViewStyle(builder: ExtendableStyleBuilder.() -> Unit): Style =
49 | ExtendableStyleBuilder().apply(builder).build()
50 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/style_extension_generation_java/input/MyView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 |
7 | import com.airbnb.paris.annotations.Style;
8 | import com.airbnb.paris.annotations.Styleable;
9 |
10 | @Styleable
11 | public class MyView extends View {
12 |
13 | @Style(isDefault = true)
14 | static final int RED_STYLE = R2.style.MyView_Red;
15 |
16 | @Style
17 | static final int greenStyle = R2.style.MyView_Red;
18 |
19 | public MyView(Context context) {
20 | super(context);
21 | }
22 |
23 | public MyView(Context context, AttributeSet attrs) {
24 | super(context, attrs);
25 | }
26 |
27 | public MyView(Context context, AttributeSet attrs, int defStyle) {
28 | super(context, attrs, defStyle);
29 | }
30 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/style_extension_generation_java/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/style_extension_generation_java/output/MyViewStyleExtensions.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress(
2 | "DEPRECATION",
3 | "Detekt.MaxLineLength",
4 | )
5 |
6 | package com.airbnb.paris.extensions
7 |
8 | import android.util.AttributeSet
9 | import androidx.`annotation`.StyleRes
10 | import com.airbnb.paris.ExtendableStyleBuilder
11 | import com.airbnb.paris.styles.Style
12 | import com.airbnb.paris.test.MyView
13 | import com.airbnb.paris.test.MyViewStyleApplier
14 | import kotlin.Int
15 | import kotlin.Suppress
16 | import kotlin.Unit
17 |
18 | public fun MyView.style(style: Style): Unit {
19 | MyViewStyleApplier(this).apply(style)
20 | }
21 |
22 | public fun MyView.style(@StyleRes styleRes: Int): Unit {
23 | MyViewStyleApplier(this).apply(styleRes)
24 | }
25 |
26 | public fun MyView.style(attrs: AttributeSet?): Unit {
27 | MyViewStyleApplier(this).apply(attrs)
28 | }
29 |
30 | public inline fun V.style(builder: ExtendableStyleBuilder.() -> Unit): Unit {
31 | MyViewStyleApplier(this).apply(ExtendableStyleBuilder().apply(builder).build())
32 | }
33 |
34 | /**
35 | * @see MyView.RED_STYLE
36 | */
37 | public fun ExtendableStyleBuilder.addRed(): Unit {
38 | add(MyViewStyleApplier.StyleBuilder().addRed().build())
39 | }
40 |
41 | /**
42 | * @see MyView.greenStyle
43 | */
44 | public fun ExtendableStyleBuilder.addGreen(): Unit {
45 | add(MyViewStyleApplier.StyleBuilder().addGreen().build())
46 | }
47 |
48 | /**
49 | * @see MyView.RED_STYLE
50 | */
51 | public fun ExtendableStyleBuilder.addDefault(): Unit {
52 | add(MyViewStyleApplier.StyleBuilder().addDefault().build())
53 | }
54 |
55 | public inline fun myViewStyle(builder: ExtendableStyleBuilder.() -> Unit): Style =
56 | ExtendableStyleBuilder().apply(builder).build()
57 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/style_extension_generation_jvm_static/input/MyView.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import com.airbnb.paris.annotations.Style
7 | import com.airbnb.paris.annotations.Styleable
8 | import com.airbnb.paris.extensions.myViewStyle
9 |
10 | @Styleable
11 | class MyView @JvmOverloads constructor(
12 | context: Context,
13 | attrs: AttributeSet? = null,
14 | defStyle: Int = 0
15 | ) : View(context, attrs, defStyle) {
16 |
17 | companion object {
18 | @JvmStatic
19 | @Style
20 | val testStyle = myViewStyle {}
21 | }
22 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/style_extension_generation_jvm_static/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/style_extension_generation_jvm_static/output/MyViewStyleExtensions.kt:
--------------------------------------------------------------------------------
1 | @file:Suppress(
2 | "DEPRECATION",
3 | "Detekt.MaxLineLength",
4 | )
5 |
6 | package com.airbnb.paris.extensions
7 |
8 | import android.util.AttributeSet
9 | import androidx.`annotation`.StyleRes
10 | import com.airbnb.paris.ExtendableStyleBuilder
11 | import com.airbnb.paris.styles.Style
12 | import com.airbnb.paris.test.MyView
13 | import com.airbnb.paris.test.MyViewStyleApplier
14 | import kotlin.Int
15 | import kotlin.Suppress
16 | import kotlin.Unit
17 |
18 | public fun MyView.style(style: Style): Unit {
19 | MyViewStyleApplier(this).apply(style)
20 | }
21 |
22 | public fun MyView.style(@StyleRes styleRes: Int): Unit {
23 | MyViewStyleApplier(this).apply(styleRes)
24 | }
25 |
26 | public fun MyView.style(attrs: AttributeSet?): Unit {
27 | MyViewStyleApplier(this).apply(attrs)
28 | }
29 |
30 | public inline fun MyView.style(builder: ExtendableStyleBuilder.() -> Unit): Unit {
31 | MyViewStyleApplier(this).apply(ExtendableStyleBuilder().apply(builder).build())
32 | }
33 |
34 | /**
35 | * @see MyView.testStyle
36 | */
37 | public fun ExtendableStyleBuilder.addTest(): Unit {
38 | add(MyViewStyleApplier.StyleBuilder().addTest().build())
39 | }
40 |
41 | /**
42 | * Empty style.
43 | */
44 | public fun ExtendableStyleBuilder.addDefault(): Unit {
45 | add(MyViewStyleApplier.StyleBuilder().addDefault().build())
46 | }
47 |
48 | public inline fun myViewStyle(builder: ExtendableStyleBuilder.() -> Unit): Style =
49 | ExtendableStyleBuilder().apply(builder).build()
50 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/style_in_kotlin_companion_object/input/MyView.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import androidx.annotation.Dimension
7 | import com.airbnb.paris.annotations.Attr
8 | import com.airbnb.paris.annotations.Style
9 | import com.airbnb.paris.annotations.Styleable
10 | import com.airbnb.paris.extensions.myViewStyle
11 |
12 | @Styleable("Formats")
13 | class MyView @JvmOverloads constructor(
14 | context: Context,
15 | attrs: AttributeSet? = null,
16 | defStyle: Int = 0
17 | ) : View(context, attrs, defStyle) {
18 |
19 | @Attr(R2.styleable.Formats_formatBoolean)
20 | fun showDivider(show: Boolean) {
21 |
22 | }
23 |
24 | @Attr(R2.styleable.Formats_formatBoolean2)
25 | fun showDivider2(show: Boolean) {
26 |
27 | }
28 |
29 | companion object {
30 | private fun foo(bar: Int = 1) = myViewStyle {
31 | println(bar)
32 | }
33 |
34 | @Style(isDefault = true)
35 | val testStyle = foo()
36 | @Style
37 | val testStyle2 = foo(2)
38 | @Style
39 | val testStyle23 = foo(3)
40 | @Style
41 | val testStyle234 = foo()
42 | @Style
43 | val testStyle2345 = foo()
44 | @Style
45 | val testStyle23456 = foo()
46 |
47 |
48 | }
49 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/style_in_kotlin_companion_object/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/styleable_fields/input/MyView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 | import android.widget.TextView;
7 |
8 | import com.airbnb.paris.annotations.Styleable;
9 | import com.airbnb.paris.annotations.StyleableChild;
10 |
11 | @Styleable("MyView")
12 | public class MyView extends View {
13 |
14 | @StyleableChild(R2.styleable.MyView_titleStyle)
15 | TextView title;
16 |
17 | @StyleableChild(R2.styleable.MyView_subtitleStyle)
18 | TextView subtitle;
19 |
20 | @StyleableChild(R2.styleable.MyView_dividerStyle)
21 | View divider;
22 |
23 | public MyView(Context context) {
24 | super(context);
25 | }
26 |
27 | public MyView(Context context, AttributeSet attrs) {
28 | super(context, attrs);
29 | }
30 |
31 | public MyView(Context context, AttributeSet attrs, int defStyle) {
32 | super(context, attrs, defStyle);
33 | }
34 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/styleable_fields/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/styleable_in_other_module_single_attr/input/MyView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 |
7 | import com.airbnb.paris.annotations.Attr;
8 | import com.airbnb.paris.annotations.Styleable;
9 |
10 | @Styleable("MyLibView")
11 | public class MyView extends View {
12 |
13 | public MyView(Context context) {
14 | super(context);
15 | }
16 |
17 | public MyView(Context context, AttributeSet attrs) {
18 | super(context, attrs);
19 | }
20 |
21 | public MyView(Context context, AttributeSet attrs, int defStyle) {
22 | super(context, attrs, defStyle);
23 | }
24 |
25 | @Attr(com.airbnb.paris.test.lib.R2.styleable.MyLibView_title)
26 | public void setTitle(String title) {
27 |
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/styleable_in_other_module_single_attr/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test.styleable_in_other_module;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/styleable_minimal/input/MyView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 |
7 | import com.airbnb.paris.annotations.Styleable;
8 |
9 | @Styleable
10 | public class MyView extends View {
11 |
12 | public MyView(Context context) {
13 | super(context);
14 | }
15 |
16 | public MyView(Context context, AttributeSet attrs) {
17 | super(context, attrs);
18 | }
19 |
20 | public MyView(Context context, AttributeSet attrs, int defStyle) {
21 | super(context, attrs, defStyle);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/styleable_minimal/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/styleable_minimal/output/MyViewStyleApplier.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import android.content.Context;
4 | import android.view.ViewStyleApplier;
5 | import androidx.annotation.UiThread;
6 | import com.airbnb.paris.StyleApplier;
7 | import com.airbnb.paris.styles.Style;
8 | import java.lang.Override;
9 |
10 | @UiThread
11 | public final class MyViewStyleApplier extends StyleApplier {
12 | public MyViewStyleApplier(MyView view) {
13 | super(view);
14 | }
15 |
16 | @Override
17 | protected void applyParent(Style style) {
18 | ViewStyleApplier applier = new ViewStyleApplier(getView());
19 | applier.setDebugListener(getDebugListener());
20 | applier.apply(style);
21 | }
22 |
23 | public StyleBuilder builder() {
24 | return new StyleBuilder(this);
25 | }
26 |
27 | /**
28 | * Empty style.
29 | */
30 | public void applyDefault() {
31 | }
32 |
33 | /**
34 | * For debugging
35 | */
36 | public static void assertStylesContainSameAttributes(Context context) {
37 | }
38 |
39 | public abstract static class BaseStyleBuilder, A extends StyleApplier, ?>> extends ViewStyleApplier.BaseStyleBuilder {
40 | public BaseStyleBuilder(A applier) {
41 | super(applier);
42 | }
43 |
44 | public BaseStyleBuilder() {
45 | }
46 |
47 | public B applyTo(MyView view) {
48 | new MyViewStyleApplier(view).apply(build());
49 | return (B) this;
50 | }
51 | }
52 |
53 | @UiThread
54 | public static final class StyleBuilder extends BaseStyleBuilder {
55 | public StyleBuilder(MyViewStyleApplier applier) {
56 | super(applier);
57 | }
58 |
59 | public StyleBuilder() {
60 | }
61 |
62 | /**
63 | * Empty style.
64 | */
65 | public StyleBuilder addDefault() {
66 | return this;
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/styleable_outside_package_single_attr/input/MyView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.other;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 |
7 | import com.airbnb.paris.annotations.Attr;
8 | import com.airbnb.paris.annotations.Styleable;
9 | import com.airbnb.paris.test.R2;
10 |
11 | @Styleable("MyView")
12 | public class MyView extends View {
13 |
14 | public MyView(Context context) {
15 | super(context);
16 | }
17 |
18 | public MyView(Context context, AttributeSet attrs) {
19 | super(context, attrs);
20 | }
21 |
22 | public MyView(Context context, AttributeSet attrs, int defStyle) {
23 | super(context, attrs, defStyle);
24 | }
25 |
26 | @Attr(R2.styleable.MyView_title)
27 | public void setTitle(String title) {
28 |
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/paris-test/src/test/resources/styleable_outside_package_single_attr/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/styles/input/MyView.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import android.content.Context;
4 | import android.util.AttributeSet;
5 | import android.view.View;
6 |
7 | import com.airbnb.paris.annotations.Style;
8 | import com.airbnb.paris.annotations.Styleable;
9 | import com.airbnb.paris.test.MyViewStyleApplier.StyleBuilder;
10 |
11 | @Styleable
12 | public class MyView extends View {
13 |
14 | @Style(isDefault = true)
15 | static final int RED_STYLE = R2.style.MyView_Red;
16 |
17 | @Style
18 | static final int greenStyle = R2.style.MyView_Red;
19 |
20 | public MyView(Context context) {
21 | super(context);
22 | }
23 |
24 | public MyView(Context context, AttributeSet attrs) {
25 | super(context, attrs);
26 | }
27 |
28 | public MyView(Context context, AttributeSet attrs, int defStyle) {
29 | super(context, attrs, defStyle);
30 | }
31 |
32 | @Style
33 | static void blue(StyleBuilder builder) {
34 | }
35 | }
--------------------------------------------------------------------------------
/paris-test/src/test/resources/styles/input/PackageInfo.java:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.test;
2 |
3 | import com.airbnb.paris.annotations.ParisConfig;
4 |
5 | @ParisConfig(rClass = com.airbnb.paris.test.R.class)
6 | class PackageInfo {
7 |
8 | }
--------------------------------------------------------------------------------
/paris/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/paris/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.jakewharton.butterknife'
3 | apply plugin: 'kotlin-android'
4 | apply plugin: 'kotlin-kapt'
5 | //apply plugin: 'com.google.devtools.ksp'
6 | apply plugin: "com.vanniktech.maven.publish"
7 |
8 | android {
9 | compileSdkVersion rootProject.COMPILE_SDK_VERSION
10 |
11 | defaultConfig {
12 | minSdkVersion rootProject.MIN_SDK_VERSION
13 | targetSdkVersion rootProject.TARGET_SDK_VERSION
14 | multiDexEnabled true
15 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
16 | }
17 |
18 | testOptions {
19 | unitTests {
20 | includeAndroidResources = true
21 | }
22 | }
23 |
24 | lintOptions {
25 | warningsAsErrors true
26 | // We use R2 to keep resource ids constant
27 | disable 'NonConstantResourceId'
28 | // UnusedResources has false positives
29 | disable 'UnusedResources'
30 | }
31 |
32 | compileOptions {
33 | sourceCompatibility rootProject.JAVA_SOURCE_VERSION
34 | targetCompatibility rootProject.JAVA_TARGET_VERSION
35 | }
36 |
37 | kotlinOptions {
38 | jvmTarget = '1.8'
39 | }
40 |
41 |
42 | }
43 |
44 | dependencies {
45 | implementation deps.appcompat
46 |
47 | api project(':paris-annotations')
48 |
49 | kapt project(':paris-processor')
50 | kaptTest project(':paris-processor')
51 |
52 | testImplementation deps.junit
53 | testImplementation deps.kotlinTest
54 | testImplementation deps.mockitoCore
55 | testImplementation deps.robolectric
56 |
57 |
58 | androidTestImplementation deps.mockitoAndroid
59 | androidTestImplementation deps.espresso
60 | }
61 |
62 | kotlin {
63 | sourceSets {
64 | // Required for KSP to work with butterknife gradle plugin codegen of R2
65 | // main.kotlin.srcDirs += 'build/generated/source/r2/debug'
66 | // main.kotlin.srcDirs += 'build/generated/source/r2/release'
67 |
68 | // Required for KSP to work with unit tests
69 | // test.kotlin.srcDirs += 'build/generated/ksp/debugUnitTest/kotlin'
70 | }
71 | }
72 |
--------------------------------------------------------------------------------
/paris/gradle.properties:
--------------------------------------------------------------------------------
1 | POM_NAME=Paris
2 | POM_ARTIFACT_ID=paris
3 | POM_PACKAGING=jar
--------------------------------------------------------------------------------
/paris/src/androidTest/java/com/airbnb/paris/StyleApplierTest.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris
2 |
3 | import android.content.Context
4 | import android.view.View
5 | import androidx.test.InstrumentationRegistry
6 | import androidx.test.runner.AndroidJUnit4
7 | import com.airbnb.paris.styles.Style
8 | import com.airbnb.paris.typed_array_wrappers.TypedArrayWrapper
9 | import org.junit.Assert.assertEquals
10 | import org.junit.Assert.assertNotEquals
11 | import org.junit.Before
12 | import org.junit.Test
13 | import org.junit.runner.RunWith
14 |
15 | @RunWith(AndroidJUnit4::class)
16 | class StyleApplierTest {
17 |
18 | private open class TestStyleApplier(view: View) : StyleApplier(view)
19 |
20 | private lateinit var context: Context
21 | private lateinit var view: View
22 | private lateinit var applier: TestStyleApplier;
23 |
24 | private fun newView() = View(context)
25 |
26 | @Before
27 | fun setup() {
28 | context = InstrumentationRegistry.getTargetContext()
29 | view = View(context)
30 | applier = TestStyleApplier(view)
31 | }
32 |
33 | @Test
34 | fun equals_empty_sameView() {
35 | val view = newView()
36 | assertEquals(TestStyleApplier(view), TestStyleApplier(view))
37 | }
38 |
39 | @Test
40 | fun equals_empty_differentViews() {
41 | assertNotEquals(TestStyleApplier(newView()), TestStyleApplier(newView()))
42 | }
43 |
44 | @Test
45 | fun nullAttributeSet() {
46 | // Applying a null AttributeSet should be a no-op
47 |
48 | var methodCallCount = 0
49 | val applier = object : TestStyleApplier(view) {
50 | override fun attributes(): IntArray? = intArrayOf(1, 2, 3)
51 |
52 | override fun processAttributes(style: Style, a: TypedArrayWrapper) {
53 | methodCallCount++
54 | }
55 | }
56 | applier.apply(null)
57 |
58 | assertEquals(0, methodCallCount)
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/paris/src/androidTest/java/com/airbnb/paris/proxies/BaseViewMappings.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.proxies
2 |
3 | import android.content.res.ColorStateList
4 | import android.graphics.Color
5 | import android.graphics.drawable.ColorDrawable
6 |
7 |
8 | internal val ARBITRARY_COLORS = listOf(Color.GREEN, Color.GREEN, Color.BLUE, Color.TRANSPARENT)
9 | internal val ARBITRARY_COLOR_STATE_LISTS = listOf(
10 | ColorStateList(
11 | arrayOf(
12 | intArrayOf(android.R.attr.state_enabled),
13 | intArrayOf(-android.R.attr.state_enabled)
14 | ), intArrayOf(Color.RED, Color.GREEN)
15 | )
16 | )
17 | internal val ARBITRARY_COLOR_DRAWABLES = listOf(
18 | ColorDrawable(Color.RED),
19 | ColorDrawable(Color.GREEN),
20 | ColorDrawable(Color.BLUE)
21 | )
22 | internal val ARBITRARY_DIMENSIONS = listOf(Integer.MIN_VALUE, -150, 0, 10, 20, 50, 200, 800, Integer.MAX_VALUE)
23 | internal val ARBITRARY_FLOATS = listOf(-5f, 0f, 8f, 10f, 11.5f, 17f)
24 | internal val ARBITRARY_INTS = listOf(Integer.MIN_VALUE, -5, 0, 1, 2, 3, 5, 15, Integer.MAX_VALUE)
25 | internal val ARBITRARY_STRINGS = listOf("string", "Hello World", "12345")
26 | internal val ARBITRARY_RESOURCE_ID = 2
27 | internal val BOOLS = listOf(true, false)
28 |
29 | // TODO What about Dp and ColorInt methods?
30 |
31 | internal open class BaseViewMapping, View : android.view.View, Input : Any?> protected constructor(
32 | val testValues: List,
33 | val attrRes: Int,
34 | val setProxyFunction: Proxy.(Input) -> Unit,
35 | val setStyleBuilderValueFunction: Builder.(Input) -> Any,
36 | val setStyleBuilderResFunction: Builder.(Int) -> Any,
37 | /**
38 | * A function which, when called, will assert that the view has been successfully modified
39 | * by the associated proxy and/or style builder methods
40 | */
41 | val assertViewSet: (View, Input) -> Unit
42 | )
--------------------------------------------------------------------------------
/paris/src/androidTest/java/com/airbnb/paris/proxies/DeprecatedTextViewProxyTest.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.proxies
2 |
3 | import android.content.Context
4 | import android.content.res.Resources
5 | import android.graphics.Color
6 | import android.graphics.drawable.ColorDrawable
7 | import android.widget.TextView
8 | import androidx.test.InstrumentationRegistry
9 | import androidx.test.runner.AndroidJUnit4
10 | import org.junit.Assert.assertEquals
11 | import org.junit.Assert.assertNull
12 | import org.junit.Before
13 | import org.junit.Test
14 | import org.junit.runner.RunWith
15 |
16 | @RunWith(AndroidJUnit4::class)
17 | class DeprecatedTextViewProxyTest {
18 |
19 | private lateinit var context: Context
20 | private lateinit var res: Resources
21 | private lateinit var view: TextView
22 | private lateinit var proxy: TextViewProxy
23 |
24 | @Before
25 | fun setup() {
26 | context = InstrumentationRegistry.getTargetContext()
27 | res = context.resources
28 | view = TextView(context)
29 | proxy = TextViewProxy(view)
30 | }
31 |
32 | @Test
33 | fun setDrawables() {
34 | // Sets drawables on all sides
35 |
36 | val drawableBottom = ColorDrawable(Color.GREEN)
37 | proxy.setDrawableBottom(drawableBottom)
38 |
39 | val drawableLeft = ColorDrawable(Color.RED)
40 | proxy.setDrawableLeft(drawableLeft)
41 |
42 | val drawableRight = ColorDrawable(Color.BLACK)
43 | proxy.setDrawableRight(drawableRight)
44 |
45 | val drawableTop = ColorDrawable(Color.YELLOW)
46 | proxy.setDrawableTop(drawableTop)
47 |
48 | // Assumes the style parameter isn't used
49 | proxy.afterStyle(null)
50 |
51 | assertEquals(drawableBottom, view.compoundDrawables[3])
52 | assertEquals(drawableLeft, view.compoundDrawables[0])
53 | assertEquals(drawableRight, view.compoundDrawables[2])
54 | assertEquals(drawableTop, view.compoundDrawables[1])
55 | }
56 |
57 | @Test(expected = IllegalStateException::class)
58 | fun setEllipsize_invalidValue() {
59 | proxy.setEllipsize(5)
60 | }
61 |
62 | @Test
63 | fun setTextAllCaps_true() {
64 | proxy.setTextAllCaps(true)
65 | // TODO This doesn't work for some reason
66 | //assertTrue(view.transformationMethod is AllCapsTransformationMethod)
67 | }
68 |
69 | @Test
70 | fun setTextAllCaps_false() {
71 | proxy.setTextAllCaps(false)
72 | // TODO This would be true regardless
73 | assertNull(view.transformationMethod)
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/paris/src/androidTest/java/com/airbnb/paris/proxies/ImageViewStyleApplierTest.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.proxies
2 |
3 | import android.widget.ImageView
4 | import android.widget.ImageViewStyleApplier
5 | import android.widget.ImageViewStyleApplier.StyleBuilder
6 | import androidx.test.InstrumentationRegistry
7 | import androidx.test.runner.AndroidJUnit4
8 | import org.junit.Before
9 | import org.junit.Test
10 | import org.junit.runner.RunWith
11 |
12 | @RunWith(AndroidJUnit4::class)
13 | class ImageViewStyleApplierTest {
14 |
15 | private val context = InstrumentationRegistry.getTargetContext()!!
16 | private lateinit var view: ImageView
17 | private lateinit var styleApplier: ImageViewStyleApplier
18 | private lateinit var styleBuilder: StyleBuilder
19 |
20 | @Before
21 | fun setup() {
22 | view = ImageView(context)
23 | styleApplier = ImageViewStyleApplier(view)
24 | styleBuilder = StyleBuilder()
25 | }
26 |
27 | @Test
28 | fun auto() {
29 | for (mapping in (VIEW_MAPPINGS + IMAGE_VIEW_MAPPINGS)) {
30 | mapping as BaseViewMapping
31 |
32 | setup()
33 |
34 | mapping.testValues.forEach {
35 | // Set the value on the style builder
36 | mapping.setStyleBuilderValueFunction(styleBuilder, it)
37 | // Apply the style to the view
38 | styleApplier.apply(styleBuilder.build())
39 | // Check that the value was correctly applied
40 | mapping.assertViewSet(view, it)
41 | }
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/paris/src/androidTest/java/com/airbnb/paris/proxies/ImageViewStyleApplier_StyleBuilderTest.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.proxies
2 |
3 | import android.widget.ImageView
4 | import android.widget.ImageViewStyleApplier.StyleBuilder
5 | import androidx.test.runner.AndroidJUnit4
6 | import com.airbnb.paris.styles.ProgrammaticStyle
7 | import org.junit.Assert
8 | import org.junit.Before
9 | import org.junit.Test
10 | import org.junit.runner.RunWith
11 |
12 | @RunWith(AndroidJUnit4::class)
13 | class ImageViewStyleApplier_StyleBuilderTest {
14 |
15 | private lateinit var programmaticStyleBuilder: ProgrammaticStyle.Builder
16 | private lateinit var styleBuilder: StyleBuilder
17 |
18 | @Before
19 | fun setup() {
20 | programmaticStyleBuilder = ProgrammaticStyle.builder().debugName("test")
21 | styleBuilder = StyleBuilder().debugName("test")
22 | }
23 |
24 | @Test
25 | fun auto() {
26 | for (mapping in IMAGE_VIEW_MAPPINGS) {
27 | mapping as BaseViewMapping
28 |
29 | // For normal values
30 | mapping.testValues.forEach {
31 | setup()
32 |
33 | programmaticStyleBuilder.put(mapping.attrRes, it)
34 | mapping.setStyleBuilderValueFunction(styleBuilder, it)
35 | Assert.assertEquals(programmaticStyleBuilder.build(), styleBuilder.build())
36 | }
37 |
38 | // For resource ids
39 | setup()
40 |
41 | programmaticStyleBuilder.putRes(mapping.attrRes, ARBITRARY_RESOURCE_ID)
42 | mapping.setStyleBuilderResFunction(styleBuilder, ARBITRARY_RESOURCE_ID)
43 | Assert.assertEquals(programmaticStyleBuilder.build(), styleBuilder.build())
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/paris/src/androidTest/java/com/airbnb/paris/spannables/SpannableBuilderTest.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.spannables
2 |
3 | import androidx.test.InstrumentationRegistry
4 | import androidx.test.runner.AndroidJUnit4
5 | import com.airbnb.paris.styles.ProgrammaticStyle
6 | import com.airbnb.paris.styles.ResourceStyle
7 | import org.hamcrest.MatcherAssert.assertThat
8 | import org.hamcrest.Matchers.equalTo
9 | import org.junit.Test
10 | import org.junit.runner.RunWith
11 |
12 | @RunWith(AndroidJUnit4::class)
13 | class SpannableBuilderTest {
14 |
15 | val context = InstrumentationRegistry.getTargetContext()!!
16 |
17 | @Test
18 | fun emptyString() {
19 |
20 | val builder = SpannableBuilder()
21 |
22 | assertThat(builder.markupItems.isEmpty(), equalTo(true))
23 | assertThat(builder.stringBuilder.toString(), equalTo(""))
24 | }
25 |
26 | @Test
27 | fun appendingStyledStrings() {
28 |
29 | val dummyStyle = ProgrammaticStyle.builder().build();
30 |
31 | val builder = SpannableBuilder()
32 | .append("Good", 1)
33 | .append(" Morning", dummyStyle)
34 |
35 | assertThat(
36 | builder.markupItems, equalTo(
37 | setOf(
38 | StyleConverter.MarkupItem(IntRange(0, 4), ResourceStyle(1)),
39 | StyleConverter.MarkupItem(IntRange(4, 12), dummyStyle)
40 | )
41 | )
42 | )
43 | assertThat(builder.stringBuilder.toString(), equalTo("Good Morning"))
44 | }
45 |
46 | @Test
47 | fun appendingNonStyledStrings() {
48 |
49 | val builder = SpannableBuilder()
50 | .append("Good")
51 | .append(" Morning")
52 |
53 | assertThat(builder.markupItems, equalTo(emptySet()))
54 | assertThat(builder.stringBuilder.toString(), equalTo("Good Morning"))
55 | }
56 | }
--------------------------------------------------------------------------------
/paris/src/androidTest/res/drawable/format_drawable.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/paris/src/androidTest/res/drawable/format_drawable_2.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/paris/src/androidTest/res/values/booleans.xml:
--------------------------------------------------------------------------------
1 |
2 | true
3 |
4 |
--------------------------------------------------------------------------------
/paris/src/debug/res/font/roboto_regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/airbnb/paris/d8b5edbc56253bcdd0d0c57930d2e91113dd0f37/paris/src/debug/res/font/roboto_regular.ttf
--------------------------------------------------------------------------------
/paris/src/debug/res/values/test_common.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | true
7 | 2
8 | This is a string
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/paris/src/debug/res/values/test_image_view_style_extensions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/paris/src/debug/res/values/test_text_view_style_applier.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
11 |
12 |
15 |
16 |
19 |
20 |
23 |
24 |
27 |
28 |
32 |
33 |
36 |
37 |
40 |
41 |
45 |
46 |
49 |
50 |
53 |
54 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/paris/src/debug/res/values/test_text_view_style_extensions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/paris/src/debug/res/values/test_typed_array_typed_array_wrapper.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
11 |
12 |
15 |
16 |
19 |
20 |
23 |
24 |
27 |
28 |
29 |
30 |
33 |
34 |
37 |
38 |
41 |
42 |
45 |
46 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/paris/src/debug/res/values/test_view_group_style_extensions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/paris/src/debug/res/values/test_view_style_builder.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 0
5 | 1
6 | 2
7 |
8 |
9 |
--------------------------------------------------------------------------------
/paris/src/debug/res/values/test_view_style_extensions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/paris/src/debug/res/values/test_with_attr_function_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/paris/src/debug/res/values/test_with_attr_property_setter_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/paris/src/debug/res/values/test_with_internal_attr_function_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/paris/src/debug/res/values/test_with_internal_attr_property_setter_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/paris/src/debug/res/values/test_with_styleable_child_internal_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/paris/src/debug/res/values/test_with_styleable_child_kotlin_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/paris/src/debug/res/values/test_with_styleable_child_kotlin_view_style_extensions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/paris/src/debug/res/values/test_with_styleable_child_lateinit_view_style_extensions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/paris/src/debug/res/values/test_with_styleable_child_property_delegate_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/paris/src/debug/res/values/test_with_styleable_child_property_delegate_view_style_extensions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/paris/src/debug/res/values/test_with_styleable_child_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/paris/src/debug/res/values/test_with_styleable_child_view_style_extensions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/paris/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/paris/src/main/java/com/airbnb/paris/ExtendableStyleBuilder.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris
2 |
3 | import android.view.View
4 |
5 | /**
6 | * Meant to be extended with attribute and linked style functions
7 | */
8 | class ExtendableStyleBuilder : StyleBuilder, StyleApplier<*, V>>() {
9 |
10 | // Makes the builder public so that extensions can access it
11 | public override var builder = super.builder
12 |
13 | // Makes the function public so that extensions can access it
14 | @Suppress("RedundantOverride")
15 | public override fun consumeProgrammaticStyleBuilder() {
16 | super.consumeProgrammaticStyleBuilder()
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/paris/src/main/java/com/airbnb/paris/attribute_values/ColorValue.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.attribute_values
2 |
3 | import androidx.annotation.ColorInt
4 |
5 | internal data class ColorValue(@ColorInt val colorValue: Int)
6 |
7 |
--------------------------------------------------------------------------------
/paris/src/main/java/com/airbnb/paris/attribute_values/DpValue.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.attribute_values
2 |
3 | import androidx.annotation.Dimension
4 |
5 | internal data class DpValue(@Dimension(unit = Dimension.DP) val dpValue: Int)
6 |
--------------------------------------------------------------------------------
/paris/src/main/java/com/airbnb/paris/attribute_values/ResourceId.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.attribute_values
2 |
3 | import androidx.annotation.AnyRes
4 |
5 | internal data class ResourceId(@AnyRes val resId: Int)
6 |
--------------------------------------------------------------------------------
/paris/src/main/java/com/airbnb/paris/attribute_values/Styles.kt:
--------------------------------------------------------------------------------
1 | package com.airbnb.paris.attribute_values
2 |
3 | import com.airbnb.paris.styles.Style
4 |
5 | internal data class Styles(val list: MutableList
10 |
11 |
17 |
18 |
23 |
24 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/sample/src/main/res/values/styles_view_section.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | }
5 | }
6 |
7 | include ':sample', ':paris-annotations', ':paris-processor', ':paris', ':paris-test', ':paris-test-lib'
8 |
9 |
10 |
--------------------------------------------------------------------------------