From e67460430ecff2edb6bff49fcf308df2a2fedb90 Mon Sep 17 00:00:00 2001 From: Eli Hart Date: Fri, 22 May 2020 11:35:21 -0500 Subject: [PATCH] Incremental annotation processor (#136) * Incremental annotation processor * Fix annotation retention to class --- .idea/codeStyles/Project.xml | 495 +++++++++--------- .idea/codeStyles/codeStyleConfig.xml | 1 - CHANGELOG.md | 4 + README.md | 4 +- RELEASING.md | 2 +- blessedDeps.gradle | 17 +- build.gradle | 9 +- gradle.properties | 8 +- gradle/gradle-maven-push.gradle | 216 -------- gradle/workaround-missing-resource.gradle | 23 - gradle/wrapper/gradle-wrapper.properties | 2 +- paris-annotations/build.gradle | 3 +- .../com/airbnb/paris/annotations/Fraction.kt | 5 +- .../annotations/GeneratedStyleableClass.kt | 2 +- .../airbnb/paris/annotations/ParisConfig.java | 2 +- .../com/airbnb/paris/annotations/Style.kt | 3 +- paris-processor/build.gradle | 12 +- .../java/com/airbnb/paris/processor/Format.kt | 11 +- .../airbnb/paris/processor/ParisProcessor.kt | 31 +- .../com/airbnb/paris/processor/RFinder.kt | 29 +- .../airbnb/paris/processor/StyleablesTree.kt | 41 +- .../paris/processor/WithParisProcessor.kt | 1 + .../AndroidResourceId.kt | 2 +- .../AndroidResourceScanner.java | 115 ---- .../AndroidResourceScanner.kt | 122 +++++ .../framework/KotlinPoetConverters.kt | 14 +- .../framework/KotlinPoetExtensions.kt | 18 +- .../paris/processor/framework/Memoizer.kt | 18 + .../processor/framework/SkyExtensions.kt | 8 +- .../paris/processor/framework/SkyJavaClass.kt | 5 + .../processor/framework/SkyKotlinFile.kt | 5 +- .../paris/processor/framework/SkyMemoizer.kt | 9 + .../paris/processor/framework/SkyProcessor.kt | 19 +- .../processor/framework/WithSkyProcessor.kt | 4 +- .../models/SkyCompanionPropertyModel.kt | 12 +- .../processor/framework/models/SkyModel.kt | 3 +- .../framework/models/SkyPropertyModel.kt | 9 +- .../paris/processor/models/AfterStyleInfo.kt | 8 +- .../airbnb/paris/processor/models/AttrInfo.kt | 7 +- .../processor/models/BaseStyleableInfo.kt | 52 +- .../paris/processor/models/BeforeStyleInfo.kt | 9 +- .../models/StyleCompanionPropertyInfo.kt | 13 +- .../paris/processor/models/StyleInfo.kt | 19 +- .../processor/models/StyleStaticMethodInfo.kt | 10 +- .../paris/processor/models/StyleableInfo.kt | 26 +- .../writers/BaseStyleBuilderJavaClass.kt | 47 +- .../processor/writers/ModuleJavaClass.kt | 8 +- .../paris/processor/writers/ParisJavaClass.kt | 18 +- .../writers/StyleApplierJavaClass.kt | 56 +- .../writers/StyleBuilderJavaClass.kt | 16 +- .../writers/StyleExtensionsKotlinFile.kt | 51 +- paris-test-lib/build.gradle | 9 + paris-test/build.gradle | 32 +- .../paris/test/StyleApplierUtilsTest.kt | 82 +-- .../airbnb/paris/test/ViewStyleBuilderTest.kt | 59 ++- .../airbnb/paris/test/ParisProcessorTest.kt | 28 +- paris/build.gradle | 12 +- .../airbnb/paris/MapTypedArrayWrapperTest.kt | 22 +- .../paris/MultiTypedArrayWrapperTest.kt | 242 ++++----- .../java/com/airbnb/paris/StyleApplierTest.kt | 22 +- .../com/airbnb/paris/StyleApplierUtilsTest.kt | 219 ++++---- .../airbnb/paris/proxies/BaseViewMappings.kt | 43 +- .../proxies/DeprecatedTextViewProxyTest.kt | 2 +- .../DeprecatedTextViewStyleApplierTest.kt | 11 +- .../proxies/DeprecatedViewStyleApplierTest.kt | 33 +- .../paris/proxies/GenericViewProxyTests.kt | 14 +- .../airbnb/paris/proxies/ImageViewMappings.kt | 152 +++--- .../proxies/ImageViewStyleApplierTest.kt | 14 +- .../ImageViewStyleApplier_StyleBuilderTest.kt | 14 +- .../airbnb/paris/proxies/TextViewMappings.kt | 361 ++++++------- .../TextViewStyleApplier_StyleBuilderTest.kt | 30 +- .../com/airbnb/paris/proxies/ViewMappings.kt | 227 ++++---- .../ViewStyleApplier_StyleBuilderTest.kt | 30 +- .../paris/spannables/SpannableBuilderTest.kt | 24 +- .../paris/spannables/StyleConverterTest.kt | 53 +- .../java/com/airbnb/paris/StyleApplier.kt | 15 +- .../com/airbnb/paris/StyleApplierUtils.kt | 2 +- .../java/com/airbnb/paris/StyleBuilder.kt | 13 +- .../paris/attribute_values/ColorValue.kt | 2 +- .../airbnb/paris/attribute_values/DpValue.kt | 2 +- .../paris/attribute_values/ResourceId.kt | 2 +- .../airbnb/paris/attribute_values/Styles.kt | 2 +- .../airbnb/paris/proxies/ImageViewProxy.kt | 3 +- .../com/airbnb/paris/proxies/TextViewProxy.kt | 2 +- .../com/airbnb/paris/proxies/ViewProxy.kt | 27 +- .../paris/spannables/SpannableBuilder.kt | 4 +- .../airbnb/paris/spannables/StyleConverter.kt | 8 +- .../airbnb/paris/styles/AttributeSetStyle.kt | 14 +- .../com/airbnb/paris/styles/EmptyStyle.kt | 9 +- .../com/airbnb/paris/styles/MultiStyle.kt | 12 +- .../airbnb/paris/styles/ProgrammaticStyle.kt | 22 +- .../com/airbnb/paris/styles/ResourceStyle.kt | 16 +- .../java/com/airbnb/paris/styles/Style.kt | 4 +- .../EmptyTypedArrayWrapper.kt | 2 +- .../MapTypedArrayWrapper.kt | 6 +- .../MultiTypedArrayWrapper.kt | 40 +- .../airbnb/paris/utils/ResourcesExtensions.kt | 5 +- .../com/airbnb/paris/utils/ViewExtensions.kt | 2 +- .../java/com/airbnb/paris/StyleBuilderTest.kt | 8 +- .../proxies/ImageViewStyleExtensionsTest.kt | 8 +- .../paris/proxies/TextViewStyleApplierTest.kt | 11 +- .../proxies/TextViewStyleExtensionsTest.kt | 15 +- .../proxies/ViewGroupStyleExtensionsTest.kt | 8 +- .../com/airbnb/paris/proxies/ViewProxyTest.kt | 8 +- .../paris/proxies/ViewStyleApplierTest.kt | 20 +- .../paris/proxies/ViewStyleBuilderTest.kt | 45 +- .../paris/proxies/ViewStyleExtensionsTest.kt | 8 +- .../com/airbnb/paris/styles/MultiStyleTest.kt | 3 - .../paris/styles/ProgrammaticStyleTest.kt | 47 +- .../airbnb/paris/styles/ResourceStyleTest.kt | 1 - .../MapTypedArrayWrapperTest.kt | 4 +- .../java/WithStyleableChildExtensionsTest.kt | 6 +- ...eableChildKotlinViewStyleExtensionsTest.kt | 7 +- ...bleChildLateinitViewStyleExtensionsTest.kt | 7 +- ...PropertyDelegateViewStyleExtensionsTest.kt | 7 +- sample/build.gradle | 9 + .../com/airbnb/paris/sample/SectionView.kt | 25 +- 117 files changed, 2050 insertions(+), 1770 deletions(-) delete mode 100644 gradle/gradle-maven-push.gradle delete mode 100644 gradle/workaround-missing-resource.gradle delete mode 100644 paris-processor/src/main/java/com/airbnb/paris/processor/android_resource_scanner/AndroidResourceScanner.java create mode 100644 paris-processor/src/main/java/com/airbnb/paris/processor/android_resource_scanner/AndroidResourceScanner.kt create mode 100644 paris-processor/src/main/java/com/airbnb/paris/processor/framework/Memoizer.kt create mode 100644 paris-processor/src/main/java/com/airbnb/paris/processor/framework/SkyMemoizer.kt diff --git a/.idea/codeStyles/Project.xml b/.idea/codeStyles/Project.xml index 78e690c2..05e182c1 100644 --- a/.idea/codeStyles/Project.xml +++ b/.idea/codeStyles/Project.xml @@ -1,252 +1,247 @@ - - + + \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml index 6e6eec11..79ee123c 100644 --- a/.idea/codeStyles/codeStyleConfig.xml +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -1,6 +1,5 @@ \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 03b1c5ee..901999b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# 1.5.0 (May 20, 2020) + +- Adds support for incremental annotation processing + # 1.4.0 (February 14, 2020) - Added support for namespaced resources. diff --git a/README.md b/README.md index eb2f165c..7e4c8e93 100644 --- a/README.md +++ b/README.md @@ -13,9 +13,9 @@ Paris lets you define and apply styles programmatically to Android views, includ In your project's `build.gradle`: ```gradle dependencies { - implementation 'com.airbnb.android:paris:1.4.0' + implementation 'com.airbnb.android:paris:1.5.0' // If you're using Paris annotations. - kapt 'com.airbnb.android:paris-processor:1.4.0' + kapt 'com.airbnb.android:paris-processor:1.5.0' } ``` diff --git a/RELEASING.md b/RELEASING.md index b3f003fd..05abe2ba 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -10,5 +10,5 @@ Releasing 1. Tag version `vX.Y.Z` 2. Release title `vX.Y.Z` 3. Paste the content from `CHANGELOG.md` as the description -7. `./gradlew clean uploadArchives` +7. `./gradlew clean uploadArchives --no-daemon --no-parallel` 8. Visit [Sonatype Nexus](https://oss.sonatype.org/) and promote the artifact. diff --git a/blessedDeps.gradle b/blessedDeps.gradle index a0b9f292..abcc84bc 100644 --- a/blessedDeps.gradle +++ b/blessedDeps.gradle @@ -12,21 +12,24 @@ rootProject.ext { ANDROIDX_APPCOMPAT_VERSION = '1.1.0' ANDROIDX_CONSTRAINTLAYOUT_VERSION = '1.1.3' ANDROIDX_ESPRESSO_VERSION = '3.1.0' - JAVAPOET_VERSION = '1.11.1' - JUNIT_VERSION = '4.12' - KOTLINPOET_VERSION = '1.0.0' + INCAP_VERSION = "0.2" + JAVAPOET_VERSION = '1.12.1' + JUNIT_VERSION = '4.13' + KOTLINPOET_VERSION = '1.5.0' KOTLIN_TEST_VERSION = '2.0.7' - MOCKITO_VERSION = '2.23.0' - ROBOLECTRIC_VERSION = '3.8' - TESTING_COMPILE_VERSION = '0.15' + MOCKITO_VERSION = '3.3.3' + ROBOLECTRIC_VERSION = '4.3.1' + TESTING_COMPILE_VERSION = '0.18' deps = [ // Keep these alphabetized androidAnnotations: "androidx.annotation:annotation:$ANDROIDX_ANNOTATIONS_VERSION", appcompat : "androidx.appcompat:appcompat:$ANDROIDX_APPCOMPAT_VERSION", constraintLayout : "androidx.constraintlayout:constraintlayout:$ANDROIDX_CONSTRAINTLAYOUT_VERSION", - espresso : "androidx.test.espresso:espresso-core:$ANDROIDX_ESPRESSO_VERSION", javaPoet : "com.squareup:javapoet:$JAVAPOET_VERSION", + espresso : "androidx.test.espresso:espresso-core:$ANDROIDX_ESPRESSO_VERSION", + incapRuntime : "net.ltgt.gradle.incap:incap:$INCAP_VERSION", + incapProcessor : "net.ltgt.gradle.incap:incap-processor:$INCAP_VERSION", junit : "junit:junit:$JUNIT_VERSION", kotlin : "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$KOTLIN_VERSION", kotlinReflect : "org.jetbrains.kotlin:kotlin-reflect:$KOTLIN_VERSION", diff --git a/build.gradle b/build.gradle index e091948e..5a7659b7 100644 --- a/build.gradle +++ b/build.gradle @@ -2,10 +2,10 @@ apply plugin: "com.github.ben-manes.versions" buildscript { ext { - ANDROID_PLUGIN_VERSION = '3.3.0' - BUTTERKNIFE_VERSION = '10.0.0' - KOTLIN_VERSION = '1.3.50' - VERSIONS_VERSION = '0.20.0' + ANDROID_PLUGIN_VERSION = '3.6.3' + BUTTERKNIFE_VERSION = '10.2.1' + KOTLIN_VERSION = '1.3.72' + VERSIONS_VERSION = '0.28.0' } repositories { @@ -19,6 +19,7 @@ buildscript { classpath "com.jakewharton:butterknife-gradle-plugin:$BUTTERKNIFE_VERSION" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$KOTLIN_VERSION" classpath "com.github.ben-manes:gradle-versions-plugin:$VERSIONS_VERSION" + classpath 'com.vanniktech:gradle-maven-publish-plugin:0.11.1' } } diff --git a/gradle.properties b/gradle.properties index 3415ff80..2c7e88a4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ -VERSION_NAME=1.4.0 +VERSION_NAME=1.5.0 GROUP=com.airbnb.android POM_DESCRIPTION=Paris is a system for creating and applying styles to views in Android. POM_URL=https://github.com/airbnb/paris @@ -14,4 +14,8 @@ POM_DEVELOPER_EMAIL=android@airbnb.com POM_INCEPTION_YEAR=2017 android.useAndroidX=true -org.gradle.configureondemand=false +org.gradle.parallel=true +org.gradle.incremental=true +org.gradle.configureondemand=true +org.gradle.daemon=true +kotlin.incremental=true diff --git a/gradle/gradle-maven-push.gradle b/gradle/gradle-maven-push.gradle deleted file mode 100644 index bf3a94d9..00000000 --- a/gradle/gradle-maven-push.gradle +++ /dev/null @@ -1,216 +0,0 @@ -/* - * Copyright 2013 Chris Banes - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -apply plugin: 'maven' -apply plugin: 'signing' - -version = VERSION_NAME -group = GROUP - -def isReleaseBuild() { - return VERSION_NAME.contains("SNAPSHOT") == false -} - -def getReleaseRepositoryUrl() { - return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL - : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" -} - -def getSnapshotRepositoryUrl() { - return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL - : "https://oss.sonatype.org/content/repositories/snapshots/" -} - -def getRepositoryUsername() { - return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : "" -} - -def getRepositoryPassword() { - return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : "" -} - -afterEvaluate { project -> - uploadArchives { - repositories { - mavenDeployer { - beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } - - pom.groupId = GROUP - pom.artifactId = POM_ARTIFACT_ID - pom.version = VERSION_NAME - - repository(url: getReleaseRepositoryUrl()) { - authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) - } - snapshotRepository(url: getSnapshotRepositoryUrl()) { - authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) - } - - pom.project { - name POM_NAME - packaging POM_PACKAGING - description POM_DESCRIPTION - url POM_URL - - scm { - url POM_SCM_URL - connection POM_SCM_CONNECTION - developerConnection POM_SCM_DEV_CONNECTION - } - - licenses { - license { - name POM_LICENSE_NAME - url POM_LICENSE_URL - distribution POM_LICENSE_DIST - } - } - - developers { - developer { - id POM_DEVELOPER_ID - name POM_DEVELOPER_NAME - } - } - } - } - } - } - - signing { - required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } - sign configurations.archives - } - - if (project.getPlugins().hasPlugin('com.android.application') || - project.getPlugins().hasPlugin('com.android.library')) { - task install(type: Upload, dependsOn: assemble) { - repositories.mavenInstaller { - configuration = configurations.archives - - pom.groupId = GROUP - pom.artifactId = POM_ARTIFACT_ID - pom.version = VERSION_NAME - - pom.project { - name POM_NAME - packaging POM_PACKAGING - description POM_DESCRIPTION - url POM_URL - - scm { - url POM_SCM_URL - connection POM_SCM_CONNECTION - developerConnection POM_SCM_DEV_CONNECTION - } - - licenses { - license { - name POM_LICENSE_NAME - url POM_LICENSE_URL - distribution POM_LICENSE_DIST - } - } - - developers { - developer { - id POM_DEVELOPER_ID - name POM_DEVELOPER_NAME - email POM_DEVELOPER_EMAIL - } - } - } - } - } - - task androidJavadocs(type: Javadoc) { - source = android.sourceSets.main.java.source - classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) - } - - task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { - classifier = 'javadoc' - from androidJavadocs.destinationDir - } - - task androidSourcesJar(type: Jar) { - classifier = 'sources' - from android.sourceSets.main.java.source - } - } else { - install { - repositories.mavenInstaller { - pom.groupId = GROUP - pom.artifactId = POM_ARTIFACT_ID - pom.version = VERSION_NAME - - pom.project { - name POM_NAME - packaging POM_PACKAGING - description POM_DESCRIPTION - url POM_URL - - scm { - url POM_SCM_URL - connection POM_SCM_CONNECTION - developerConnection POM_SCM_DEV_CONNECTION - } - - licenses { - license { - name POM_LICENSE_NAME - url POM_LICENSE_URL - distribution POM_LICENSE_DIST - } - } - - developers { - developer { - id POM_DEVELOPER_ID - name POM_DEVELOPER_NAME - } - } - } - } - } - - task sourcesJar(type: Jar, dependsOn: classes) { - classifier = 'sources' - from sourceSets.main.allSource - } - - task javadocJar(type: Jar, dependsOn: javadoc) { - classifier = 'javadoc' - from javadoc.destinationDir - } - } - - // Javadoc isn't working well with Kotlin :( - allprojects { - tasks.withType(Javadoc).all { enabled = false } - } - - artifacts { - if (project.getPlugins().hasPlugin('com.android.application') || - project.getPlugins().hasPlugin('com.android.library')) { - archives androidSourcesJar - archives androidJavadocsJar - } else { - archives sourcesJar - archives javadocJar - } - } -} diff --git a/gradle/workaround-missing-resource.gradle b/gradle/workaround-missing-resource.gradle deleted file mode 100644 index 8f24db27..00000000 --- a/gradle/workaround-missing-resource.gradle +++ /dev/null @@ -1,23 +0,0 @@ -// Source - https://github.com/nenick/AndroidStudioAndRobolectric/blob/master/app/build.workaround-missing-resource.gradle -// Workaround for missing test resources when running unit tests within android studio. -// This copies the test resources next to the test classes for each variant. -// Tracked at https://github.com/nenick/AndroidStudioAndRobolectric/issues/7 -// Original solution comes from https://code.google.com/p/android/issues/detail?id=136013#c10 -// See also https://code.google.com/p/android/issues/detail?id=64887 -gradle.projectsEvaluated { - // Base path which is recognized by android studio. - def testClassesPath = "${buildDir}/intermediates/" - // Copy must be done for each variant. - def variants = android.libraryVariants.collect() - - variants.each { variant -> - def variationName = variant.name.capitalize() - def variationPath = variant.buildType.name - - // Specific copy task for each variant - def copyTestResourcesTask = project.tasks.create("copyTest${variationName}Resources", Copy) - copyTestResourcesTask.from("${projectDir}/src/test/resources") - copyTestResourcesTask.into("${testClassesPath}/sourceFolderJavaResources/${variationPath}") - copyTestResourcesTask.execute() - } -} diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 702465cc..1bb22b46 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.4.1-all.zip diff --git a/paris-annotations/build.gradle b/paris-annotations/build.gradle index 6a118333..7dcdedb0 100644 --- a/paris-annotations/build.gradle +++ b/paris-annotations/build.gradle @@ -1,5 +1,6 @@ apply plugin: 'java' apply plugin: 'kotlin' +apply plugin: "com.vanniktech.maven.publish" sourceCompatibility = rootProject.JAVA_SOURCE_VERSION targetCompatibility = rootProject.JAVA_TARGET_VERSION @@ -7,5 +8,3 @@ targetCompatibility = rootProject.JAVA_TARGET_VERSION dependencies { implementation deps.kotlin } - -apply from: rootProject.file('gradle/gradle-maven-push.gradle') diff --git a/paris-annotations/src/main/java/com/airbnb/paris/annotations/Fraction.kt b/paris-annotations/src/main/java/com/airbnb/paris/annotations/Fraction.kt index 2b24107f..d9708914 100644 --- a/paris-annotations/src/main/java/com/airbnb/paris/annotations/Fraction.kt +++ b/paris-annotations/src/main/java/com/airbnb/paris/annotations/Fraction.kt @@ -2,5 +2,6 @@ package com.airbnb.paris.annotations @Target(AnnotationTarget.VALUE_PARAMETER) annotation class Fraction( - val base: Int = 1, - val pbase: Int = 1) + val base: Int = 1, + val pbase: Int = 1 +) diff --git a/paris-annotations/src/main/java/com/airbnb/paris/annotations/GeneratedStyleableClass.kt b/paris-annotations/src/main/java/com/airbnb/paris/annotations/GeneratedStyleableClass.kt index d27c5dd2..3bf40a6a 100644 --- a/paris-annotations/src/main/java/com/airbnb/paris/annotations/GeneratedStyleableClass.kt +++ b/paris-annotations/src/main/java/com/airbnb/paris/annotations/GeneratedStyleableClass.kt @@ -1,6 +1,6 @@ package com.airbnb.paris.annotations -import kotlin.reflect.* +import kotlin.reflect.KClass /** * DO NOT USE. This annotation is meant to be used by generated classes only diff --git a/paris-annotations/src/main/java/com/airbnb/paris/annotations/ParisConfig.java b/paris-annotations/src/main/java/com/airbnb/paris/annotations/ParisConfig.java index 9d5ff6a6..cd555dbc 100644 --- a/paris-annotations/src/main/java/com/airbnb/paris/annotations/ParisConfig.java +++ b/paris-annotations/src/main/java/com/airbnb/paris/annotations/ParisConfig.java @@ -5,7 +5,7 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; -@Retention(RetentionPolicy.SOURCE) +@Retention(RetentionPolicy.CLASS) @Target(ElementType.PACKAGE) public @interface ParisConfig { diff --git a/paris-annotations/src/main/java/com/airbnb/paris/annotations/Style.kt b/paris-annotations/src/main/java/com/airbnb/paris/annotations/Style.kt index 30324cbb..2a79d582 100644 --- a/paris-annotations/src/main/java/com/airbnb/paris/annotations/Style.kt +++ b/paris-annotations/src/main/java/com/airbnb/paris/annotations/Style.kt @@ -1,6 +1,7 @@ package com.airbnb.paris.annotations -import kotlin.annotation.AnnotationTarget.* +import kotlin.annotation.AnnotationTarget.FIELD +import kotlin.annotation.AnnotationTarget.FUNCTION @Target(FIELD, FUNCTION) annotation class Style(val isDefault: Boolean = false) diff --git a/paris-processor/build.gradle b/paris-processor/build.gradle index dd667e5d..a93c3294 100644 --- a/paris-processor/build.gradle +++ b/paris-processor/build.gradle @@ -2,6 +2,8 @@ import org.gradle.internal.jvm.Jvm apply plugin: 'java' apply plugin: 'kotlin' +apply plugin: 'kotlin-kapt' +apply plugin: "com.vanniktech.maven.publish" sourceCompatibility = rootProject.JAVA_SOURCE_VERSION targetCompatibility = rootProject.JAVA_TARGET_VERSION @@ -12,8 +14,10 @@ dependencies { implementation deps.androidAnnotations implementation deps.kotlin - compile deps.javaPoet - compile deps.kotlinPoet + implementation deps.javaPoet + implementation deps.kotlinPoet + compileOnly deps.incapRuntime + kapt deps.incapProcessor compileOnly files(Jvm.current().getToolsJar()) @@ -21,4 +25,6 @@ dependencies { testImplementation deps.kotlinTest } -apply from: rootProject.file('gradle/gradle-maven-push.gradle') +compileKotlin { + kotlinOptions.jvmTarget = "1.8" +} diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/Format.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/Format.kt index 25bc63cb..029dec8c 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/Format.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/Format.kt @@ -113,8 +113,7 @@ internal class Format private constructor( return Format.RESOURCE_ID } - val typeString = element.asType().toString() - val formatType = when (typeString) { + val formatType = when (val typeString = element.asType().toString()) { "java.lang.Boolean", "boolean" -> Type.BOOLEAN "java.lang.CharSequence" -> Type.CHARSEQUENCE "java.lang.CharSequence[]" -> Type.CHARSEQUENCE_ARRAY @@ -124,7 +123,7 @@ internal class Format private constructor( "java.lang.Float", "float" -> Type.FLOAT "java.lang.Integer", "int" -> Type.INT "java.lang.String" -> Type.STRING - else -> throw IllegalArgumentException(String.format("Invalid type")) + else -> throw IllegalArgumentException("Invalid type: $typeString") } return Format(formatType) } @@ -210,15 +209,15 @@ internal class Format private constructor( Type.NON_RESOURCE_STRING -> "getNonResourceString(\$L)" Type.STRING -> "getString(\$L)" - // Using extension functions because unsupported by Resources + // Using extension functions because unsupported by Resources Type.LAYOUT_DIMENSION -> "\$T.getLayoutDimension(\$L, \$L)" Type.FLOAT -> "\$T.getFloat(\$L, \$L)" Type.STYLE -> "\$T.getStyle(\$L, \$L)" - // Using ResourcesCompat with context and font resource arguments + // Using ResourcesCompat with context and font resource arguments Type.FONT -> "\$T.getFont(\$L, \$L)" - // Special case, the resource id is the value + // Special case, the resource id is the value Type.RESOURCE_ID -> "\$L" } diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/ParisProcessor.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/ParisProcessor.kt index 452b748d..4529de63 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/ParisProcessor.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/ParisProcessor.kt @@ -4,21 +4,30 @@ import com.airbnb.paris.annotations.Attr import com.airbnb.paris.annotations.ParisConfig import com.airbnb.paris.annotations.Styleable import com.airbnb.paris.processor.android_resource_scanner.AndroidResourceScanner +import com.airbnb.paris.processor.framework.Memoizer import com.airbnb.paris.processor.framework.SkyProcessor -import com.airbnb.paris.processor.framework.className import com.airbnb.paris.processor.framework.packageName -import com.airbnb.paris.processor.framework.toKPoet -import com.airbnb.paris.processor.models.* +import com.airbnb.paris.processor.models.AfterStyleInfoExtractor +import com.airbnb.paris.processor.models.AttrInfoExtractor +import com.airbnb.paris.processor.models.BaseStyleableInfo +import com.airbnb.paris.processor.models.BaseStyleableInfoExtractor +import com.airbnb.paris.processor.models.BeforeStyleInfoExtractor +import com.airbnb.paris.processor.models.StyleInfoExtractor +import com.airbnb.paris.processor.models.StyleableChildInfoExtractor +import com.airbnb.paris.processor.models.StyleableInfo +import com.airbnb.paris.processor.models.StyleableInfoExtractor import com.airbnb.paris.processor.writers.ModuleJavaClass import com.airbnb.paris.processor.writers.ParisJavaClass import com.airbnb.paris.processor.writers.StyleApplierJavaClass import com.airbnb.paris.processor.writers.StyleExtensionsKotlinFile +import net.ltgt.gradle.incap.IncrementalAnnotationProcessor +import net.ltgt.gradle.incap.IncrementalAnnotationProcessorType import javax.annotation.processing.ProcessingEnvironment import javax.annotation.processing.RoundEnvironment import javax.lang.model.SourceVersion import javax.lang.model.element.TypeElement - +@IncrementalAnnotationProcessor(IncrementalAnnotationProcessorType.AGGREGATING) class ParisProcessor : SkyProcessor(), WithParisProcessor { override val processor = this @@ -31,17 +40,19 @@ class ParisProcessor : SkyProcessor(), WithParisProcessor { override var namespacedResourcesEnabled: Boolean = false - private var beforeStyleInfoExtractor = BeforeStyleInfoExtractor(this) + override val memoizer = Memoizer(this) + + private val beforeStyleInfoExtractor = BeforeStyleInfoExtractor(this) - private var afterStyleInfoExtractor = AfterStyleInfoExtractor(this) + private val afterStyleInfoExtractor = AfterStyleInfoExtractor(this) - private var styleableChildInfoExtractor = StyleableChildInfoExtractor(this) + private val styleableChildInfoExtractor = StyleableChildInfoExtractor(this) - private var attrInfoExtractor = AttrInfoExtractor(this) + private val attrInfoExtractor = AttrInfoExtractor(this) - private var styleInfoExtractor = StyleInfoExtractor(this) + private val styleInfoExtractor = StyleInfoExtractor(this) - private var styleableInfoExtractor = StyleableInfoExtractor(this) + private val styleableInfoExtractor = StyleableInfoExtractor(this) private lateinit var externalStyleablesInfo: List diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/RFinder.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/RFinder.kt index 0f3877d3..dfd484ac 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/RFinder.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/RFinder.kt @@ -11,6 +11,7 @@ import javax.lang.model.type.TypeMirror internal class RFinder(override val processor: ParisProcessor) : WithParisProcessor { var element: TypeElement? = null + private set fun processConfig(config: ParisConfig) { if (element != null) { @@ -43,20 +44,20 @@ internal class RFinder(override val processor: ParisProcessor) : WithParisProces } fun processStyleables(styleablesInfo: List) { - if (element == null && styleablesInfo.isNotEmpty()) { - styleablesInfo[0].let { styleableInfo -> - var packageName = styleableInfo.elementPackageName - while (packageName.isNotBlank()) { - elements.getTypeElement("$packageName.R")?.let { - element = it - return - } - val lastIndexOfDot = packageName.lastIndexOf('.') - packageName = if (lastIndexOfDot > 0) { - packageName.substring(0, lastIndexOfDot) - } else { - "" - } + if (element != null || styleablesInfo.isEmpty()) return + + styleablesInfo[0].let { styleableInfo -> + var packageName = styleableInfo.elementPackageName + while (packageName.isNotBlank()) { + elements.getTypeElement("$packageName.R")?.let { + element = it + return + } + val lastIndexOfDot = packageName.lastIndexOf('.') + packageName = if (lastIndexOfDot > 0) { + packageName.substring(0, lastIndexOfDot) + } else { + "" } } } diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/StyleablesTree.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/StyleablesTree.kt index 9913758d..4dff7691 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/StyleablesTree.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/StyleablesTree.kt @@ -2,6 +2,8 @@ package com.airbnb.paris.processor import com.airbnb.paris.processor.models.BaseStyleableInfo import com.squareup.javapoet.ClassName +import javax.lang.model.element.Element +import javax.lang.model.element.Name import javax.lang.model.element.TypeElement internal class StyleablesTree( @@ -9,32 +11,33 @@ internal class StyleablesTree( private val styleablesInfo: List ) : WithParisProcessor { - // This is a map of the View class qualified name to the StyleApplier ClassName + // This is a map of the View class qualified name to the StyleApplier class details // eg. "android.view.View" -> "com.airbnb.paris.ViewStyleApplier".className() - private val viewQualifiedNameToStyleApplierClassName = mutableMapOf() + private val viewQualifiedNameToStyleApplierClassName = mutableMapOf() /** * Traverses the class hierarchy of the given View type to find and return the first * corresponding style applier */ - internal fun findStyleApplier(viewTypeElement: TypeElement): ClassName { - var styleApplierClassName = viewQualifiedNameToStyleApplierClassName[viewTypeElement.qualifiedName.toString()] - if (styleApplierClassName != null) { - return styleApplierClassName - } + internal fun findStyleApplier(viewTypeElement: TypeElement): StyleApplierDetails { + return viewQualifiedNameToStyleApplierClassName.getOrPut(viewTypeElement.qualifiedName) { - val type = viewTypeElement.asType() - // Check to see if the view type is handled by a styleable class - val styleableInfo = styleablesInfo.find { isSameType(type, it.viewElementType) } - if (styleableInfo != null) { - styleApplierClassName = styleableInfo.styleApplierClassName - } else { - styleApplierClassName = findStyleApplier(viewTypeElement.superclass.asTypeElement()) + val type = viewTypeElement.asType() + // Check to see if the view type is handled by a styleable class + val styleableInfo = styleablesInfo.find { isSameType(type, it.viewElementType) } + if (styleableInfo != null) { + StyleApplierDetails( + annotatedElement = styleableInfo.annotatedElement, + className = styleableInfo.styleApplierClassName + ) + } else { + findStyleApplier(viewTypeElement.superclass.asTypeElement()) + } } - - viewQualifiedNameToStyleApplierClassName.put( - viewTypeElement.qualifiedName.toString(), - styleApplierClassName) - return styleApplierClassName } } + +data class StyleApplierDetails( + val annotatedElement: Element, + val className: ClassName +) diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/WithParisProcessor.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/WithParisProcessor.kt index 4b7d5a40..be505a21 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/WithParisProcessor.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/WithParisProcessor.kt @@ -14,6 +14,7 @@ internal interface WithParisProcessor : WithSkyProcessor { val namespacedResourcesEnabled get() = processor.namespacedResourcesEnabled + fun getResourceId(annotation: Class, element: Element, value: Int): AndroidResourceId? { val resourceId = processor.resourceScanner.getId(annotation, element, value) if (resourceId == null) { diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/android_resource_scanner/AndroidResourceId.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/android_resource_scanner/AndroidResourceId.kt index 3e5bb81d..2f594b27 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/android_resource_scanner/AndroidResourceId.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/android_resource_scanner/AndroidResourceId.kt @@ -6,7 +6,7 @@ import com.airbnb.paris.processor.framework.KotlinCodeBlock import com.airbnb.paris.processor.framework.toKPoet import com.squareup.javapoet.ClassName -internal class AndroidResourceId(val value: Int, val className: ClassName, val resourceName: String) { +class AndroidResourceId(val value: Int, val className: ClassName, val resourceName: String) { val rClassName: ClassName = className.topLevelClassName() diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/android_resource_scanner/AndroidResourceScanner.java b/paris-processor/src/main/java/com/airbnb/paris/processor/android_resource_scanner/AndroidResourceScanner.java deleted file mode 100644 index 9a47fc8a..00000000 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/android_resource_scanner/AndroidResourceScanner.java +++ /dev/null @@ -1,115 +0,0 @@ -package com.airbnb.paris.processor.android_resource_scanner; - -import com.squareup.javapoet.ClassName; -import com.sun.source.util.Trees; -import com.sun.tools.javac.code.Symbol; -import com.sun.tools.javac.tree.JCTree; -import com.sun.tools.javac.tree.TreeScanner; - -import java.lang.annotation.Annotation; -import java.util.HashMap; -import java.util.Map; - -import javax.annotation.processing.ProcessingEnvironment; -import javax.lang.model.element.AnnotationMirror; -import javax.lang.model.element.Element; -import javax.lang.model.type.MirroredTypeException; -import javax.lang.model.util.Elements; -import javax.lang.model.util.Types; - -public class AndroidResourceScanner { - - private Types typeUtils; - private Elements elementUtils; - private Trees trees; - - public void init(ProcessingEnvironment processingEnv) { - typeUtils = processingEnv.getTypeUtils(); - elementUtils = processingEnv.getElementUtils(); - try { - trees = Trees.instance(processingEnv); - } catch (IllegalArgumentException ignored) { - } - } - - /** - * Returns the {@link AndroidResourceId} that is used as an annotation value of the given {@link Element} - */ - public AndroidResourceId getId(Class annotation, Element element, int value) { - Map results = getResults(annotation, element); - if (results.containsKey(value)) { - return results.get(value); - } else { - return null; - } - } - - private static AnnotationMirror getMirror(Element element, - Class annotation) { - for (AnnotationMirror annotationMirror : element.getAnnotationMirrors()) { - if (annotationMirror.getAnnotationType().toString().equals(annotation.getCanonicalName())) { - return annotationMirror; - } - } - return null; - } - - private Map getResults(Class annotation, Element element) { - AnnotationScanner scanner = new AnnotationScanner(); - // TODO I suspect the annotation mirror isn't needed here because StyleableChildInfo was using the wrong annotation class for a long time - JCTree tree = (JCTree) trees.getTree(element, getMirror(element, annotation)); - if (tree != null) { // tree can be null if the references are compiled types and not source - tree.accept(scanner); - } - return scanner.results(); - } - - private class AnnotationScanner extends TreeScanner { - - private final Map results = new HashMap<>(); - - @Override public void visitSelect(JCTree.JCFieldAccess jcFieldAccess) { - Symbol symbol = jcFieldAccess.sym; - if (symbol instanceof Symbol.VarSymbol - && symbol.getEnclosingElement() != null - && symbol.getEnclosingElement().getEnclosingElement() != null - && symbol.getEnclosingElement().getEnclosingElement().enclClass() != null) { - parseResourceSymbol((Symbol.VarSymbol) symbol); - } - } - - private void parseResourceSymbol(Symbol.VarSymbol symbol) { - // eg com.airbnb.paris.R - String rClass = symbol.getEnclosingElement().getEnclosingElement().enclClass().className(); - // eg styleable - String rTypeClass = symbol.getEnclosingElement().getSimpleName().toString(); - // eg View_background - String resourceName = symbol.getSimpleName().toString(); - - Object value = symbol.getConstantValue(); - if (!(value instanceof Integer)) { - return; - } - - AndroidResourceId androidResourceId = new AndroidResourceId((int) value, getClassName(rClass, rTypeClass), resourceName); - results.put(androidResourceId.getValue(), androidResourceId); - } - - Map results() { - return results; - } - } - - private ClassName getClassName(String rClass, String rTypeClass) { - Element rClassElement; - try { - rClassElement = elementUtils.getTypeElement(rClass); - } catch (MirroredTypeException mte) { - rClassElement = typeUtils.asElement(mte.getTypeMirror()); - } - - String rClassPackageName = - elementUtils.getPackageOf(rClassElement).getQualifiedName().toString(); - return ClassName.get(rClassPackageName, "R", rTypeClass); - } -} diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/android_resource_scanner/AndroidResourceScanner.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/android_resource_scanner/AndroidResourceScanner.kt new file mode 100644 index 00000000..04d1c33a --- /dev/null +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/android_resource_scanner/AndroidResourceScanner.kt @@ -0,0 +1,122 @@ +package com.airbnb.paris.processor.android_resource_scanner + +import com.squareup.javapoet.ClassName +import com.sun.source.util.Trees +import com.sun.tools.javac.code.Symbol.VarSymbol +import com.sun.tools.javac.tree.JCTree +import com.sun.tools.javac.tree.JCTree.JCFieldAccess +import com.sun.tools.javac.tree.TreeScanner +import java.util.HashMap +import javax.annotation.processing.ProcessingEnvironment +import javax.lang.model.element.AnnotationMirror +import javax.lang.model.element.Element +import javax.lang.model.type.MirroredTypeException +import javax.lang.model.util.Elements +import javax.lang.model.util.Types + +class AndroidResourceScanner { + private lateinit var typeUtils: Types + private lateinit var elementUtils: Elements + private var trees: Trees? = null + + fun init(processingEnv: ProcessingEnvironment) { + typeUtils = processingEnv.typeUtils + elementUtils = processingEnv.elementUtils + + trees = try { + Trees.instance(processingEnv) + } catch (ignored: IllegalArgumentException) { + try { + // Get original ProcessingEnvironment from Gradle-wrapped one or KAPT-wrapped one. + // In Kapt, its field is called "delegate". In Gradle's, it's called "processingEnv" + processingEnv.javaClass.declaredFields.mapNotNull { field -> + if (field.name == "delegate" || field.name == "processingEnv") { + field.isAccessible = true + val javacEnv = field[processingEnv] as ProcessingEnvironment + Trees.instance(javacEnv) + } else { + null + } + }.firstOrNull() + } catch (ignored2: Throwable) { + null + } + } + } + + /** + * Returns the [AndroidResourceId] that is used as an annotation value of the given [Element] + */ + fun getId( + annotation: Class, + element: Element, + value: Int + ): AndroidResourceId? { + val results = getResults(annotation, element) + return if (results.containsKey(value)) { + results[value] + } else { + null + } + } + + private fun getResults( + annotation: Class, + element: Element + ): Map { + val scanner = AnnotationScanner() + // TODO I suspect the annotation mirror isn't needed here because StyleableChildInfo was using the wrong annotation class for a long time + val tree = trees?.getTree( + element, + getMirror(element, annotation) + ) as JCTree? + tree?.accept(scanner) + return scanner.results() + } + + private inner class AnnotationScanner : TreeScanner() { + private val results: MutableMap = HashMap() + + override fun visitSelect(jcFieldAccess: JCFieldAccess) { + val symbol = jcFieldAccess.sym + if ((symbol as? VarSymbol)?.enclosingElement?.enclosingElement?.enclClass() != null) { + parseResourceSymbol(symbol) + } + } + + private fun parseResourceSymbol(symbol: VarSymbol) { + // eg com.airbnb.paris.R + val rClass = symbol.enclosingElement.enclosingElement.enclClass().className() + // eg styleable + val rTypeClass = symbol.enclosingElement.simpleName.toString() + // eg View_background + val resourceName = symbol.simpleName.toString() + val value = symbol.constantValue as? Int ?: return + val androidResourceId = AndroidResourceId(value, getClassName(rClass, rTypeClass), resourceName) + results[androidResourceId.value] = androidResourceId + } + + fun results(): Map = results + } + + private fun getClassName(rClass: String, rTypeClass: String): ClassName { + val rClassElement: Element? + rClassElement = try { + elementUtils.getTypeElement(rClass) + } catch (mte: MirroredTypeException) { + typeUtils.asElement(mte.typeMirror) + } + val rClassPackageName = elementUtils.getPackageOf(rClassElement).qualifiedName.toString() + return ClassName.get(rClassPackageName, "R", rTypeClass) + } + + companion object { + private fun getMirror( + element: Element, + annotation: Class + ): AnnotationMirror? { + val targetName = annotation.canonicalName + return element.annotationMirrors.firstOrNull { it.annotationType.toString() == targetName } + } + } +} \ No newline at end of file diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/framework/KotlinPoetConverters.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/KotlinPoetConverters.kt index a44871ce..7c87d6ea 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/framework/KotlinPoetConverters.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/KotlinPoetConverters.kt @@ -4,9 +4,19 @@ package com.airbnb.paris.processor.framework import com.squareup.javapoet.TypeName -import com.squareup.kotlinpoet.* -import javax.lang.model.element.Modifier +import com.squareup.kotlinpoet.ANY +import com.squareup.kotlinpoet.BOOLEAN +import com.squareup.kotlinpoet.BYTE +import com.squareup.kotlinpoet.CHAR +import com.squareup.kotlinpoet.DOUBLE +import com.squareup.kotlinpoet.FLOAT +import com.squareup.kotlinpoet.INT +import com.squareup.kotlinpoet.KModifier +import com.squareup.kotlinpoet.LONG import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy +import com.squareup.kotlinpoet.SHORT +import com.squareup.kotlinpoet.UNIT +import javax.lang.model.element.Modifier internal typealias JavaClassName = com.squareup.javapoet.ClassName internal typealias JavaTypeName = com.squareup.javapoet.TypeName diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/framework/KotlinPoetExtensions.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/KotlinPoetExtensions.kt index 97debc49..238c109d 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/framework/KotlinPoetExtensions.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/KotlinPoetExtensions.kt @@ -1,6 +1,10 @@ package com.airbnb.paris.processor.framework -import com.squareup.kotlinpoet.* +import com.squareup.kotlinpoet.FileSpec +import com.squareup.kotlinpoet.FunSpec +import com.squareup.kotlinpoet.ParameterSpec +import com.squareup.kotlinpoet.TypeName +import com.squareup.kotlinpoet.asTypeName import java.lang.reflect.Type import javax.lang.model.type.TypeMirror import kotlin.reflect.KClass @@ -50,9 +54,9 @@ internal inline fun FunSpec.Builder.addParameter( } internal fun FunSpec.Builder.parameter( - name: String, - type: Type, - block: ParameterSpec.Builder.() -> Unit = {} + name: String, + type: Type, + block: ParameterSpec.Builder.() -> Unit = {} ): ParameterSpec { return ParameterSpec.builder(name, type).apply(block).build().also { addParameter(it) @@ -60,9 +64,9 @@ internal fun FunSpec.Builder.parameter( } internal fun FunSpec.Builder.parameter( - name: String, - type: KotlinTypeName, - block: ParameterSpec.Builder.() -> Unit = {} + name: String, + type: KotlinTypeName, + block: ParameterSpec.Builder.() -> Unit = {} ): ParameterSpec { return ParameterSpec.builder(name, type).apply(block).build().also { addParameter(it) diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/framework/Memoizer.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/Memoizer.kt new file mode 100644 index 00000000..84580e66 --- /dev/null +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/Memoizer.kt @@ -0,0 +1,18 @@ +package com.airbnb.paris.processor.framework + +import com.airbnb.paris.processor.PROXY_CLASS_NAME +import com.airbnb.paris.processor.ParisProcessor +import com.airbnb.paris.processor.STYLE_CLASS_NAME +import javax.lang.model.element.TypeElement +import javax.lang.model.type.TypeMirror + +class Memoizer(override val processor: ParisProcessor) : SkyMemoizer(processor) { + + val proxyClassTypeErased: TypeMirror by lazy { erasure(PROXY_CLASS_NAME.toTypeMirror()) } + + val styleClassType: TypeMirror by lazy { STYLE_CLASS_NAME.toTypeMirror() } + + val rStyleTypeElement: TypeElement? by lazy { + val rElement = processor.RElement ?: error("R Class not found") + elements.getTypeElement("${rElement.qualifiedName}.style") } +} \ No newline at end of file diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/framework/SkyExtensions.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/SkyExtensions.kt index 2754c64c..f4990a11 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/framework/SkyExtensions.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/SkyExtensions.kt @@ -1,7 +1,13 @@ package com.airbnb.paris.processor.framework import com.squareup.javapoet.ClassName -import javax.lang.model.element.* +import javax.lang.model.element.Element +import javax.lang.model.element.ElementKind +import javax.lang.model.element.ExecutableElement +import javax.lang.model.element.Modifier +import javax.lang.model.element.PackageElement +import javax.lang.model.element.TypeElement +import javax.lang.model.element.VariableElement // Element diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/framework/SkyJavaClass.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/SkyJavaClass.kt index c0077dcf..dafbc353 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/framework/SkyJavaClass.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/SkyJavaClass.kt @@ -2,15 +2,20 @@ package com.airbnb.paris.processor.framework import com.squareup.javapoet.JavaFile import com.squareup.javapoet.TypeSpec +import javax.lang.model.element.Element internal abstract class SkyJavaClass(override val processor: SkyProcessor) : WithSkyProcessor { protected abstract val packageName: String protected abstract val name: String protected abstract val block: TypeSpec.Builder.() -> Unit + protected abstract val originatingElements: List fun build(): TypeSpec { val builder = TypeSpec.classBuilder(name) + originatingElements.forEach { + builder.addOriginatingElement(it) + } builder.block() return builder.build() } diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/framework/SkyKotlinFile.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/SkyKotlinFile.kt index f3842562..788ddce8 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/framework/SkyKotlinFile.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/SkyKotlinFile.kt @@ -1,7 +1,6 @@ package com.airbnb.paris.processor.framework import com.squareup.kotlinpoet.FileSpec -import java.io.File internal abstract class SkyKotlinFile(override val processor: SkyProcessor) : WithSkyProcessor { @@ -21,8 +20,6 @@ internal abstract class SkyKotlinFile(override val processor: SkyProcessor) : Wi * If this module is being processed with kapt then the file is written, otherwise this is a no-op. */ fun write() { - kaptOutputPath?.let { - build().writeTo(File(it)) - } + build().writeTo(processor.filer) } } diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/framework/SkyMemoizer.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/SkyMemoizer.kt new file mode 100644 index 00000000..01867e73 --- /dev/null +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/SkyMemoizer.kt @@ -0,0 +1,9 @@ +package com.airbnb.paris.processor.framework + +import javax.lang.model.type.TypeMirror + +open class SkyMemoizer(withSkyProcessor: WithSkyProcessor) : WithSkyProcessor by withSkyProcessor { + + val androidViewClassType: TypeMirror by lazy { AndroidClassNames.VIEW.toTypeMirror() } + +} \ No newline at end of file diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/framework/SkyProcessor.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/SkyProcessor.kt index 61d524e6..0af147e9 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/framework/SkyProcessor.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/SkyProcessor.kt @@ -15,17 +15,7 @@ abstract class SkyProcessor : AbstractProcessor(), WithSkyProcessor { override val elements: Elements by lazy { processingEnv.elementUtils } override val types: Types by lazy { processingEnv.typeUtils } - /** - * The directory name where kapt output files should be placed. - * - * If null, this is not being processed by kapt, so we can't generate kotlin code. - */ - override val kaptOutputPath: String? by lazy { - processingEnv.options[KAPT_KOTLIN_GENERATED_OPTION_NAME] - // Need to change the path because of https://youtrack.jetbrains.com/issue/KT-19097 - ?.replace("kaptKotlin", "kapt") - } - + override val memoizer: SkyMemoizer by lazy { SkyMemoizer(this) } override val loggedMessages = mutableListOf() override fun process(annotations: Set, roundEnv: RoundEnvironment): Boolean { @@ -47,10 +37,3 @@ abstract class SkyProcessor : AbstractProcessor(), WithSkyProcessor { abstract fun processingOver() } - -/** - * This option will be present when processed by kapt, and it tells us where to put our - * generated kotlin files - * https://github.com/JetBrains/kotlin-examples/blob/master/gradle/kotlin-code-generation/annotation-processor/src/main/java/TestAnnotationProcessor.kt - */ -private const val KAPT_KOTLIN_GENERATED_OPTION_NAME = "kapt.kotlin.generated" diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/framework/WithSkyProcessor.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/WithSkyProcessor.kt index 85d4f53f..4a5f28d8 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/framework/WithSkyProcessor.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/WithSkyProcessor.kt @@ -21,8 +21,8 @@ interface WithSkyProcessor { val messager get() = processor.messager val elements get() = processor.elements val types get() = processor.types - val kaptOutputPath get() = processor.kaptOutputPath val loggedMessages get() = processor.loggedMessages + val memoizer get() = processor.memoizer fun erasure(type: TypeMirror): TypeMirror = types.erasure(type) @@ -60,7 +60,7 @@ interface WithSkyProcessor { // Android specific - fun isView(type: TypeMirror): Boolean = isSubtype(type, AndroidClassNames.VIEW.toTypeMirror()) + fun isView(type: TypeMirror): Boolean = isSubtype(type, processor.memoizer.androidViewClassType) // Error handling diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/framework/models/SkyCompanionPropertyModel.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/models/SkyCompanionPropertyModel.kt index ff9291b4..346772e4 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/framework/models/SkyCompanionPropertyModel.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/models/SkyCompanionPropertyModel.kt @@ -1,7 +1,15 @@ package com.airbnb.paris.processor.framework.models -import com.airbnb.paris.processor.framework.* -import javax.lang.model.element.* +import com.airbnb.paris.processor.framework.JavaCodeBlock +import com.airbnb.paris.processor.framework.KotlinCodeBlock +import com.airbnb.paris.processor.framework.SkyProcessor +import com.airbnb.paris.processor.framework.isJava +import com.airbnb.paris.processor.framework.siblings +import javax.lang.model.element.Element +import javax.lang.model.element.ElementKind +import javax.lang.model.element.ExecutableElement +import javax.lang.model.element.TypeElement +import javax.lang.model.element.VariableElement import javax.lang.model.type.TypeMirror /** diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/framework/models/SkyModel.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/models/SkyModel.kt index 7029737b..6efc24ca 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/framework/models/SkyModel.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/models/SkyModel.kt @@ -20,10 +20,9 @@ abstract class SkyModelFactory( fun process(roundEnv: RoundEnvironment) { roundEnv.getElementsAnnotatedWith(annotationClass) - .filter { filter(it) } .mapNotNull { @Suppress("UNCHECKED_CAST") - elementToModel(it as E) + if (filter(it)) elementToModel(it as E) else null } .let { models += it diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/framework/models/SkyPropertyModel.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/models/SkyPropertyModel.kt index 7633e3f2..a6f76e51 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/framework/models/SkyPropertyModel.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/framework/models/SkyPropertyModel.kt @@ -1,6 +1,10 @@ package com.airbnb.paris.processor.framework.models -import com.airbnb.paris.processor.framework.* +import com.airbnb.paris.processor.framework.SkyProcessor +import com.airbnb.paris.processor.framework.isJava +import com.airbnb.paris.processor.framework.isKotlin +import com.airbnb.paris.processor.framework.siblings +import com.airbnb.paris.processor.framework.toStringId import javax.lang.model.element.Element import javax.lang.model.element.ExecutableElement import javax.lang.model.element.TypeElement @@ -47,7 +51,8 @@ abstract class SkyPropertyModel(val element: Element) : SkyModel { } .singleOrNull() as ExecutableElement? - kotlinGetterElement ?: throw IllegalArgumentException("${element.toStringId()}: Could not find getter for property annotated with @StyleableChild. This probably means the property is private or protected.") + kotlinGetterElement + ?: throw IllegalArgumentException("${element.toStringId()}: Could not find getter for property annotated with @StyleableChild. This probably means the property is private or protected.") getterElement = kotlinGetterElement getter = "${kotlinGetterElement.simpleName}()" diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/models/AfterStyleInfo.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/models/AfterStyleInfo.kt index fbde20c0..64db985d 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/models/AfterStyleInfo.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/models/AfterStyleInfo.kt @@ -9,8 +9,7 @@ import com.airbnb.paris.processor.framework.models.SkyMethodModel import com.airbnb.paris.processor.framework.models.SkyMethodModelFactory import javax.lang.model.element.ExecutableElement -internal class AfterStyleInfoExtractor(processor: ParisProcessor) - : SkyMethodModelFactory(processor, AfterStyle::class.java) { +internal class AfterStyleInfoExtractor(override val processor: ParisProcessor) : SkyMethodModelFactory(processor, AfterStyle::class.java) { override fun elementToModel(element: ExecutableElement): AfterStyleInfo? { if (element.isPrivate() || element.isProtected()) { @@ -20,10 +19,9 @@ internal class AfterStyleInfoExtractor(processor: ParisProcessor) return null } - val styleType = STYLE_CLASS_NAME.toTypeMirror() - val parameterType = element.parameters[0].asType() + val parameterType = element.parameters.firstOrNull()?.asType() - if (element.parameters.size != 1 || !isSameType(styleType, parameterType)) { + if (parameterType == null || !isSameType(processor.memoizer.styleClassType, parameterType)) { logError(element) { "Methods annotated with @AfterStyle must have a single Style parameter." } diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/models/AttrInfo.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/models/AttrInfo.kt index c2e78068..9a211330 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/models/AttrInfo.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/models/AttrInfo.kt @@ -31,7 +31,12 @@ internal class AttrInfoExtractor( val attr = element.getAnnotation(Attr::class.java) - val targetType = element.parameters[0].asType() + val targetType = element.parameters.firstOrNull()?.asType() ?: run { + logError(element) { + "Method with @Attr must provide a single parameter" + } + return null + } val targetFormat = Format.forElement(processor, element) diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/models/BaseStyleableInfo.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/models/BaseStyleableInfo.kt index cbb4312d..7de52c14 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/models/BaseStyleableInfo.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/models/BaseStyleableInfo.kt @@ -9,6 +9,7 @@ import com.airbnb.paris.processor.STYLE_APPLIER_SIMPLE_CLASS_NAME_FORMAT import com.airbnb.paris.processor.framework.WithSkyProcessor import com.airbnb.paris.processor.framework.packageName import com.squareup.javapoet.ClassName +import javax.lang.model.element.Element import javax.lang.model.element.TypeElement import javax.lang.model.type.DeclaredType import javax.lang.model.type.MirroredTypeException @@ -28,16 +29,18 @@ internal class BaseStyleableInfoExtractor(override val processor: ParisProcessor .forEach { styleableModule -> baseStyleablesInfo.addAll( styleableModule.value - .mapNotNull { + .mapNotNull { generatedStyleableClass -> var typeElement: TypeElement? = null try { - it.value + generatedStyleableClass.value } catch (e: MirroredTypeException) { typeElement = e.typeMirror.asTypeElement() } typeElement } - .map { BaseStyleableInfoExtractor(processor).fromElement(it) } + .map { typeElement -> + BaseStyleableInfoExtractor(processor).fromElement(typeElement) + } ) } } @@ -50,7 +53,7 @@ internal class BaseStyleableInfoExtractor(override val processor: ParisProcessor val elementType = element.asType() val viewElementType: TypeMirror - viewElementType = if (isSubtype(elementType, erasure(PROXY_CLASS_NAME.toTypeMirror()))) { + viewElementType = if (isSubtype(elementType, processor.memoizer.proxyClassTypeErased)) { // Get the parameterized type, which should be the view type (element.superclass as DeclaredType).typeArguments[1] } else { @@ -65,19 +68,25 @@ internal class BaseStyleableInfoExtractor(override val processor: ParisProcessor val styleableResourceName = styleable.value return BaseStyleableInfo( - elementPackageName, - elementName, - elementType, - viewElementPackageName, - viewElement, - viewElementName, - viewElementType, - styleableResourceName + annotatedElement = element, + elementPackageName = elementPackageName, + elementName = elementName, + elementType = elementType, + viewElementPackageName = viewElementPackageName, + viewElement = viewElement, + viewElementName = viewElementName, + viewElementType = viewElementType, + styleableResourceName = styleableResourceName ) } } internal open class BaseStyleableInfo( + /** + * The element that is annotated with @Styleable. + * This is used to determine the originating element of generated files. + */ + val annotatedElement: Element, val elementPackageName: String, val elementName: String, /** @@ -86,8 +95,8 @@ internal open class BaseStyleableInfo( */ val elementType: TypeMirror, private val viewElementPackageName: String, - /** The simple name of the view eg. "AirImageView" */ val viewElement: TypeElement, + /** The simple name of the view eg. "AirImageView" */ val viewElementName: String, /** * If the styleable class is not a proxy, will be equal to [elementType]. Refers to the view @@ -98,14 +107,15 @@ internal open class BaseStyleableInfo( ) { constructor(baseStyleableInfo: BaseStyleableInfo) : this( - baseStyleableInfo.elementPackageName, - baseStyleableInfo.elementName, - baseStyleableInfo.elementType, - baseStyleableInfo.viewElementPackageName, - baseStyleableInfo.viewElement, - baseStyleableInfo.viewElementName, - baseStyleableInfo.viewElementType, - baseStyleableInfo.styleableResourceName + annotatedElement = baseStyleableInfo.annotatedElement, + elementPackageName = baseStyleableInfo.elementPackageName, + elementName = baseStyleableInfo.elementName, + elementType = baseStyleableInfo.elementType, + viewElementPackageName = baseStyleableInfo.viewElementPackageName, + viewElement = baseStyleableInfo.viewElement, + viewElementName = baseStyleableInfo.viewElementName, + viewElementType = baseStyleableInfo.viewElementType, + styleableResourceName = baseStyleableInfo.styleableResourceName ) val styleApplierClassName: ClassName = ClassName.get( diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/models/BeforeStyleInfo.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/models/BeforeStyleInfo.kt index 2c911500..bb991545 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/models/BeforeStyleInfo.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/models/BeforeStyleInfo.kt @@ -9,8 +9,7 @@ import com.airbnb.paris.processor.framework.models.SkyMethodModel import com.airbnb.paris.processor.framework.models.SkyMethodModelFactory import javax.lang.model.element.ExecutableElement -internal class BeforeStyleInfoExtractor(processor: ParisProcessor) - : SkyMethodModelFactory(processor, BeforeStyle::class.java) { +internal class BeforeStyleInfoExtractor(override val processor: ParisProcessor) : SkyMethodModelFactory(processor, BeforeStyle::class.java) { override fun elementToModel(element: ExecutableElement): BeforeStyleInfo? { if (element.isPrivate() || element.isProtected()) { @@ -20,9 +19,9 @@ internal class BeforeStyleInfoExtractor(processor: ParisProcessor) return null } - val styleType = STYLE_CLASS_NAME.toTypeMirror() - val parameterType = element.parameters[0].asType() - if (element.parameters.size != 1 || !isSameType(styleType, parameterType)) { + + val parameterType = element.parameters.firstOrNull()?.asType() + if (parameterType == null || !isSameType(processor.memoizer.styleClassType, parameterType)) { logError(element) { "Methods annotated with @BeforeStyle must have a single Style parameter." } diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/models/StyleCompanionPropertyInfo.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/models/StyleCompanionPropertyInfo.kt index d0a8501d..2a65f02e 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/models/StyleCompanionPropertyInfo.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/models/StyleCompanionPropertyInfo.kt @@ -3,15 +3,20 @@ package com.airbnb.paris.processor.models import com.airbnb.paris.annotations.Style import com.airbnb.paris.processor.ParisProcessor import com.airbnb.paris.processor.STYLE_CLASS_NAME -import com.airbnb.paris.processor.framework.* +import com.airbnb.paris.processor.framework.JavaCodeBlock +import com.airbnb.paris.processor.framework.KotlinCodeBlock +import com.airbnb.paris.processor.framework.isNotFinal +import com.airbnb.paris.processor.framework.isNotStatic +import com.airbnb.paris.processor.framework.isPrivate +import com.airbnb.paris.processor.framework.isProtected import com.airbnb.paris.processor.framework.models.SkyCompanionPropertyModel import com.airbnb.paris.processor.framework.models.SkyCompanionPropertyModelFactory import com.airbnb.paris.processor.utils.ParisProcessorUtils import javax.lang.model.element.VariableElement import javax.lang.model.type.TypeKind -internal class StyleCompanionPropertyInfoExtractor(processor: ParisProcessor) - : SkyCompanionPropertyModelFactory(processor, Style::class.java) { +internal class StyleCompanionPropertyInfoExtractor(override val processor: ParisProcessor) : + SkyCompanionPropertyModelFactory(processor, Style::class.java) { override fun elementToModel(element: VariableElement): StyleCompanionPropertyInfo? { // TODO Get Javadoc from field/method and add it to the generated methods @@ -31,7 +36,7 @@ internal class StyleCompanionPropertyInfoExtractor(processor: ParisProcessor) } val type = element.asType() - if (!isSubtype(type, STYLE_CLASS_NAME.toTypeMirror()) && type.kind != TypeKind.INT && !type.isNonExistent()) { + if (type.kind != TypeKind.INT && !isSubtype(type, processor.memoizer.styleClassType) && !type.isNonExistent()) { // Note: if the type is non existent we ignore this error check so that users don't need to change their kapt configuration, they'll still // get a build error though not as explicit. logError(element) { diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/models/StyleInfo.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/models/StyleInfo.kt index 5c255578..45b2d757 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/models/StyleInfo.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/models/StyleInfo.kt @@ -5,7 +5,7 @@ import com.airbnb.paris.processor.ParisProcessor import com.airbnb.paris.processor.WithParisProcessor import com.airbnb.paris.processor.framework.JavaCodeBlock import com.airbnb.paris.processor.framework.KotlinCodeBlock -import java.util.* +import java.util.Locale import javax.annotation.processing.RoundEnvironment import javax.lang.model.element.Element @@ -19,8 +19,8 @@ internal class StyleInfoExtractor(override val processor: ParisProcessor) : With var latest = emptyList() private set - private var styleCompanionPropertyInfoExtractor = StyleCompanionPropertyInfoExtractor(processor) - private var styleStaticMethodInfoExtractor = StyleStaticMethodInfoExtractor(processor) + private val styleCompanionPropertyInfoExtractor = StyleCompanionPropertyInfoExtractor(processor) + private val styleStaticMethodInfoExtractor = StyleStaticMethodInfoExtractor(processor) fun process(roundEnv: RoundEnvironment) { // TODO Check that no style was left behind? @@ -82,8 +82,10 @@ internal class StyleInfoExtractor(override val processor: ParisProcessor) : With styles + defaultNameFormatStyle } else { if (processor.namespacedResourcesEnabled && !styleableElement.getAnnotation(Styleable::class.java).emptyDefaultStyle) { - logError { "No default style found for ${styleableElement.simpleName}. Link an appropriate default style, " + - "or set @Styleable(emptyDefaultStyle = true) for this element if none exist." } + logError { + "No default style found for ${styleableElement.simpleName}. Link an appropriate default style, " + + "or set @Styleable(emptyDefaultStyle = true) for this element if none exist." + } } styles + EmptyStyleInfo(styleableElement, true) } @@ -103,7 +105,7 @@ internal class StyleInfoExtractor(override val processor: ParisProcessor) : With val elementName = styleableElement.simpleName.toString() val defaultStyleName = String.format(Locale.US, defaultStyleNameFormat, elementName) - val rStyleTypeElement = elements.getTypeElement("${RElement!!.qualifiedName}.style") + val rStyleTypeElement = processor.memoizer.rStyleTypeElement val defaultStyleExists = rStyleTypeElement != null && elements.getAllMembers(rStyleTypeElement).any { it.simpleName.toString() == defaultStyleName } @@ -120,8 +122,8 @@ internal class StyleInfoExtractor(override val processor: ParisProcessor) : With DEFAULT_STYLE_FORMATTED_NAME, javadoc, kdoc, - true, - styleResourceCode + isDefault = true, + styleResourceCode = styleResourceCode ) } else { return null @@ -133,6 +135,7 @@ internal interface StyleInfo { val enclosingElement: Element val elementName: String val formattedName: String + // val styleResourceCode: JavaCodeBlock? val javadoc: JavaCodeBlock val kdoc: KotlinCodeBlock diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/models/StyleStaticMethodInfo.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/models/StyleStaticMethodInfo.kt index ba33b1b8..3ed90645 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/models/StyleStaticMethodInfo.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/models/StyleStaticMethodInfo.kt @@ -2,14 +2,18 @@ package com.airbnb.paris.processor.models import com.airbnb.paris.annotations.Style import com.airbnb.paris.processor.ParisProcessor -import com.airbnb.paris.processor.framework.* +import com.airbnb.paris.processor.framework.JavaCodeBlock +import com.airbnb.paris.processor.framework.KotlinCodeBlock +import com.airbnb.paris.processor.framework.isNotStatic +import com.airbnb.paris.processor.framework.isPrivate +import com.airbnb.paris.processor.framework.isProtected import com.airbnb.paris.processor.framework.models.SkyStaticMethodModel import com.airbnb.paris.processor.framework.models.SkyStaticMethodModelFactory import com.airbnb.paris.processor.utils.ParisProcessorUtils import javax.lang.model.element.ExecutableElement -internal class StyleStaticMethodInfoExtractor(processor: ParisProcessor) - : SkyStaticMethodModelFactory(processor, Style::class.java) { +internal class StyleStaticMethodInfoExtractor(processor: ParisProcessor) : + SkyStaticMethodModelFactory(processor, Style::class.java) { override fun elementToModel(element: ExecutableElement): StyleStaticMethodInfo? { // TODO Get Javadoc from field/method and add it to the generated methods diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/models/StyleableInfo.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/models/StyleableInfo.kt index 8687ebe6..3c2093f3 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/models/StyleableInfo.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/models/StyleableInfo.kt @@ -3,7 +3,6 @@ package com.airbnb.paris.processor.models import com.airbnb.paris.annotations.Styleable import com.airbnb.paris.processor.ParisProcessor import com.airbnb.paris.processor.framework.WithSkyProcessor -import com.squareup.javapoet.ClassName import javax.annotation.processing.RoundEnvironment import javax.lang.model.element.Element import javax.lang.model.element.TypeElement @@ -34,18 +33,16 @@ internal class StyleableInfoExtractor(override val processor: ParisProcessor) : } } - return styleableElements - .mapNotNull { - fromElement( - it as TypeElement, - classesToStyleableChildInfo[it] ?: emptyList(), - classesToBeforeStyleInfo[it] ?: emptyList(), - classesToAfterStyleInfo[it] ?: emptyList(), - classesToAttrsInfo[it] ?: emptyList(), - classesToStylesInfo[it] ?: emptyList() - ) - } - .also { + return styleableElements.mapNotNull { + fromElement( + it as TypeElement, + classesToStyleableChildInfo[it] ?: emptyList(), + classesToBeforeStyleInfo[it] ?: emptyList(), + classesToAfterStyleInfo[it] ?: emptyList(), + classesToAttrsInfo[it] ?: emptyList(), + classesToStylesInfo[it] ?: emptyList() + ) + }.also { mutableModels.addAll(it) } } @@ -119,8 +116,7 @@ internal class StyleableInfo( } } - val formattedName = name.removePrefix("${styleableResourceName}_") - .removePrefix("android_") + val formattedName = name.removePrefix("${styleableResourceName}_").removePrefix("android_") return formattedName .foldRightIndexed("") { index, c, acc -> if (c == '_') { diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/writers/BaseStyleBuilderJavaClass.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/writers/BaseStyleBuilderJavaClass.kt index 56febad3..7b18c528 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/writers/BaseStyleBuilderJavaClass.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/writers/BaseStyleBuilderJavaClass.kt @@ -1,11 +1,33 @@ package com.airbnb.paris.processor.writers import androidx.annotation.RequiresApi -import com.airbnb.paris.processor.* -import com.airbnb.paris.processor.framework.* +import com.airbnb.paris.processor.Format +import com.airbnb.paris.processor.ParisProcessor +import com.airbnb.paris.processor.STYLE_APPLIER_CLASS_NAME +import com.airbnb.paris.processor.STYLE_BUILDER_CLASS_NAME +import com.airbnb.paris.processor.STYLE_BUILDER_FUNCTION_CLASS_NAME +import com.airbnb.paris.processor.STYLE_CLASS_NAME +import com.airbnb.paris.processor.StyleablesTree +import com.airbnb.paris.processor.framework.AndroidClassNames +import com.airbnb.paris.processor.framework.SkyJavaClass +import com.airbnb.paris.processor.framework.WithSkyProcessor +import com.airbnb.paris.processor.framework.abstract +import com.airbnb.paris.processor.framework.constructor +import com.airbnb.paris.processor.framework.method +import com.airbnb.paris.processor.framework.public +import com.airbnb.paris.processor.framework.static import com.airbnb.paris.processor.models.AttrInfo import com.airbnb.paris.processor.models.StyleableInfo -import com.squareup.javapoet.* +import com.squareup.javapoet.AnnotationSpec +import com.squareup.javapoet.ClassName +import com.squareup.javapoet.MethodSpec +import com.squareup.javapoet.ParameterSpec +import com.squareup.javapoet.ParameterizedTypeName +import com.squareup.javapoet.TypeName +import com.squareup.javapoet.TypeSpec +import com.squareup.javapoet.TypeVariableName +import com.squareup.javapoet.WildcardTypeName +import javax.lang.model.element.Element internal class BaseStyleBuilderJavaClass( override val processor: ParisProcessor, @@ -16,6 +38,10 @@ internal class BaseStyleBuilderJavaClass( override val packageName: String override val name: String + override val originatingElements: List = listOfNotNull( + styleableInfo.annotatedElement, + processor.memoizer.rStyleTypeElement + ) init { val styleApplierClassName = styleableInfo.styleApplierClassName @@ -84,8 +110,7 @@ internal class BaseStyleBuilderJavaClass( styleableInfo.styleableChildren.distinctBy { it.styleableResId.resourceName } for (styleableChildInfo in distinctStyleableChildren) { - val methodName = - styleableInfo.attrResourceNameToCamelCase(styleableChildInfo.styleableResId.resourceName) + val methodName = styleableInfo.attrResourceNameToCamelCase(styleableChildInfo.styleableResId.resourceName) method(methodName) { public() @@ -117,9 +142,11 @@ internal class BaseStyleBuilderJavaClass( addStatement("return (B) this") } - val subStyleApplierClassName = styleablesTree.findStyleApplier( + val (subStyleApplierAnnotatedElement, subStyleApplierClassName) = styleablesTree.findStyleApplier( styleableChildInfo.type.asTypeElement() ) + addOriginatingElement(subStyleApplierAnnotatedElement) + val subStyleBuilderClassName = subStyleApplierClassName.nestedClass("StyleBuilder") method(methodName) { public() @@ -277,9 +304,11 @@ internal class BaseStyleBuilderJavaClass( private fun addRequiresApiAnnotation(builder: MethodSpec.Builder, attr: AttrInfo) { if (attr.requiresApi > 1) { - builder.addAnnotation(AnnotationSpec.builder(RequiresApi::class.java) - .addMember("value", "\$L", attr.requiresApi) - .build()) + builder.addAnnotation( + AnnotationSpec.builder(RequiresApi::class.java) + .addMember("value", "\$L", attr.requiresApi) + .build() + ) } } } diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/writers/ModuleJavaClass.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/writers/ModuleJavaClass.kt index 3a9a15eb..8031e6bc 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/writers/ModuleJavaClass.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/writers/ModuleJavaClass.kt @@ -5,12 +5,17 @@ import com.airbnb.paris.annotations.GeneratedStyleableModule import com.airbnb.paris.processor.MODULE_SIMPLE_CLASS_NAME_FORMAT import com.airbnb.paris.processor.PARIS_MODULES_PACKAGE_NAME import com.airbnb.paris.processor.ParisProcessor -import com.airbnb.paris.processor.framework.* +import com.airbnb.paris.processor.framework.SkyJavaClass +import com.airbnb.paris.processor.framework.annotation +import com.airbnb.paris.processor.framework.final +import com.airbnb.paris.processor.framework.public +import com.airbnb.paris.processor.framework.value import com.airbnb.paris.processor.models.StyleableInfo import com.squareup.javapoet.AnnotationSpec import com.squareup.javapoet.TypeSpec import java.math.BigInteger import java.security.MessageDigest +import javax.lang.model.element.Element /** * Module classes index the styleable views available in their module. Since they are all put in the @@ -24,6 +29,7 @@ internal class ModuleJavaClass( override val packageName = PARIS_MODULES_PACKAGE_NAME override val name: String + override val originatingElements: List = styleablesInfo.map { it.annotatedElement } init { // The class name is a hash of all the styleable views' canonical names so the likelihood of diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/writers/ParisJavaClass.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/writers/ParisJavaClass.kt index f42d3295..0b64269f 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/writers/ParisJavaClass.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/writers/ParisJavaClass.kt @@ -3,11 +3,17 @@ package com.airbnb.paris.processor.writers import com.airbnb.paris.processor.PARIS_SIMPLE_CLASS_NAME import com.airbnb.paris.processor.ParisProcessor import com.airbnb.paris.processor.SPANNABLE_BUILDER_CLASS_NAME -import com.airbnb.paris.processor.framework.* +import com.airbnb.paris.processor.framework.AndroidClassNames +import com.airbnb.paris.processor.framework.SkyJavaClass +import com.airbnb.paris.processor.framework.final +import com.airbnb.paris.processor.framework.method +import com.airbnb.paris.processor.framework.public +import com.airbnb.paris.processor.framework.static import com.airbnb.paris.processor.models.BaseStyleableInfo import com.airbnb.paris.processor.models.StyleableInfo import com.squareup.javapoet.TypeName import com.squareup.javapoet.TypeSpec +import javax.lang.model.element.Element internal class ParisJavaClass( override val processor: ParisProcessor, @@ -16,17 +22,19 @@ internal class ParisJavaClass( externalStyleableClassesInfo: List ) : SkyJavaClass(processor) { + val sortedStyleableClassesInfo = (styleableClassesInfo + externalStyleableClassesInfo).sortedBy { + it.elementName + } + override val packageName: String = parisClassPackageName override val name: String = PARIS_SIMPLE_CLASS_NAME + override val originatingElements: List = + sortedStyleableClassesInfo.map { it.annotatedElement } override val block: TypeSpec.Builder.() -> Unit = { public() final() - val sortedStyleableClassesInfo = - (styleableClassesInfo + externalStyleableClassesInfo).sortedBy { - it.elementName - } for (styleableClassInfo in sortedStyleableClassesInfo) { val styleApplierClassName = styleableClassInfo.styleApplierClassName val viewParameterTypeName = TypeName.get(styleableClassInfo.viewElementType) diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/writers/StyleApplierJavaClass.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/writers/StyleApplierJavaClass.kt index 49a9dd4a..23db2f4d 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/writers/StyleApplierJavaClass.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/writers/StyleApplierJavaClass.kt @@ -1,9 +1,37 @@ package com.airbnb.paris.processor.writers -import com.airbnb.paris.processor.* -import com.airbnb.paris.processor.framework.* -import com.airbnb.paris.processor.models.* -import com.squareup.javapoet.* +import com.airbnb.paris.processor.Format +import com.airbnb.paris.processor.ParisProcessor +import com.airbnb.paris.processor.STYLE_APPLIER_CLASS_NAME +import com.airbnb.paris.processor.STYLE_APPLIER_UTILS_CLASS_NAME +import com.airbnb.paris.processor.STYLE_CLASS_NAME +import com.airbnb.paris.processor.StyleablesTree +import com.airbnb.paris.processor.TYPED_ARRAY_WRAPPER_CLASS_NAME +import com.airbnb.paris.processor.WithParisProcessor +import com.airbnb.paris.processor.framework.AndroidClassNames +import com.airbnb.paris.processor.framework.SkyJavaClass +import com.airbnb.paris.processor.framework.codeBlock +import com.airbnb.paris.processor.framework.constructor +import com.airbnb.paris.processor.framework.controlFlow +import com.airbnb.paris.processor.framework.final +import com.airbnb.paris.processor.framework.method +import com.airbnb.paris.processor.framework.override +import com.airbnb.paris.processor.framework.protected +import com.airbnb.paris.processor.framework.public +import com.airbnb.paris.processor.framework.static +import com.airbnb.paris.processor.models.EmptyStyleInfo +import com.airbnb.paris.processor.models.StyleCompanionPropertyInfo +import com.airbnb.paris.processor.models.StyleResInfo +import com.airbnb.paris.processor.models.StyleStaticMethodInfo +import com.airbnb.paris.processor.models.StyleableInfo +import com.squareup.javapoet.ArrayTypeName +import com.squareup.javapoet.ClassName +import com.squareup.javapoet.CodeBlock +import com.squareup.javapoet.MethodSpec +import com.squareup.javapoet.ParameterizedTypeName +import com.squareup.javapoet.TypeName +import com.squareup.javapoet.TypeSpec +import javax.lang.model.element.Element internal class StyleApplierJavaClass( override val processor: ParisProcessor, @@ -13,6 +41,12 @@ internal class StyleApplierJavaClass( override val packageName = styleableInfo.styleApplierClassName.packageName()!! override val name = styleableInfo.styleApplierClassName.simpleName()!! + override val originatingElements: List = listOfNotNull( + styleableInfo.annotatedElement, + // The R.style class is used to look up matching default style names, so if + // the R class changes it can affect the code we generate. + processor.memoizer.rStyleTypeElement + ) override val block: TypeSpec.Builder.() -> Unit = { addAnnotation(AndroidClassNames.UI_THREAD) @@ -39,10 +73,14 @@ internal class StyleApplierJavaClass( // If the view type is "View" then there is no parent var parentStyleApplierClassName: ClassName? = null - if (!isSameType(AndroidClassNames.VIEW.toTypeMirror(), styleableInfo.viewElementType)) { - parentStyleApplierClassName = styleablesTree.findStyleApplier( + if (!isSameType(processor.memoizer.androidViewClassType, styleableInfo.viewElementType)) { + val parentStyleApplierDetails = styleablesTree.findStyleApplier( styleableInfo.viewElementType.asTypeElement().superclass.asTypeElement() ) + + parentStyleApplierClassName = parentStyleApplierDetails.className + addOriginatingElement(parentStyleApplierDetails.annotatedElement) + method("applyParent") { override() protected() @@ -204,9 +242,13 @@ internal class StyleApplierJavaClass( } for (styleableChildInfo in styleableInfo.styleableChildren) { - val subStyleApplierClassName = styleablesTree.findStyleApplier( + val (subStyleApplierAnnotatedElement, subStyleApplierClassName) = styleablesTree.findStyleApplier( styleableChildInfo.type.asTypeElement() ) + // If the name of the proxy or subStyle type changes then our generated code needs to update as well, + // therefore we must depend on it as an originating element. + addOriginatingElement(subStyleApplierAnnotatedElement) + method(styleableChildInfo.name) { public() returns(subStyleApplierClassName) diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/writers/StyleBuilderJavaClass.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/writers/StyleBuilderJavaClass.kt index 5fb9f732..dfd9d34c 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/writers/StyleBuilderJavaClass.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/writers/StyleBuilderJavaClass.kt @@ -1,11 +1,22 @@ package com.airbnb.paris.processor.writers import com.airbnb.paris.processor.ParisProcessor -import com.airbnb.paris.processor.framework.* -import com.airbnb.paris.processor.models.* +import com.airbnb.paris.processor.framework.AndroidClassNames +import com.airbnb.paris.processor.framework.SkyJavaClass +import com.airbnb.paris.processor.framework.constructor +import com.airbnb.paris.processor.framework.final +import com.airbnb.paris.processor.framework.method +import com.airbnb.paris.processor.framework.public +import com.airbnb.paris.processor.framework.static +import com.airbnb.paris.processor.models.EmptyStyleInfo +import com.airbnb.paris.processor.models.StyleCompanionPropertyInfo +import com.airbnb.paris.processor.models.StyleResInfo +import com.airbnb.paris.processor.models.StyleStaticMethodInfo +import com.airbnb.paris.processor.models.StyleableInfo import com.squareup.javapoet.ClassName import com.squareup.javapoet.ParameterizedTypeName import com.squareup.javapoet.TypeSpec +import javax.lang.model.element.Element internal fun getStyleBuilderClassName(styleApplierClassName: ClassName) = styleApplierClassName.nestedClass("StyleBuilder") @@ -17,6 +28,7 @@ internal class StyleBuilderJavaClass( override val packageName: String override val name: String + override val originatingElements: List = listOf(styleableInfo.annotatedElement) init { val className = getStyleBuilderClassName(styleableInfo.styleApplierClassName) diff --git a/paris-processor/src/main/java/com/airbnb/paris/processor/writers/StyleExtensionsKotlinFile.kt b/paris-processor/src/main/java/com/airbnb/paris/processor/writers/StyleExtensionsKotlinFile.kt index 08a2aa85..60f75d85 100644 --- a/paris-processor/src/main/java/com/airbnb/paris/processor/writers/StyleExtensionsKotlinFile.kt +++ b/paris-processor/src/main/java/com/airbnb/paris/processor/writers/StyleExtensionsKotlinFile.kt @@ -1,16 +1,30 @@ package com.airbnb.paris.processor.writers import androidx.annotation.RequiresApi -import androidx.annotation.StyleRes -import com.airbnb.paris.processor.* +import com.airbnb.paris.processor.EXTENDABLE_STYLE_BUILDER_CLASS_NAME +import com.airbnb.paris.processor.EXTENSIONS_FILE_NAME_FORMAT +import com.airbnb.paris.processor.Format +import com.airbnb.paris.processor.PARIS_KOTLIN_EXTENSIONS_PACKAGE_NAME +import com.airbnb.paris.processor.ParisProcessor +import com.airbnb.paris.processor.STYLE_CLASS_NAME import com.airbnb.paris.processor.framework.* import com.airbnb.paris.processor.framework.AndroidClassNames.ATTRIBUTE_SET import com.airbnb.paris.processor.framework.AndroidClassNames.COLOR_INT import com.airbnb.paris.processor.framework.AndroidClassNames.STYLE_RES import com.airbnb.paris.processor.models.AttrInfo import com.airbnb.paris.processor.models.StyleableInfo -import com.squareup.kotlinpoet.* +import com.squareup.kotlinpoet.AnnotationSpec +import com.squareup.kotlinpoet.FileSpec +import com.squareup.kotlinpoet.FunSpec +import com.squareup.kotlinpoet.INT +import com.squareup.kotlinpoet.KModifier +import com.squareup.kotlinpoet.LambdaTypeName +import com.squareup.kotlinpoet.ParameterSpec import com.squareup.kotlinpoet.ParameterizedTypeName.Companion.parameterizedBy +import com.squareup.kotlinpoet.UNIT +import com.squareup.kotlinpoet.WildcardTypeName +import com.squareup.kotlinpoet.asClassName +import com.squareup.kotlinpoet.asTypeName /** * This generates a kt file with extension functions to style a specific view. @@ -56,6 +70,7 @@ internal class StyleExtensionsKotlinFile( receiver(styleable.viewElementType) addParameter("style", STYLE_CLASS_NAME.toKPoet()) addStatement("%T(this).apply(style)", styleable.styleApplierClassName.toKPoet()) + addOriginatingElement(styleable.annotatedElement) } /* @@ -69,6 +84,7 @@ internal class StyleExtensionsKotlinFile( addAnnotation(STYLE_RES) } addStatement("%T(this).apply(styleRes)", styleable.styleApplierClassName.toKPoet()) + addOriginatingElement(styleable.annotatedElement) } /* @@ -80,6 +96,7 @@ internal class StyleExtensionsKotlinFile( receiver(styleable.viewElementType) addParameter("attrs", ATTRIBUTE_SET.toKPoet().copy(nullable = true)) addStatement("%T(this).apply(attrs)", styleable.styleApplierClassName.toKPoet()) + addOriginatingElement(styleable.annotatedElement) } /* @@ -118,6 +135,8 @@ internal class StyleExtensionsKotlinFile( extendableStyleBuilderTypeName, builderParameter ) + + addOriginatingElement(styleable.annotatedElement) } /* @@ -140,6 +159,8 @@ internal class StyleExtensionsKotlinFile( "add(%T().add${it.formattedName}().build())", styleable.styleBuilderClassName.toKPoet() ) + + addOriginatingElement(styleable.annotatedElement) } } @@ -171,6 +192,9 @@ internal class StyleExtensionsKotlinFile( styleable.styleableResourceName, styleableChildInfo.styleableResId.kotlinCode ) + + addOriginatingElement(styleable.annotatedElement) + addOriginatingElement(styleableChildInfo.element) } // Sub-styles can be style objects: "view.style { titleStyle(styleObject) }" @@ -183,6 +207,8 @@ internal class StyleExtensionsKotlinFile( styleable.styleableResourceName, styleableChildInfo.styleableResId.kotlinCode ) + addOriginatingElement(styleable.annotatedElement) + addOriginatingElement(styleableChildInfo.element) } /* @@ -213,6 +239,8 @@ internal class StyleExtensionsKotlinFile( subExtendableStyleBuilderTypeName, builderParameter ) + addOriginatingElement(styleable.annotatedElement) + addOriginatingElement(styleableChildInfo.element) } } @@ -258,6 +286,9 @@ internal class StyleExtensionsKotlinFile( styleable.styleableResourceName, attr.styleableResId.kotlinCode ) + + addOriginatingElement(styleable.annotatedElement) + addOriginatingElement(attr.element) } } @@ -278,6 +309,9 @@ internal class StyleExtensionsKotlinFile( styleable.styleableResourceName, attr.styleableResId.kotlinCode ) + + addOriginatingElement(styleable.annotatedElement) + addOriginatingElement(attr.element) } // Adds a special Dp method that automatically converts a dp value to pixels for dimensions @@ -302,6 +336,9 @@ internal class StyleExtensionsKotlinFile( styleable.styleableResourceName, attr.styleableResId.kotlinCode ) + + addOriginatingElement(styleable.annotatedElement) + addOriginatingElement(attr.element) } } @@ -323,6 +360,9 @@ internal class StyleExtensionsKotlinFile( styleable.styleableResourceName, attr.styleableResId.kotlinCode ) + + addOriginatingElement(styleable.annotatedElement) + addOriginatingElement(attr.element) } } } @@ -353,6 +393,8 @@ internal class StyleExtensionsKotlinFile( extendableStyleBuilderTypeName, builderParam ) + + addOriginatingElement(styleable.annotatedElement) } } @@ -361,7 +403,8 @@ internal class StyleExtensionsKotlinFile( builder.addAnnotation( AnnotationSpec.builder(RequiresApi::class.java) .addMember("%L", attr.requiresApi) - .build()) + .build() + ) } } } diff --git a/paris-test-lib/build.gradle b/paris-test-lib/build.gradle index d4e33fd6..2c235b95 100644 --- a/paris-test-lib/build.gradle +++ b/paris-test-lib/build.gradle @@ -17,6 +17,15 @@ android { } } } + + compileOptions { + sourceCompatibility rootProject.JAVA_SOURCE_VERSION + targetCompatibility rootProject.JAVA_TARGET_VERSION + } + + kotlinOptions { + jvmTarget = '1.8' + } } dependencies { diff --git a/paris-test/build.gradle b/paris-test/build.gradle index 37ab134d..5e0ce091 100644 --- a/paris-test/build.gradle +++ b/paris-test/build.gradle @@ -3,7 +3,6 @@ import org.gradle.internal.jvm.Jvm apply plugin: 'com.android.library' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' -apply from: rootProject.file('gradle/workaround-missing-resource.gradle') android { compileSdkVersion rootProject.COMPILE_SDK_VERSION @@ -19,6 +18,22 @@ android { } } } + + testOptions { + unitTests { + includeAndroidResources = true + } + } + + + compileOptions { + sourceCompatibility rootProject.JAVA_SOURCE_VERSION + targetCompatibility rootProject.JAVA_TARGET_VERSION + } + + kotlinOptions { + jvmTarget = '1.8' + } } dependencies { @@ -54,3 +69,18 @@ static def getRuntimeJar() { throw new RuntimeException(e) } } + +// Java files in the "resources" folder are not included in the build for some reason (seems like source files are skipped?) +// This files are needed to test the annotation processor, so we manually copy them into the build. +task('copyDebugTestResources', type: Copy) { + from("${projectDir}/src/test/resources") + into("${buildDir}/intermediates/sourceFolderJavaResources/debug") +} + +task('copyReleaseTestResources', type: Copy) { + from("${projectDir}/src/test/resources") + into("${buildDir}/intermediates/sourceFolderJavaResources/release") +} + +preBuild.dependsOn copyReleaseTestResources +preBuild.dependsOn copyDebugTestResources diff --git a/paris-test/src/androidTest/java/com/airbnb/paris/test/StyleApplierUtilsTest.kt b/paris-test/src/androidTest/java/com/airbnb/paris/test/StyleApplierUtilsTest.kt index 877ae328..55afbfea 100644 --- a/paris-test/src/androidTest/java/com/airbnb/paris/test/StyleApplierUtilsTest.kt +++ b/paris-test/src/androidTest/java/com/airbnb/paris/test/StyleApplierUtilsTest.kt @@ -1,13 +1,16 @@ package com.airbnb.paris.test -import androidx.test.* -import androidx.test.runner.* -import android.util.* -import android.view.*; -import com.airbnb.paris.* -import com.airbnb.paris.styles.* -import org.junit.* -import org.junit.runner.* +import android.util.LayoutDirection +import android.util.TypedValue +import android.view.ViewGroup +import androidx.test.InstrumentationRegistry +import androidx.test.runner.AndroidJUnit4 +import com.airbnb.paris.StyleApplierUtils +import com.airbnb.paris.styles.ResourceStyle +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class StyleApplierUtilsTest { @@ -30,37 +33,47 @@ class StyleApplierUtilsTest { @Test fun subStylesSameStyle() { - StyleApplierUtils.assertSameAttributes(myViewApplier, - ResourceStyle(R.style.StyleApplierUtilsTest_MyView_titleStyle_textColorTextSizePadding_1), - ResourceStyle(R.style.StyleApplierUtilsTest_MyView_titleStyle_textColorTextSizePadding_1)) + StyleApplierUtils.assertSameAttributes( + myViewApplier, + ResourceStyle(R.style.StyleApplierUtilsTest_MyView_titleStyle_textColorTextSizePadding_1), + ResourceStyle(R.style.StyleApplierUtilsTest_MyView_titleStyle_textColorTextSizePadding_1) + ) } @Test fun subStylesSameAttributes() { - StyleApplierUtils.assertSameAttributes(myViewApplier, - ResourceStyle(R.style.StyleApplierUtilsTest_MyView_titleStyle_textColorTextSizePadding_1), - ResourceStyle(R.style.StyleApplierUtilsTest_MyView_titleStyle_textColorTextSizePadding_2)) + StyleApplierUtils.assertSameAttributes( + myViewApplier, + ResourceStyle(R.style.StyleApplierUtilsTest_MyView_titleStyle_textColorTextSizePadding_1), + ResourceStyle(R.style.StyleApplierUtilsTest_MyView_titleStyle_textColorTextSizePadding_2) + ) } @Test(expected = AssertionError::class) fun subStylesDifferentAttributes() { - StyleApplierUtils.assertSameAttributes(myViewApplier, - ResourceStyle(R.style.StyleApplierUtilsTest_MyView_titleStyle_textColorTextSizePadding_1), - ResourceStyle(R.style.StyleApplierUtilsTest_MyView_titleStyle_textSizePadding)) + StyleApplierUtils.assertSameAttributes( + myViewApplier, + ResourceStyle(R.style.StyleApplierUtilsTest_MyView_titleStyle_textColorTextSizePadding_1), + ResourceStyle(R.style.StyleApplierUtilsTest_MyView_titleStyle_textSizePadding) + ) } @Test fun emptyStyleSameStyle() { - StyleApplierUtils.assertSameAttributes(myViewApplier, - ResourceStyle(R.style.Empty), - ResourceStyle(R.style.Empty)) + StyleApplierUtils.assertSameAttributes( + myViewApplier, + ResourceStyle(R.style.Empty), + ResourceStyle(R.style.Empty) + ) } @Test(expected = AssertionError::class) fun emptyStyleDifferentAttributes() { - StyleApplierUtils.assertSameAttributes(myViewApplier, - ResourceStyle(R.style.StyleApplierUtilsTest_MyView_titleStyle_textColorTextSizePadding_1), - ResourceStyle(R.style.Empty)) + StyleApplierUtils.assertSameAttributes( + myViewApplier, + ResourceStyle(R.style.StyleApplierUtilsTest_MyView_titleStyle_textColorTextSizePadding_1), + ResourceStyle(R.style.Empty) + ) } @Test @@ -75,9 +88,11 @@ class StyleApplierUtilsTest { fun defaultValues() { // Because MyView specifies a default value for active this should be fine - StyleApplierUtils.assertSameAttributes(myViewApplier, - ResourceStyle(R.style.StyleApplierUtilsTest_MyView_active), - ResourceStyle(R.style.Empty)) + StyleApplierUtils.assertSameAttributes( + myViewApplier, + ResourceStyle(R.style.StyleApplierUtilsTest_MyView_active), + ResourceStyle(R.style.Empty) + ) } @Test @@ -87,13 +102,14 @@ class StyleApplierUtilsTest { myOtherView.title.setTextSize(TypedValue.COMPLEX_UNIT_PX, 10f) Assert.assertEquals(10f, myOtherView.title.textSize) - StyleApplierUtils.assertSameAttributes(myViewApplier, - myViewApplier.builder() - .titleStyle({ it.textSize(15) }) - .build(), - myViewApplier.builder() - .titleStyle({ it.textSize(15) }) - .build() + StyleApplierUtils.assertSameAttributes( + myViewApplier, + myViewApplier.builder() + .titleStyle({ it.textSize(15) }) + .build(), + myViewApplier.builder() + .titleStyle({ it.textSize(15) }) + .build() ) Assert.assertEquals(10f, myOtherView.title.textSize) } diff --git a/paris-test/src/androidTest/java/com/airbnb/paris/test/ViewStyleBuilderTest.kt b/paris-test/src/androidTest/java/com/airbnb/paris/test/ViewStyleBuilderTest.kt index 148c674e..b497577b 100644 --- a/paris-test/src/androidTest/java/com/airbnb/paris/test/ViewStyleBuilderTest.kt +++ b/paris-test/src/androidTest/java/com/airbnb/paris/test/ViewStyleBuilderTest.kt @@ -1,12 +1,13 @@ package com.airbnb.paris.test -import android.graphics.* -import androidx.test.* -import androidx.test.runner.* -import com.airbnb.paris.styles.* -import org.junit.* -import org.junit.Assert.* -import org.junit.runner.* +import android.graphics.Color +import androidx.test.InstrumentationRegistry +import androidx.test.runner.AndroidJUnit4 +import com.airbnb.paris.styles.EmptyStyle +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class ViewStyleBuilderTest { @@ -30,13 +31,13 @@ class ViewStyleBuilderTest { @Test fun subStyleCombination() { val style = myViewBuilder - .titleStyle { builder -> - builder.textColor(Color.RED) - } - .titleStyle { builder -> - builder.textSize(16) - } - .build() + .titleStyle { builder -> + builder.textColor(Color.RED) + } + .titleStyle { builder -> + builder.textSize(16) + } + .build() val typedArray = style.obtainStyledAttributes(context, R.styleable.MyView) val subStyle = typedArray.getStyle(R.styleable.MyView_titleStyle) val subTypedArray = subStyle.obtainStyledAttributes(context, R.styleable.Paris_TextView) @@ -48,13 +49,13 @@ class ViewStyleBuilderTest { @Test fun subStyleCombinationPrecedence() { val style = myViewBuilder - .titleStyle { builder -> - builder.textColor(Color.RED) - } - .titleStyle { builder -> - builder.textColor(Color.GREEN) - } - .build() + .titleStyle { builder -> + builder.textColor(Color.RED) + } + .titleStyle { builder -> + builder.textColor(Color.GREEN) + } + .build() val typedArray = style.obtainStyledAttributes(context, R.styleable.MyView) val subStyle = typedArray.getStyle(R.styleable.MyView_titleStyle) val subTypedArray = subStyle.obtainStyledAttributes(context, R.styleable.Paris_TextView) @@ -70,14 +71,14 @@ class ViewStyleBuilderTest { // substyle combinatory rules it had to be removed val style = myViewBuilder - .titleStyle { builder -> - builder.textColor(Color.RED) - } - .add(EmptyStyle) - .titleStyle { builder -> - builder.textColor(Color.GREEN) - } - .build() + .titleStyle { builder -> + builder.textColor(Color.RED) + } + .add(EmptyStyle) + .titleStyle { builder -> + builder.textColor(Color.GREEN) + } + .build() val typedArray = style.obtainStyledAttributes(context, intArrayOf(R.attr.titleStyle)) val subStyle = typedArray.getStyle(0) val subTypedArray = subStyle.obtainStyledAttributes(context, R.styleable.Paris_TextView) diff --git a/paris-test/src/test/java/com/airbnb/paris/test/ParisProcessorTest.kt b/paris-test/src/test/java/com/airbnb/paris/test/ParisProcessorTest.kt index 44c0d023..a4f73373 100644 --- a/paris-test/src/test/java/com/airbnb/paris/test/ParisProcessorTest.kt +++ b/paris-test/src/test/java/com/airbnb/paris/test/ParisProcessorTest.kt @@ -6,15 +6,26 @@ import com.google.testing.compile.JavaFileObjects import com.google.testing.compile.JavaSourceSubjectFactory.javaSource import com.google.testing.compile.JavaSourcesSubjectFactory.javaSources import org.junit.Test +import java.io.File +import java.net.URL +/** + * Since AGP 3.6.0, the class-loader behavior has been modified. + * Unfortunately Guava (via compile-testing) uses a class-loader based mechanism + * which is valid on JVM but not supposed to be supported on Android. + * As the project paths are simple enough, we can hardcode them for now. + */ +fun String.patchResource(): URL = + File("build/intermediates/sourceFolderJavaResources/debug/$this").toURI().toURL() + class ParisProcessorTest { private fun assertCase(folder: String) { - val view = JavaFileObjects.forResource("$folder/MyView.java") - val generatedParisClass = JavaFileObjects.forResource("$folder/Paris.java") + val view = JavaFileObjects.forResource("$folder/MyView.java".patchResource()) + val generatedParisClass = JavaFileObjects.forResource("$folder/Paris.java".patchResource()) val generatedStyleApplierClass = - JavaFileObjects.forResource("$folder/MyViewStyleApplier.java") + JavaFileObjects.forResource("$folder/MyViewStyleApplier.java".patchResource()) assert_().about(javaSource()) .that(view) @@ -27,8 +38,8 @@ class ParisProcessorTest { } private fun assertCaseWithInput(folder: String, input: List, output: List) { - val inputFileObjects = input.map { JavaFileObjects.forResource("$folder/input/${it}") } - val outputFileObjects = output.map { JavaFileObjects.forResource("$folder/output/${it}") } + val inputFileObjects = input.map { JavaFileObjects.forResource("$folder/input/${it}".patchResource()) } + val outputFileObjects = output.map { JavaFileObjects.forResource("$folder/output/${it}".patchResource()) } assert_().about(javaSources()) .that(inputFileObjects) @@ -43,7 +54,7 @@ class ParisProcessorTest { errorCount: Int? = null, errorFragment: String? = null ) { - val view = JavaFileObjects.forResource("$folder/MyView.java") + val view = JavaFileObjects.forResource("$folder/MyView.java".patchResource()) assert_().about(javaSource()) .that(view) @@ -65,7 +76,7 @@ class ParisProcessorTest { errorFragment: String? = null, input: List ) { - val inputFileObjects = input.map { JavaFileObjects.forResource("$folder/input/${it}") } + val inputFileObjects = input.map { JavaFileObjects.forResource("$folder/input/${it}".patchResource()) } assert_().about(javaSources()) .that(inputFileObjects) @@ -278,4 +289,5 @@ class ParisProcessorTest { fun styles() { assertCase("styles") } -} \ No newline at end of file +} + diff --git a/paris/build.gradle b/paris/build.gradle index 62175dee..c0cb1014 100644 --- a/paris/build.gradle +++ b/paris/build.gradle @@ -2,6 +2,7 @@ apply plugin: 'com.android.library' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' apply plugin: 'com.jakewharton.butterknife' +apply plugin: "com.vanniktech.maven.publish" android { compileSdkVersion rootProject.COMPILE_SDK_VERSION @@ -22,6 +23,15 @@ android { lintOptions { warningsAsErrors true } + + compileOptions { + sourceCompatibility rootProject.JAVA_SOURCE_VERSION + targetCompatibility rootProject.JAVA_TARGET_VERSION + } + + kotlinOptions { + jvmTarget = '1.8' + } } dependencies { @@ -45,5 +55,3 @@ dependencies { androidTestImplementation deps.mockitoAndroid androidTestImplementation deps.espresso } - -apply from: rootProject.file('gradle/gradle-maven-push.gradle') \ No newline at end of file diff --git a/paris/src/androidTest/java/com/airbnb/paris/MapTypedArrayWrapperTest.kt b/paris/src/androidTest/java/com/airbnb/paris/MapTypedArrayWrapperTest.kt index f0884a9d..a4b158c6 100644 --- a/paris/src/androidTest/java/com/airbnb/paris/MapTypedArrayWrapperTest.kt +++ b/paris/src/androidTest/java/com/airbnb/paris/MapTypedArrayWrapperTest.kt @@ -3,9 +3,9 @@ package com.airbnb.paris import android.content.Context import android.content.res.Resources import android.graphics.Color +import android.view.ViewGroup import androidx.test.InstrumentationRegistry import androidx.test.runner.AndroidJUnit4 -import android.view.ViewGroup import com.airbnb.paris.attribute_values.ColorValue import com.airbnb.paris.attribute_values.DpValue import com.airbnb.paris.attribute_values.ResourceId @@ -32,16 +32,16 @@ class MapTypedArrayWrapperTest { private lateinit var wrapper: MapTypedArrayWrapper private val attrResToValueResMaps = listOf( - emptyMap(), - mapOf(R.attr.formatBoolean to ResourceId(R.bool.format_boolean)), - mapOf( - R.attr.formatBoolean to ResourceId(R.bool.format_boolean), - R.attr.formatColor to ResourceId(R.color.format_color), - R.attr.formatDimension to ResourceId(R.dimen.format_dimension), - // This attr is not included in R.styleable.Format, as a result it should be ignored by - // the MapTypedArrayWrapper - R.attr.background to ResourceId(R.color.format_color) - ) + emptyMap(), + mapOf(R.attr.formatBoolean to ResourceId(R.bool.format_boolean)), + mapOf( + R.attr.formatBoolean to ResourceId(R.bool.format_boolean), + R.attr.formatColor to ResourceId(R.color.format_color), + R.attr.formatDimension to ResourceId(R.dimen.format_dimension), + // This attr is not included in R.styleable.Format, as a result it should be ignored by + // the MapTypedArrayWrapper + R.attr.background to ResourceId(R.color.format_color) + ) ) @Before diff --git a/paris/src/androidTest/java/com/airbnb/paris/MultiTypedArrayWrapperTest.kt b/paris/src/androidTest/java/com/airbnb/paris/MultiTypedArrayWrapperTest.kt index 40d2c703..50c465bb 100644 --- a/paris/src/androidTest/java/com/airbnb/paris/MultiTypedArrayWrapperTest.kt +++ b/paris/src/androidTest/java/com/airbnb/paris/MultiTypedArrayWrapperTest.kt @@ -26,28 +26,28 @@ class MultiTypedArrayWrapperTest { private lateinit var multi: MultiTypedArrayWrapper private val attrResToValueResMaps = listOf( - emptyMap(), - mapOf(R.attr.formatBoolean to ResourceId(R.bool.format_boolean)), - mapOf( - R.attr.formatBoolean to ResourceId(R.bool.format_boolean_2), - R.attr.formatColor to ResourceId(R.color.format_color), - R.attr.formatDimension to ResourceId(R.dimen.format_dimension), - // This attr is not included in R.styleable.Format, as a result it should be ignored by - // the MapTypedArrayWrapper - R.attr.background to ResourceId(R.color.format_color) - ), - mapOf( - R.attr.formatColor to ResourceId(R.color.format_color_2), - R.attr.formatInteger to ResourceId(R.integer.format_integer), - R.attr.background to ResourceId(R.color.format_color_2) - ) + emptyMap(), + mapOf(R.attr.formatBoolean to ResourceId(R.bool.format_boolean)), + mapOf( + R.attr.formatBoolean to ResourceId(R.bool.format_boolean_2), + R.attr.formatColor to ResourceId(R.color.format_color), + R.attr.formatDimension to ResourceId(R.dimen.format_dimension), + // This attr is not included in R.styleable.Format, as a result it should be ignored by + // the MapTypedArrayWrapper + R.attr.background to ResourceId(R.color.format_color) + ), + mapOf( + R.attr.formatColor to ResourceId(R.color.format_color_2), + R.attr.formatInteger to ResourceId(R.integer.format_integer), + R.attr.background to ResourceId(R.color.format_color_2) + ) ) private fun newFormatWrappers() = - attrResToValueResMaps.map { newFormatWrapper(it) } + attrResToValueResMaps.map { newFormatWrapper(it) } private fun newFormatWrapper(attrResToValueResMap: Map) = - MapTypedArrayWrapper(context, R.styleable.Formats, attrResToValueResMap) + MapTypedArrayWrapper(context, R.styleable.Formats, attrResToValueResMap) @Before fun setup() { @@ -65,10 +65,10 @@ class MultiTypedArrayWrapperTest { fun getIndex() { multi = MultiTypedArrayWrapper(newFormatWrappers(), R.styleable.Formats) val indexes = mutableListOf( - R.styleable.Formats_formatBoolean, - R.styleable.Formats_formatColor, - R.styleable.Formats_formatDimension, - R.styleable.Formats_formatInteger + R.styleable.Formats_formatBoolean, + R.styleable.Formats_formatColor, + R.styleable.Formats_formatDimension, + R.styleable.Formats_formatInteger ) (0 until indexes.size).forEach { at -> indexes.remove(multi.getIndex(at)) @@ -80,10 +80,10 @@ class MultiTypedArrayWrapperTest { fun hasValue() { multi = MultiTypedArrayWrapper(newFormatWrappers(), R.styleable.Formats) listOf( - R.styleable.Formats_formatBoolean, - R.styleable.Formats_formatColor, - R.styleable.Formats_formatDimension, - R.styleable.Formats_formatInteger + R.styleable.Formats_formatBoolean, + R.styleable.Formats_formatColor, + R.styleable.Formats_formatDimension, + R.styleable.Formats_formatInteger ).forEach { index -> assertEquals(true, multi.hasValue(index)) } @@ -92,11 +92,11 @@ class MultiTypedArrayWrapperTest { @Test fun getBoolean() { multi = MultiTypedArrayWrapper( - listOf( - mapOf(R.attr.formatBoolean to ResourceId(R.bool.format_boolean)), - mapOf(R.attr.formatBoolean to ResourceId(R.bool.format_boolean_2)) - ).map { newFormatWrapper(it) }, - R.styleable.Formats + listOf( + mapOf(R.attr.formatBoolean to ResourceId(R.bool.format_boolean)), + mapOf(R.attr.formatBoolean to ResourceId(R.bool.format_boolean_2)) + ).map { newFormatWrapper(it) }, + R.styleable.Formats ) val actual = res.getBoolean(R.bool.format_boolean_2) assertEquals(actual, multi.getBoolean(R.styleable.Formats_formatBoolean)) @@ -105,11 +105,11 @@ class MultiTypedArrayWrapperTest { @Test fun getColor() { multi = MultiTypedArrayWrapper( - listOf( - mapOf(R.attr.formatColor to ResourceId(R.color.format_color)), - mapOf(R.attr.formatColor to ResourceId(R.color.format_color_2)) - ).map { newFormatWrapper(it) }, - R.styleable.Formats + listOf( + mapOf(R.attr.formatColor to ResourceId(R.color.format_color)), + mapOf(R.attr.formatColor to ResourceId(R.color.format_color_2)) + ).map { newFormatWrapper(it) }, + R.styleable.Formats ) val actual = res.getColor(R.color.format_color_2) assertEquals(actual, multi.getColor(R.styleable.Formats_formatColor)) @@ -118,11 +118,11 @@ class MultiTypedArrayWrapperTest { @Test fun getColorStateList() { multi = MultiTypedArrayWrapper( - listOf( - mapOf(R.attr.formatColor to ResourceId(R.color.format_color)), - mapOf(R.attr.formatColor to ResourceId(R.color.format_color_2)) - ).map { newFormatWrapper(it) }, - R.styleable.Formats + listOf( + mapOf(R.attr.formatColor to ResourceId(R.color.format_color)), + mapOf(R.attr.formatColor to ResourceId(R.color.format_color_2)) + ).map { newFormatWrapper(it) }, + R.styleable.Formats ) val actual = res.getColorStateList(R.color.format_color_2) assertEquals(actual, multi.getColorStateList(R.styleable.Formats_formatColor)) @@ -131,11 +131,11 @@ class MultiTypedArrayWrapperTest { @Test fun getDimensionPixelSize() { multi = MultiTypedArrayWrapper( - listOf( - mapOf(R.attr.formatDimension to ResourceId(R.dimen.format_dimension)), - mapOf(R.attr.formatDimension to ResourceId(R.dimen.format_dimension_2)) - ).map { newFormatWrapper(it) }, - R.styleable.Formats + listOf( + mapOf(R.attr.formatDimension to ResourceId(R.dimen.format_dimension)), + mapOf(R.attr.formatDimension to ResourceId(R.dimen.format_dimension_2)) + ).map { newFormatWrapper(it) }, + R.styleable.Formats ) val actual = res.getDimensionPixelSize(R.dimen.format_dimension_2) assertEquals(actual, multi.getDimensionPixelSize(R.styleable.Formats_formatDimension)) @@ -144,11 +144,11 @@ class MultiTypedArrayWrapperTest { @Test fun getDrawable() { multi = MultiTypedArrayWrapper( - listOf( - mapOf(R.attr.formatReference to ResourceId(R.drawable.format_drawable)), - mapOf(R.attr.formatReference to ResourceId(R.drawable.format_drawable_2)) - ).map { newFormatWrapper(it) }, - R.styleable.Formats + listOf( + mapOf(R.attr.formatReference to ResourceId(R.drawable.format_drawable)), + mapOf(R.attr.formatReference to ResourceId(R.drawable.format_drawable_2)) + ).map { newFormatWrapper(it) }, + R.styleable.Formats ) val actual = res.getDrawable(R.drawable.format_drawable_2) assertEquals(actual.constantState, multi.getDrawable(R.styleable.Formats_formatReference)?.constantState) @@ -157,11 +157,11 @@ class MultiTypedArrayWrapperTest { @Test fun getFloat() { multi = MultiTypedArrayWrapper( - listOf( - mapOf(R.attr.formatFloat to ResourceId(R.dimen.format_float)), - mapOf(R.attr.formatFloat to ResourceId(R.dimen.format_float_2)) - ).map { newFormatWrapper(it) }, - R.styleable.Formats + listOf( + mapOf(R.attr.formatFloat to ResourceId(R.dimen.format_float)), + mapOf(R.attr.formatFloat to ResourceId(R.dimen.format_float_2)) + ).map { newFormatWrapper(it) }, + R.styleable.Formats ) val actual = res.getFloat(R.dimen.format_float_2) assertEquals(actual, multi.getFloat(R.styleable.Formats_formatFloat)) @@ -170,11 +170,11 @@ class MultiTypedArrayWrapperTest { @Test fun getFraction() { multi = MultiTypedArrayWrapper( - listOf( - mapOf(R.attr.formatFraction to ResourceId(R.fraction.format_fraction)), - mapOf(R.attr.formatFraction to ResourceId(R.fraction.format_fraction_2)) - ).map { newFormatWrapper(it) }, - R.styleable.Formats + listOf( + mapOf(R.attr.formatFraction to ResourceId(R.fraction.format_fraction)), + mapOf(R.attr.formatFraction to ResourceId(R.fraction.format_fraction_2)) + ).map { newFormatWrapper(it) }, + R.styleable.Formats ) val actual = res.getFraction(R.fraction.format_fraction_2, 1, 2) assertEquals(actual, multi.getFraction(R.styleable.Formats_formatFraction, 1, 2)) @@ -183,11 +183,11 @@ class MultiTypedArrayWrapperTest { @Test fun getInt() { multi = MultiTypedArrayWrapper( - listOf( - mapOf(R.attr.formatInteger to ResourceId(R.integer.format_integer)), - mapOf(R.attr.formatInteger to ResourceId(R.integer.format_integer_2)) - ).map { newFormatWrapper(it) }, - R.styleable.Formats + listOf( + mapOf(R.attr.formatInteger to ResourceId(R.integer.format_integer)), + mapOf(R.attr.formatInteger to ResourceId(R.integer.format_integer_2)) + ).map { newFormatWrapper(it) }, + R.styleable.Formats ) val actual = res.getInteger(R.integer.format_integer_2) assertEquals(actual, multi.getInt(R.styleable.Formats_formatInteger)) @@ -196,12 +196,12 @@ class MultiTypedArrayWrapperTest { @Test fun getLayoutDimension() { multi = MultiTypedArrayWrapper( - listOf( - mapOf(R.attr.formatDimension to ResourceId(R.dimen.format_layout_dimension_match_parent)), - mapOf(R.attr.formatDimension to ResourceId(R.dimen.format_layout_dimension_wrap_content)), - mapOf(R.attr.formatDimension to ResourceId(R.dimen.format_dimension)) - ).map { newFormatWrapper(it) }, - R.styleable.Formats + listOf( + mapOf(R.attr.formatDimension to ResourceId(R.dimen.format_layout_dimension_match_parent)), + mapOf(R.attr.formatDimension to ResourceId(R.dimen.format_layout_dimension_wrap_content)), + mapOf(R.attr.formatDimension to ResourceId(R.dimen.format_dimension)) + ).map { newFormatWrapper(it) }, + R.styleable.Formats ) val actual = res.getDimension(R.dimen.format_dimension).toInt() assertEquals(actual, multi.getLayoutDimension(R.styleable.Formats_formatDimension)) @@ -211,11 +211,11 @@ class MultiTypedArrayWrapperTest { fun getResourceId() { // Using R.string.format_string as an arbitrary resource multi = MultiTypedArrayWrapper( - listOf( - mapOf(R.attr.formatReference to ResourceId(R.string.format_string)), - mapOf(R.attr.formatReference to ResourceId(R.string.format_string_2)) - ).map { newFormatWrapper(it) }, - R.styleable.Formats + listOf( + mapOf(R.attr.formatReference to ResourceId(R.string.format_string)), + mapOf(R.attr.formatReference to ResourceId(R.string.format_string_2)) + ).map { newFormatWrapper(it) }, + R.styleable.Formats ) val actual = R.string.format_string_2 assertEquals(actual, multi.getResourceId(R.styleable.Formats_formatReference)) @@ -224,11 +224,11 @@ class MultiTypedArrayWrapperTest { @Test fun getString() { multi = MultiTypedArrayWrapper( - listOf( - mapOf(R.attr.formatString to ResourceId(R.string.format_string)), - mapOf(R.attr.formatString to ResourceId(R.string.format_string_2)) - ).map { newFormatWrapper(it) }, - R.styleable.Formats + listOf( + mapOf(R.attr.formatString to ResourceId(R.string.format_string)), + mapOf(R.attr.formatString to ResourceId(R.string.format_string_2)) + ).map { newFormatWrapper(it) }, + R.styleable.Formats ) val actual = res.getString(R.string.format_string_2) assertEquals(actual, multi.getString(R.styleable.Formats_formatString)) @@ -237,9 +237,9 @@ class MultiTypedArrayWrapperTest { @Test fun getStyle_resourceStyle_singleStyle() { multi = MultiTypedArrayWrapper( - listOf(mapOf(R.attr.formatReference to ResourceId(R.style.Green))) - .map { newFormatWrapper(it) }, - R.styleable.Formats + listOf(mapOf(R.attr.formatReference to ResourceId(R.style.Green))) + .map { newFormatWrapper(it) }, + R.styleable.Formats ) val actual = res.getStyle(R.style.Green) assertEquals(actual, multi.getStyle(R.styleable.Formats_formatReference)) @@ -248,11 +248,11 @@ class MultiTypedArrayWrapperTest { @Test fun getStyle_resourceStyle_multiStyle() { multi = MultiTypedArrayWrapper( - listOf( - mapOf(R.attr.formatReference to ResourceId(R.style.Green)), - mapOf(R.attr.formatReference to ResourceId(R.style.Red)) - ).map { newFormatWrapper(it) }, - R.styleable.Formats + listOf( + mapOf(R.attr.formatReference to ResourceId(R.style.Green)), + mapOf(R.attr.formatReference to ResourceId(R.style.Red)) + ).map { newFormatWrapper(it) }, + R.styleable.Formats ) val actual = MultiStyle("a_MultiTypedArrayWrapper_MultiStyle", listOf(res.getStyle(R.style.Green), res.getStyle(R.style.Red))) assertEquals(actual, multi.getStyle(R.styleable.Formats_formatReference)) @@ -261,15 +261,19 @@ class MultiTypedArrayWrapperTest { @Test fun getStyle_programmaticStyle() { multi = MultiTypedArrayWrapper( - listOf( - mapOf(R.attr.formatReference to ProgrammaticStyle.builder() - .put(R.attr.formatBoolean, true) - .build()), - mapOf(R.attr.formatReference to ProgrammaticStyle.builder() - .put(R.attr.formatString, "my string") - .build()) - ).map { newFormatWrapper(it) }, - R.styleable.Formats + listOf( + mapOf( + R.attr.formatReference to ProgrammaticStyle.builder() + .put(R.attr.formatBoolean, true) + .build() + ), + mapOf( + R.attr.formatReference to ProgrammaticStyle.builder() + .put(R.attr.formatString, "my string") + .build() + ) + ).map { newFormatWrapper(it) }, + R.styleable.Formats ) val subStyle = multi.getStyle(R.styleable.Formats_formatReference) val subTypedArray = subStyle.obtainStyledAttributes(context, R.styleable.Formats) @@ -281,15 +285,19 @@ class MultiTypedArrayWrapperTest { @Test fun getStyle_programmaticStyle_precedence() { multi = MultiTypedArrayWrapper( - listOf( - mapOf(R.attr.formatReference to ProgrammaticStyle.builder() - .put(R.attr.formatString, "string1") - .build()), - mapOf(R.attr.formatReference to ProgrammaticStyle.builder() - .put(R.attr.formatString, "string2") - .build()) - ).map { newFormatWrapper(it) }, - R.styleable.Formats + listOf( + mapOf( + R.attr.formatReference to ProgrammaticStyle.builder() + .put(R.attr.formatString, "string1") + .build() + ), + mapOf( + R.attr.formatReference to ProgrammaticStyle.builder() + .put(R.attr.formatString, "string2") + .build() + ) + ).map { newFormatWrapper(it) }, + R.styleable.Formats ) val subStyle = multi.getStyle(R.styleable.Formats_formatReference) val subTypedArray = subStyle.obtainStyledAttributes(context, R.styleable.Formats) @@ -300,11 +308,11 @@ class MultiTypedArrayWrapperTest { @Test fun getText() { multi = MultiTypedArrayWrapper( - listOf( - mapOf(R.attr.formatString to ResourceId(R.string.format_char_sequence)), - mapOf(R.attr.formatString to ResourceId(R.string.format_char_sequence_2)) - ).map { newFormatWrapper(it) }, - R.styleable.Formats + listOf( + mapOf(R.attr.formatString to ResourceId(R.string.format_char_sequence)), + mapOf(R.attr.formatString to ResourceId(R.string.format_char_sequence_2)) + ).map { newFormatWrapper(it) }, + R.styleable.Formats ) val actual = res.getText(R.string.format_char_sequence_2) assertEquals(actual, multi.getText(R.styleable.Formats_formatString)) @@ -313,11 +321,11 @@ class MultiTypedArrayWrapperTest { @Test fun getTextArray() { multi = MultiTypedArrayWrapper( - listOf( - mapOf(R.attr.formatReference to ResourceId(R.array.format_string_array)), - mapOf(R.attr.formatReference to ResourceId(R.array.format_string_array_2)) - ).map { newFormatWrapper(it) }, - R.styleable.Formats + listOf( + mapOf(R.attr.formatReference to ResourceId(R.array.format_string_array)), + mapOf(R.attr.formatReference to ResourceId(R.array.format_string_array_2)) + ).map { newFormatWrapper(it) }, + R.styleable.Formats ) val actual = res.getTextArray(R.array.format_string_array_2) assertEquals(actual, multi.getTextArray(R.styleable.Formats_formatReference)) diff --git a/paris/src/androidTest/java/com/airbnb/paris/StyleApplierTest.kt b/paris/src/androidTest/java/com/airbnb/paris/StyleApplierTest.kt index 5bac53a9..4481f1ee 100644 --- a/paris/src/androidTest/java/com/airbnb/paris/StyleApplierTest.kt +++ b/paris/src/androidTest/java/com/airbnb/paris/StyleApplierTest.kt @@ -1,14 +1,16 @@ package com.airbnb.paris -import android.content.* -import androidx.test.* -import androidx.test.runner.* -import android.view.* -import com.airbnb.paris.styles.* -import com.airbnb.paris.typed_array_wrappers.* -import org.junit.* -import org.junit.Assert.* -import org.junit.runner.* +import android.content.Context +import android.view.View +import androidx.test.InstrumentationRegistry +import androidx.test.runner.AndroidJUnit4 +import com.airbnb.paris.styles.Style +import com.airbnb.paris.typed_array_wrappers.TypedArrayWrapper +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class StyleApplierTest { @@ -17,7 +19,7 @@ class StyleApplierTest { private lateinit var context: Context private lateinit var view: View - private lateinit var applier : TestStyleApplier; + private lateinit var applier: TestStyleApplier; private fun newView() = View(context) diff --git a/paris/src/androidTest/java/com/airbnb/paris/StyleApplierUtilsTest.kt b/paris/src/androidTest/java/com/airbnb/paris/StyleApplierUtilsTest.kt index 0abed4b8..de830d3a 100644 --- a/paris/src/androidTest/java/com/airbnb/paris/StyleApplierUtilsTest.kt +++ b/paris/src/androidTest/java/com/airbnb/paris/StyleApplierUtilsTest.kt @@ -1,15 +1,18 @@ package com.airbnb.paris -import android.graphics.* -import androidx.test.* -import androidx.test.runner.* -import android.util.* -import android.widget.* -import com.airbnb.paris.styles.* +import android.graphics.Color +import android.util.TypedValue +import android.widget.TextView +import android.widget.TextViewStyleApplier +import androidx.test.InstrumentationRegistry +import androidx.test.runner.AndroidJUnit4 +import com.airbnb.paris.styles.MultiStyle +import com.airbnb.paris.styles.ResourceStyle import com.airbnb.paris.test.R -import org.junit.* -import org.junit.Assert.* -import org.junit.runner.* +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class StyleApplierUtilsTest { @@ -32,141 +35,166 @@ class StyleApplierUtilsTest { @Test fun sameStyle() { - StyleApplierUtils.assertSameAttributes(textViewApplier, - ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_1), - ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_1)) + StyleApplierUtils.assertSameAttributes( + textViewApplier, + ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_1), + ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_1) + ) } @Test fun sameAttributes() { - StyleApplierUtils.assertSameAttributes(textViewApplier, - ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_1), - ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_2)) + StyleApplierUtils.assertSameAttributes( + textViewApplier, + ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_1), + ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_2) + ) } @Test(expected = AssertionError::class) fun differentAttributes() { - StyleApplierUtils.assertSameAttributes(textViewApplier, - ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_1), - ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textSizePadding)) + StyleApplierUtils.assertSameAttributes( + textViewApplier, + ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_1), + ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textSizePadding) + ) } @Test fun inheritance() { - StyleApplierUtils.assertSameAttributes(textViewApplier, - ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_1), - ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_withInheritance)) + StyleApplierUtils.assertSameAttributes( + textViewApplier, + ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_1), + ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_withInheritance) + ) } @Test fun multiStyleResCombinationNoOverlap() { - StyleApplierUtils.assertSameAttributes(textViewApplier, - ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_1), - MultiStyle("MultiStyle_textColorTextSizePadding", - R.style.StyleApplierUtilsTest_TextView_textColor, - R.style.StyleApplierUtilsTest_TextView_textSizePadding - ) + StyleApplierUtils.assertSameAttributes( + textViewApplier, + ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_1), + MultiStyle( + "MultiStyle_textColorTextSizePadding", + R.style.StyleApplierUtilsTest_TextView_textColor, + R.style.StyleApplierUtilsTest_TextView_textSizePadding + ) ) } @Test fun multiStyleResCombinationWithOverlap() { - StyleApplierUtils.assertSameAttributes(textViewApplier, - ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_1), - MultiStyle("MultiStyle_textColorTextSizePadding", - R.style.StyleApplierUtilsTest_TextView_textColorTextSize, - R.style.StyleApplierUtilsTest_TextView_textSizePadding - ) + StyleApplierUtils.assertSameAttributes( + textViewApplier, + ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_1), + MultiStyle( + "MultiStyle_textColorTextSizePadding", + R.style.StyleApplierUtilsTest_TextView_textColorTextSize, + R.style.StyleApplierUtilsTest_TextView_textSizePadding + ) ) } @Test(expected = AssertionError::class) fun multiStyleResCombinationMissingAttribute() { - StyleApplierUtils.assertSameAttributes(textViewApplier, - ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_1), - MultiStyle("MultiStyle_textColorTextSize", - R.style.StyleApplierUtilsTest_TextView_textColor, - R.style.StyleApplierUtilsTest_TextView_textSize - ) + StyleApplierUtils.assertSameAttributes( + textViewApplier, + ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_1), + MultiStyle( + "MultiStyle_textColorTextSize", + R.style.StyleApplierUtilsTest_TextView_textColor, + R.style.StyleApplierUtilsTest_TextView_textSize + ) ) } @Test(expected = AssertionError::class) fun multiStyleResCombinationAdditionalAttribute() { - StyleApplierUtils.assertSameAttributes(textViewApplier, - ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSize), - MultiStyle("MultiStyle_textColorTextSizePadding", - R.style.StyleApplierUtilsTest_TextView_textColorTextSize, - R.style.StyleApplierUtilsTest_TextView_textSizePadding - ) + StyleApplierUtils.assertSameAttributes( + textViewApplier, + ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSize), + MultiStyle( + "MultiStyle_textColorTextSizePadding", + R.style.StyleApplierUtilsTest_TextView_textColorTextSize, + R.style.StyleApplierUtilsTest_TextView_textSizePadding + ) ) } @Test fun multiStyleCombinationWithOverlap() { - StyleApplierUtils.assertSameAttributes(textViewApplier, - ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_1), - MultiStyle("MultiStyle_textColorTextSizePadding", - ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSize), - textViewApplier.builder() - .textSize(R.dimen.test_text_view_text_size) - .padding(R.dimen.test_view_padding) - .build() - ) + StyleApplierUtils.assertSameAttributes( + textViewApplier, + ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_1), + MultiStyle( + "MultiStyle_textColorTextSizePadding", + ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSize), + textViewApplier.builder() + .textSize(R.dimen.test_text_view_text_size) + .padding(R.dimen.test_view_padding) + .build() + ) ) } @Test fun multiStyleCombinationNoOverlap() { - StyleApplierUtils.assertSameAttributes(textViewApplier, - ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_1), - MultiStyle("MultiStyle_textColorTextSizePadding", - ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColor), - textViewApplier.builder() - .textSize(R.dimen.test_text_view_text_size) - .padding(R.dimen.test_view_padding) - .build() - ) + StyleApplierUtils.assertSameAttributes( + textViewApplier, + ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_1), + MultiStyle( + "MultiStyle_textColorTextSizePadding", + ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColor), + textViewApplier.builder() + .textSize(R.dimen.test_text_view_text_size) + .padding(R.dimen.test_view_padding) + .build() + ) ) } @Test(expected = AssertionError::class) fun multiStyleCombinationMissingAttribute() { - StyleApplierUtils.assertSameAttributes(textViewApplier, - ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_1), - MultiStyle("MultiStyle_textColorTextSize", - ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColor), - textViewApplier.builder().textSize(R.dimen.test_text_view_text_size).build() - ) + StyleApplierUtils.assertSameAttributes( + textViewApplier, + ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSizePadding_1), + MultiStyle( + "MultiStyle_textColorTextSize", + ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColor), + textViewApplier.builder().textSize(R.dimen.test_text_view_text_size).build() + ) ) } @Test(expected = AssertionError::class) fun multiStyleCombinationAdditionalAttribute() { - StyleApplierUtils.assertSameAttributes(textViewApplier, - ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSize), - MultiStyle("MultiStyle_textColorTextSizePadding", - ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColor), - textViewApplier.builder() - .textSize(R.dimen.test_text_view_text_size) - .padding(R.dimen.test_view_padding) - .build() - ) + StyleApplierUtils.assertSameAttributes( + textViewApplier, + ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColorTextSize), + MultiStyle( + "MultiStyle_textColorTextSizePadding", + ResourceStyle(R.style.StyleApplierUtilsTest_TextView_textColor), + textViewApplier.builder() + .textSize(R.dimen.test_text_view_text_size) + .padding(R.dimen.test_view_padding) + .build() + ) ) } @Test(expected = AssertionError::class) fun missingFromBoth() { - StyleApplierUtils.assertSameAttributes(textViewApplier, - textViewApplier.builder() - .textSize(R.dimen.test_text_view_text_size) - .build(), - textViewApplier.builder() - .padding(R.dimen.test_view_padding) - .build(), - textViewApplier.builder() - .textColor(Color.GREEN) - .build() + StyleApplierUtils.assertSameAttributes( + textViewApplier, + textViewApplier.builder() + .textSize(R.dimen.test_text_view_text_size) + .build(), + textViewApplier.builder() + .padding(R.dimen.test_view_padding) + .build(), + textViewApplier.builder() + .textColor(Color.GREEN) + .build() ) } @@ -177,13 +205,14 @@ class StyleApplierUtilsTest { textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, 10f) assertEquals(10f, textView.textSize) - StyleApplierUtils.assertSameAttributes(textViewApplier, - textViewApplier.builder() - .textSize(15) - .build(), - textViewApplier.builder() - .textSize(15) - .build() + StyleApplierUtils.assertSameAttributes( + textViewApplier, + textViewApplier.builder() + .textSize(15) + .build(), + textViewApplier.builder() + .textSize(15) + .build() ) assertEquals(10f, textView.textSize) } diff --git a/paris/src/androidTest/java/com/airbnb/paris/proxies/BaseViewMappings.kt b/paris/src/androidTest/java/com/airbnb/paris/proxies/BaseViewMappings.kt index 99df6604..d047afa3 100644 --- a/paris/src/androidTest/java/com/airbnb/paris/proxies/BaseViewMappings.kt +++ b/paris/src/androidTest/java/com/airbnb/paris/proxies/BaseViewMappings.kt @@ -1,21 +1,23 @@ package com.airbnb.paris.proxies -import android.content.res.* -import android.graphics.* -import android.graphics.drawable.* +import android.content.res.ColorStateList +import android.graphics.Color +import android.graphics.drawable.ColorDrawable internal val ARBITRARY_COLORS = listOf(Color.GREEN, Color.GREEN, Color.BLUE, Color.TRANSPARENT) internal val ARBITRARY_COLOR_STATE_LISTS = listOf( - ColorStateList(arrayOf( - intArrayOf(android.R.attr.state_enabled), - intArrayOf(-android.R.attr.state_enabled) - ), intArrayOf(Color.RED, Color.GREEN)) + ColorStateList( + arrayOf( + intArrayOf(android.R.attr.state_enabled), + intArrayOf(-android.R.attr.state_enabled) + ), intArrayOf(Color.RED, Color.GREEN) + ) ) internal val ARBITRARY_COLOR_DRAWABLES = listOf( - ColorDrawable(Color.RED), - ColorDrawable(Color.GREEN), - ColorDrawable(Color.BLUE) + ColorDrawable(Color.RED), + ColorDrawable(Color.GREEN), + ColorDrawable(Color.BLUE) ) internal val ARBITRARY_DIMENSIONS = listOf(Integer.MIN_VALUE, -150, 0, 10, 20, 50, 200, 800, Integer.MAX_VALUE) internal val ARBITRARY_FLOATS = listOf(-5f, 0f, 8f, 10f, 11.5f, 17f) @@ -27,13 +29,14 @@ internal val BOOLS = listOf(true, false) // TODO What about Dp and ColorInt methods? internal open class BaseViewMapping, View : android.view.View, Input : Any> protected constructor( - val testValues: List, - val attrRes: Int, - val setProxyFunction: Proxy.(Input) -> Unit, - val setStyleBuilderValueFunction: Builder.(Input) -> Any, - val setStyleBuilderResFunction: Builder.(Int) -> Any, - /** - * A function which, when called, will assert that the view has been successfully modified - * by the associated proxy and/or style builder methods - */ - val assertViewSet: (View, Input) -> Unit) \ No newline at end of file + val testValues: List, + val attrRes: Int, + val setProxyFunction: Proxy.(Input) -> Unit, + val setStyleBuilderValueFunction: Builder.(Input) -> Any, + val setStyleBuilderResFunction: Builder.(Int) -> Any, + /** + * A function which, when called, will assert that the view has been successfully modified + * by the associated proxy and/or style builder methods + */ + val assertViewSet: (View, Input) -> Unit +) \ No newline at end of file diff --git a/paris/src/androidTest/java/com/airbnb/paris/proxies/DeprecatedTextViewProxyTest.kt b/paris/src/androidTest/java/com/airbnb/paris/proxies/DeprecatedTextViewProxyTest.kt index b5332041..7164bf1d 100644 --- a/paris/src/androidTest/java/com/airbnb/paris/proxies/DeprecatedTextViewProxyTest.kt +++ b/paris/src/androidTest/java/com/airbnb/paris/proxies/DeprecatedTextViewProxyTest.kt @@ -4,9 +4,9 @@ import android.content.Context import android.content.res.Resources import android.graphics.Color import android.graphics.drawable.ColorDrawable +import android.widget.TextView import androidx.test.InstrumentationRegistry import androidx.test.runner.AndroidJUnit4 -import android.widget.TextView import org.junit.Assert.assertEquals import org.junit.Assert.assertNull import org.junit.Before diff --git a/paris/src/androidTest/java/com/airbnb/paris/proxies/DeprecatedTextViewStyleApplierTest.kt b/paris/src/androidTest/java/com/airbnb/paris/proxies/DeprecatedTextViewStyleApplierTest.kt index cf11fb90..88306089 100644 --- a/paris/src/androidTest/java/com/airbnb/paris/proxies/DeprecatedTextViewStyleApplierTest.kt +++ b/paris/src/androidTest/java/com/airbnb/paris/proxies/DeprecatedTextViewStyleApplierTest.kt @@ -25,11 +25,12 @@ class DeprecatedTextViewStyleApplierTest { private lateinit var styleBuilder: StyleBuilder private fun apply(builderFunctions: StyleBuilder.() -> StyleBuilder) = - TextViewStyleApplier(view).apply( - StyleBuilder() - .debugName("test") - .builderFunctions() - .build()) + TextViewStyleApplier(view).apply( + StyleBuilder() + .debugName("test") + .builderFunctions() + .build() + ) @Before fun setup() { diff --git a/paris/src/androidTest/java/com/airbnb/paris/proxies/DeprecatedViewStyleApplierTest.kt b/paris/src/androidTest/java/com/airbnb/paris/proxies/DeprecatedViewStyleApplierTest.kt index dda5f877..ca3c8f16 100644 --- a/paris/src/androidTest/java/com/airbnb/paris/proxies/DeprecatedViewStyleApplierTest.kt +++ b/paris/src/androidTest/java/com/airbnb/paris/proxies/DeprecatedViewStyleApplierTest.kt @@ -1,16 +1,21 @@ package com.airbnb.paris.proxies -import android.content.* -import android.content.res.* -import androidx.test.* -import androidx.test.runner.* -import android.view.* -import android.widget.* -import android.view.ViewStyleApplier.* +import android.content.Context +import android.content.res.Resources +import android.view.View +import android.view.ViewGroup +import android.view.ViewStyleApplier +import android.view.ViewStyleApplier.StyleBuilder +import android.widget.FrameLayout +import androidx.test.InstrumentationRegistry +import androidx.test.runner.AndroidJUnit4 import com.airbnb.paris.test.R -import org.junit.* -import org.junit.Assert.* -import org.junit.runner.* +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class DeprecatedViewStyleApplierTest { @@ -70,10 +75,10 @@ class DeprecatedViewStyleApplierTest { assertNull(params) ViewStyleApplier(view) - .builder() - .ignoreLayoutWidthAndHeight(true) - .add(R.style.Test_View_width_height) - .apply() + .builder() + .ignoreLayoutWidthAndHeight(true) + .add(R.style.Test_View_width_height) + .apply() params = view.layoutParams assertNull(params) diff --git a/paris/src/androidTest/java/com/airbnb/paris/proxies/GenericViewProxyTests.kt b/paris/src/androidTest/java/com/airbnb/paris/proxies/GenericViewProxyTests.kt index cca34dea..e8311ed9 100644 --- a/paris/src/androidTest/java/com/airbnb/paris/proxies/GenericViewProxyTests.kt +++ b/paris/src/androidTest/java/com/airbnb/paris/proxies/GenericViewProxyTests.kt @@ -1,13 +1,13 @@ package com.airbnb.paris.proxies import android.content.Context -import androidx.test.InstrumentationRegistry -import androidx.test.runner.AndroidJUnit4 import android.view.View import android.view.ViewGroup import android.widget.FrameLayout import android.widget.ImageView import android.widget.TextView +import androidx.test.InstrumentationRegistry +import androidx.test.runner.AndroidJUnit4 import org.junit.Test import org.junit.runner.RunWith @@ -31,7 +31,10 @@ class GenericViewProxyTests { assertProxySet(IMAGE_VIEW_MAPPINGS as List>) } - private inline fun , reified V : View> assertProxySet(mappings: List>, setups: List<(V) -> Unit> = listOf({ _ -> })) { + private inline fun , reified V : View> assertProxySet( + mappings: List>, + setups: List<(V) -> Unit> = listOf({ _ -> }) + ) { for (setup in setups) { for (mapping in mappings) { assertProxySet(mapping, setup) @@ -39,7 +42,10 @@ class GenericViewProxyTests { } } - private inline fun , reified V : View> assertProxySet(mapping: BaseViewMapping, noinline setup: (V) -> Unit) { + private inline fun , reified V : View> assertProxySet( + mapping: BaseViewMapping, + noinline setup: (V) -> Unit + ) { val view: V if (V::class == ViewGroup::class) { // ViewGroup is abstract so we can't instantiate it diff --git a/paris/src/androidTest/java/com/airbnb/paris/proxies/ImageViewMappings.kt b/paris/src/androidTest/java/com/airbnb/paris/proxies/ImageViewMappings.kt index 9fb9aeaa..3706dd8e 100644 --- a/paris/src/androidTest/java/com/airbnb/paris/proxies/ImageViewMappings.kt +++ b/paris/src/androidTest/java/com/airbnb/paris/proxies/ImageViewMappings.kt @@ -1,60 +1,66 @@ package com.airbnb.paris.proxies -import android.widget.* -import android.widget.ImageView.* -import java.util.* +import android.widget.ImageView +import android.widget.ImageView.ScaleType +import android.widget.ImageViewStyleApplier +import java.util.ArrayList internal class ImageViewMapping private constructor( - testValues: List, - attrRes: Int, - setProxyFunction: ImageViewProxy.(I) -> Unit, - setStyleBuilderValueFunction: ImageViewStyleApplier.StyleBuilder.(I) -> Any, - setStyleBuilderResFunction: ImageViewStyleApplier.StyleBuilder.(Int) -> Any, - /** - * A function which, when called, will assert that the view has been successfully modified - * by the associated proxy and/or style builder methods - */ - assertViewSet: (ImageView, I) -> Unit) : - BaseViewMapping( - testValues, - attrRes, - setProxyFunction, - setStyleBuilderValueFunction, - setStyleBuilderResFunction, - assertViewSet) { + testValues: List, + attrRes: Int, + setProxyFunction: ImageViewProxy.(I) -> Unit, + setStyleBuilderValueFunction: ImageViewStyleApplier.StyleBuilder.(I) -> Any, + setStyleBuilderResFunction: ImageViewStyleApplier.StyleBuilder.(Int) -> Any, + /** + * A function which, when called, will assert that the view has been successfully modified + * by the associated proxy and/or style builder methods + */ + assertViewSet: (ImageView, I) -> Unit +) : + BaseViewMapping( + testValues, + attrRes, + setProxyFunction, + setStyleBuilderValueFunction, + setStyleBuilderResFunction, + assertViewSet + ) { companion object { fun withCustomAssert( - testValues: List, - attrRes: Int, - setProxyFunction: ImageViewProxy.(I) -> Unit, - setStyleBuilderValueFunction: ImageViewStyleApplier.StyleBuilder.(I) -> Any, - setStyleBuilderResFunction: ImageViewStyleApplier.StyleBuilder.(Int) -> Any, - assertViewSet: (ImageView, I) -> Unit): ImageViewMapping { + testValues: List, + attrRes: Int, + setProxyFunction: ImageViewProxy.(I) -> Unit, + setStyleBuilderValueFunction: ImageViewStyleApplier.StyleBuilder.(I) -> Any, + setStyleBuilderResFunction: ImageViewStyleApplier.StyleBuilder.(Int) -> Any, + assertViewSet: (ImageView, I) -> Unit + ): ImageViewMapping { return ImageViewMapping( - testValues, - attrRes, - setProxyFunction, - setStyleBuilderValueFunction, - setStyleBuilderResFunction, - assertViewSet) + testValues, + attrRes, + setProxyFunction, + setStyleBuilderValueFunction, + setStyleBuilderResFunction, + assertViewSet + ) } fun withAssertEquals( - testValues: List, - attrRes: Int, - setProxyFunction: ImageViewProxy.(I) -> Unit, - setStyleBuilderValueFunction: ImageViewStyleApplier.StyleBuilder.(I) -> Any, - setStyleBuilderResFunction: ImageViewStyleApplier.StyleBuilder.(Int) -> Any, - getViewFunction: (ImageView) -> I): ImageViewMapping { + testValues: List, + attrRes: Int, + setProxyFunction: ImageViewProxy.(I) -> Unit, + setStyleBuilderValueFunction: ImageViewStyleApplier.StyleBuilder.(I) -> Any, + setStyleBuilderResFunction: ImageViewStyleApplier.StyleBuilder.(Int) -> Any, + getViewFunction: (ImageView) -> I + ): ImageViewMapping { return withCustomAssert( - testValues, - attrRes, - setProxyFunction, - setStyleBuilderValueFunction, - setStyleBuilderResFunction, - { ImageView, input -> junit.framework.Assert.assertEquals(input, getViewFunction(ImageView)) }) + testValues, + attrRes, + setProxyFunction, + setStyleBuilderValueFunction, + setStyleBuilderResFunction, + { ImageView, input -> junit.framework.Assert.assertEquals(input, getViewFunction(ImageView)) }) } } } @@ -63,42 +69,42 @@ internal val IMAGE_VIEW_MAPPINGS = ArrayList>().apply { // scaleType add(ImageViewMapping.withAssertEquals( - (0 until ScaleType.values().size).toList(), - android.R.attr.scaleType, - ImageViewProxy::setScaleType, - ImageViewStyleApplier.StyleBuilder::scaleType, - ImageViewStyleApplier.StyleBuilder::scaleTypeRes, - { - listOf( - ScaleType.MATRIX, - ScaleType.FIT_XY, - ScaleType.FIT_START, - ScaleType.FIT_CENTER, - ScaleType.FIT_END, - ScaleType.CENTER, - ScaleType.CENTER_CROP, - ScaleType.CENTER_INSIDE - ).indexOf(it.scaleType) - } + (0 until ScaleType.values().size).toList(), + android.R.attr.scaleType, + ImageViewProxy::setScaleType, + ImageViewStyleApplier.StyleBuilder::scaleType, + ImageViewStyleApplier.StyleBuilder::scaleTypeRes, + { + listOf( + ScaleType.MATRIX, + ScaleType.FIT_XY, + ScaleType.FIT_START, + ScaleType.FIT_CENTER, + ScaleType.FIT_END, + ScaleType.CENTER, + ScaleType.CENTER_CROP, + ScaleType.CENTER_INSIDE + ).indexOf(it.scaleType) + } )) // tint add(ImageViewMapping.withAssertEquals( - ARBITRARY_COLOR_STATE_LISTS, - android.R.attr.tint, - ImageViewProxy::setTint, - ImageViewStyleApplier.StyleBuilder::tint, - ImageViewStyleApplier.StyleBuilder::tintRes, - { it.imageTintList } + ARBITRARY_COLOR_STATE_LISTS, + android.R.attr.tint, + ImageViewProxy::setTint, + ImageViewStyleApplier.StyleBuilder::tint, + ImageViewStyleApplier.StyleBuilder::tintRes, + { it.imageTintList } )) // src add(ImageViewMapping.withAssertEquals( - ARBITRARY_COLOR_DRAWABLES, - android.R.attr.src, - ImageViewProxy::setSrc, - ImageViewStyleApplier.StyleBuilder::src, - ImageViewStyleApplier.StyleBuilder::srcRes, - { it.drawable } + ARBITRARY_COLOR_DRAWABLES, + android.R.attr.src, + ImageViewProxy::setSrc, + ImageViewStyleApplier.StyleBuilder::src, + ImageViewStyleApplier.StyleBuilder::srcRes, + { it.drawable } )) } diff --git a/paris/src/androidTest/java/com/airbnb/paris/proxies/ImageViewStyleApplierTest.kt b/paris/src/androidTest/java/com/airbnb/paris/proxies/ImageViewStyleApplierTest.kt index 8a2f497d..041b02d5 100644 --- a/paris/src/androidTest/java/com/airbnb/paris/proxies/ImageViewStyleApplierTest.kt +++ b/paris/src/androidTest/java/com/airbnb/paris/proxies/ImageViewStyleApplierTest.kt @@ -1,11 +1,13 @@ package com.airbnb.paris.proxies -import androidx.test.* -import androidx.test.runner.* -import android.widget.* -import android.widget.ImageViewStyleApplier.* -import org.junit.* -import org.junit.runner.* +import android.widget.ImageView +import android.widget.ImageViewStyleApplier +import android.widget.ImageViewStyleApplier.StyleBuilder +import androidx.test.InstrumentationRegistry +import androidx.test.runner.AndroidJUnit4 +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class ImageViewStyleApplierTest { diff --git a/paris/src/androidTest/java/com/airbnb/paris/proxies/ImageViewStyleApplier_StyleBuilderTest.kt b/paris/src/androidTest/java/com/airbnb/paris/proxies/ImageViewStyleApplier_StyleBuilderTest.kt index 0245c2ab..002281ff 100644 --- a/paris/src/androidTest/java/com/airbnb/paris/proxies/ImageViewStyleApplier_StyleBuilderTest.kt +++ b/paris/src/androidTest/java/com/airbnb/paris/proxies/ImageViewStyleApplier_StyleBuilderTest.kt @@ -1,11 +1,13 @@ package com.airbnb.paris.proxies -import androidx.test.runner.* -import android.widget.* -import android.widget.ImageViewStyleApplier.* -import com.airbnb.paris.styles.* -import org.junit.* -import org.junit.runner.* +import android.widget.ImageView +import android.widget.ImageViewStyleApplier.StyleBuilder +import androidx.test.runner.AndroidJUnit4 +import com.airbnb.paris.styles.ProgrammaticStyle +import org.junit.Assert +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class ImageViewStyleApplier_StyleBuilderTest { diff --git a/paris/src/androidTest/java/com/airbnb/paris/proxies/TextViewMappings.kt b/paris/src/androidTest/java/com/airbnb/paris/proxies/TextViewMappings.kt index ce88699b..39d991c9 100644 --- a/paris/src/androidTest/java/com/airbnb/paris/proxies/TextViewMappings.kt +++ b/paris/src/androidTest/java/com/airbnb/paris/proxies/TextViewMappings.kt @@ -1,69 +1,74 @@ package com.airbnb.paris.proxies -import android.* -import android.graphics.* -import android.graphics.drawable.* -import android.os.* -import android.text.* -import android.view.* -import android.widget.* +import android.R +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.os.Build +import android.text.TextUtils +import android.view.Gravity +import android.widget.TextView +import android.widget.TextViewStyleApplier.StyleBuilder import com.airbnb.paris.proxies.TextViewMapping.Companion.withAssertEquals import com.airbnb.paris.proxies.TextViewMapping.Companion.withCustomAssert -import android.widget.TextViewStyleApplier.* -import junit.framework.Assert.* -import org.junit.* +import junit.framework.Assert.assertEquals +import org.junit.Assert internal class TextViewMapping private constructor( - testValues: List, - attrRes: Int, - setProxyFunction: TextViewProxy.(I) -> Unit, - setStyleBuilderValueFunction: StyleBuilder.(I) -> Any, - setStyleBuilderResFunction: StyleBuilder.(Int) -> Any, - /** - * A function which, when called, will assert that the view has been successfully modified - * by the associated proxy and/or style builder methods - */ - assertViewSet: (TextView, I) -> Unit) : - BaseViewMapping( - testValues, - attrRes, - setProxyFunction, - setStyleBuilderValueFunction, - setStyleBuilderResFunction, - assertViewSet) { + testValues: List, + attrRes: Int, + setProxyFunction: TextViewProxy.(I) -> Unit, + setStyleBuilderValueFunction: StyleBuilder.(I) -> Any, + setStyleBuilderResFunction: StyleBuilder.(Int) -> Any, + /** + * A function which, when called, will assert that the view has been successfully modified + * by the associated proxy and/or style builder methods + */ + assertViewSet: (TextView, I) -> Unit +) : + BaseViewMapping( + testValues, + attrRes, + setProxyFunction, + setStyleBuilderValueFunction, + setStyleBuilderResFunction, + assertViewSet + ) { companion object { fun withCustomAssert( - testValues: List, - attrRes: Int, - setProxyFunction: TextViewProxy.(I) -> Unit, - setStyleBuilderValueFunction: StyleBuilder.(I) -> Any, - setStyleBuilderResFunction: StyleBuilder.(Int) -> Any, - assertViewSet: (TextView, I) -> Unit): TextViewMapping { + testValues: List, + attrRes: Int, + setProxyFunction: TextViewProxy.(I) -> Unit, + setStyleBuilderValueFunction: StyleBuilder.(I) -> Any, + setStyleBuilderResFunction: StyleBuilder.(Int) -> Any, + assertViewSet: (TextView, I) -> Unit + ): TextViewMapping { return TextViewMapping( - testValues, - attrRes, - setProxyFunction, - setStyleBuilderValueFunction, - setStyleBuilderResFunction, - assertViewSet) + testValues, + attrRes, + setProxyFunction, + setStyleBuilderValueFunction, + setStyleBuilderResFunction, + assertViewSet + ) } fun withAssertEquals( - testValues: List, - attrRes: Int, - setProxyFunction: TextViewProxy.(I) -> Unit, - setStyleBuilderValueFunction: StyleBuilder.(I) -> Any, - setStyleBuilderResFunction: StyleBuilder.(Int) -> Any, - getViewFunction: (TextView) -> I): TextViewMapping { + testValues: List, + attrRes: Int, + setProxyFunction: TextViewProxy.(I) -> Unit, + setStyleBuilderValueFunction: StyleBuilder.(I) -> Any, + setStyleBuilderResFunction: StyleBuilder.(Int) -> Any, + getViewFunction: (TextView) -> I + ): TextViewMapping { return withCustomAssert( - testValues, - attrRes, - setProxyFunction, - setStyleBuilderValueFunction, - setStyleBuilderResFunction, - { textView, input -> assertEquals(input, getViewFunction(textView)) }) + testValues, + attrRes, + setProxyFunction, + setStyleBuilderValueFunction, + setStyleBuilderResFunction, + { textView, input -> assertEquals(input, getViewFunction(textView)) }) } } } @@ -72,200 +77,204 @@ internal val TEXT_VIEW_MAPPINGS = ArrayList>().apply { // drawableBottom add(withAssertEquals( - listOf(ColorDrawable(Color.GREEN)), - android.R.attr.drawableBottom, - TextViewProxy::setDrawableBottom, - StyleBuilder::drawableBottom, - StyleBuilder::drawableBottomRes, - { it.compoundDrawables[3] } + listOf(ColorDrawable(Color.GREEN)), + android.R.attr.drawableBottom, + TextViewProxy::setDrawableBottom, + StyleBuilder::drawableBottom, + StyleBuilder::drawableBottomRes, + { it.compoundDrawables[3] } )) // drawableLeft add(withAssertEquals( - listOf(ColorDrawable(Color.GREEN)), - android.R.attr.drawableLeft, - TextViewProxy::setDrawableLeft, - StyleBuilder::drawableLeft, - StyleBuilder::drawableLeftRes, - { it.compoundDrawables[0] } + listOf(ColorDrawable(Color.GREEN)), + android.R.attr.drawableLeft, + TextViewProxy::setDrawableLeft, + StyleBuilder::drawableLeft, + StyleBuilder::drawableLeftRes, + { it.compoundDrawables[0] } )) // drawableRight add(withAssertEquals( - listOf(ColorDrawable(Color.GREEN)), - android.R.attr.drawableRight, - TextViewProxy::setDrawableRight, - StyleBuilder::drawableRight, - StyleBuilder::drawableRightRes, - { it.compoundDrawables[2] } + listOf(ColorDrawable(Color.GREEN)), + android.R.attr.drawableRight, + TextViewProxy::setDrawableRight, + StyleBuilder::drawableRight, + StyleBuilder::drawableRightRes, + { it.compoundDrawables[2] } )) // drawableTop add(withAssertEquals( - listOf(ColorDrawable(Color.GREEN)), - android.R.attr.drawableTop, - TextViewProxy::setDrawableTop, - StyleBuilder::drawableTop, - StyleBuilder::drawableTopRes, - { it.compoundDrawables[1] } + listOf(ColorDrawable(Color.GREEN)), + android.R.attr.drawableTop, + TextViewProxy::setDrawableTop, + StyleBuilder::drawableTop, + StyleBuilder::drawableTopRes, + { it.compoundDrawables[1] } )) // ellipsize add(withAssertEquals( - (1..4).toList(), - android.R.attr.ellipsize, - TextViewProxy::setEllipsize, - StyleBuilder::ellipsize, - StyleBuilder::ellipsizeRes, - { mapOf(TextUtils.TruncateAt.START to 1, - TextUtils.TruncateAt.MIDDLE to 2, - TextUtils.TruncateAt.END to 3, - TextUtils.TruncateAt.MARQUEE to 4)[it.ellipsize]!! - } + (1..4).toList(), + android.R.attr.ellipsize, + TextViewProxy::setEllipsize, + StyleBuilder::ellipsize, + StyleBuilder::ellipsizeRes, + { + mapOf( + TextUtils.TruncateAt.START to 1, + TextUtils.TruncateAt.MIDDLE to 2, + TextUtils.TruncateAt.END to 3, + TextUtils.TruncateAt.MARQUEE to 4 + )[it.ellipsize]!! + } )) // gravity add(withCustomAssert( - listOf( - Gravity.BOTTOM, - Gravity.CENTER, - Gravity.CENTER_VERTICAL, - Gravity.START, - Gravity.TOP), - android.R.attr.gravity, - TextViewProxy::setGravity, - StyleBuilder::gravity, - StyleBuilder::gravityRes, - { view, input -> - assertEquals(input, view.gravity and input) - } + listOf( + Gravity.BOTTOM, + Gravity.CENTER, + Gravity.CENTER_VERTICAL, + Gravity.START, + Gravity.TOP + ), + android.R.attr.gravity, + TextViewProxy::setGravity, + StyleBuilder::gravity, + StyleBuilder::gravityRes, + { view, input -> + assertEquals(input, view.gravity and input) + } )) // letterSpacing if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { add(withAssertEquals( - ARBITRARY_FLOATS, - android.R.attr.letterSpacing, - TextViewProxy::setLetterSpacing, - StyleBuilder::letterSpacing, - StyleBuilder::letterSpacingRes, - { it.letterSpacing } + ARBITRARY_FLOATS, + android.R.attr.letterSpacing, + TextViewProxy::setLetterSpacing, + StyleBuilder::letterSpacing, + StyleBuilder::letterSpacingRes, + { it.letterSpacing } )) } // lines add(withCustomAssert( - ARBITRARY_INTS, - R.attr.lines, - TextViewProxy::setLines, - StyleBuilder::lines, - StyleBuilder::linesRes, - { view, input -> - assertEquals(input, view.minLines) - assertEquals(input, view.maxLines) - } + ARBITRARY_INTS, + R.attr.lines, + TextViewProxy::setLines, + StyleBuilder::lines, + StyleBuilder::linesRes, + { view, input -> + assertEquals(input, view.minLines) + assertEquals(input, view.maxLines) + } )) // lineSpacingExtra add(withAssertEquals( - ARBITRARY_INTS, - R.attr.lineSpacingExtra, - TextViewProxy::setLineSpacingExtra, - StyleBuilder::lineSpacingExtra, - StyleBuilder::lineSpacingExtraRes, - { it.lineSpacingExtra.toInt() } + ARBITRARY_INTS, + R.attr.lineSpacingExtra, + TextViewProxy::setLineSpacingExtra, + StyleBuilder::lineSpacingExtra, + StyleBuilder::lineSpacingExtraRes, + { it.lineSpacingExtra.toInt() } )) // lineSpacingMultiplier add(withAssertEquals( - ARBITRARY_FLOATS, - R.attr.lineSpacingMultiplier, - TextViewProxy::setLineSpacingMultiplier, - StyleBuilder::lineSpacingMultiplier, - StyleBuilder::lineSpacingMultiplierRes, - { it.lineSpacingMultiplier } + ARBITRARY_FLOATS, + R.attr.lineSpacingMultiplier, + TextViewProxy::setLineSpacingMultiplier, + StyleBuilder::lineSpacingMultiplier, + StyleBuilder::lineSpacingMultiplierRes, + { it.lineSpacingMultiplier } )) // maxLines add(withAssertEquals( - ARBITRARY_INTS, - R.attr.maxLines, - TextViewProxy::setMaxLines, - StyleBuilder::maxLines, - StyleBuilder::maxLinesRes, - { it.maxLines } + ARBITRARY_INTS, + R.attr.maxLines, + TextViewProxy::setMaxLines, + StyleBuilder::maxLines, + StyleBuilder::maxLinesRes, + { it.maxLines } )) // minLines add(withAssertEquals( - ARBITRARY_INTS, - R.attr.minLines, - TextViewProxy::setMinLines, - StyleBuilder::minLines, - StyleBuilder::minLinesRes, - { it.minLines } + ARBITRARY_INTS, + R.attr.minLines, + TextViewProxy::setMinLines, + StyleBuilder::minLines, + StyleBuilder::minLinesRes, + { it.minLines } )) // minWidth add(withAssertEquals( - ARBITRARY_INTS, - R.attr.minWidth, - TextViewProxy::setMinWidth, - StyleBuilder::minWidth, - StyleBuilder::minWidthRes, - { it.minWidth } + ARBITRARY_INTS, + R.attr.minWidth, + TextViewProxy::setMinWidth, + StyleBuilder::minWidth, + StyleBuilder::minWidthRes, + { it.minWidth } )) // singleLine add(withCustomAssert( - listOf(true), - R.attr.singleLine, - TextViewProxy::setSingleLine, - StyleBuilder::singleLine, - StyleBuilder::singleLineRes, - { view, _ -> - Assert.assertEquals(1, view.minLines) - Assert.assertEquals(1, view.maxLines) - } + listOf(true), + R.attr.singleLine, + TextViewProxy::setSingleLine, + StyleBuilder::singleLine, + StyleBuilder::singleLineRes, + { view, _ -> + Assert.assertEquals(1, view.minLines) + Assert.assertEquals(1, view.maxLines) + } )) // textColor add(withAssertEquals( - ARBITRARY_COLOR_STATE_LISTS, - R.attr.textColor, - TextViewProxy::setTextColor, - StyleBuilder::textColor, - StyleBuilder::textColorRes, - { it.textColors } + ARBITRARY_COLOR_STATE_LISTS, + R.attr.textColor, + TextViewProxy::setTextColor, + StyleBuilder::textColor, + StyleBuilder::textColorRes, + { it.textColors } )) // textColorHint add(withAssertEquals( - ARBITRARY_COLOR_STATE_LISTS, - R.attr.textColorHint, - TextViewProxy::setTextColorHint, - StyleBuilder::textColorHint, - StyleBuilder::textColorHintRes, - { it.hintTextColors } + ARBITRARY_COLOR_STATE_LISTS, + R.attr.textColorHint, + TextViewProxy::setTextColorHint, + StyleBuilder::textColorHint, + StyleBuilder::textColorHintRes, + { it.hintTextColors } )) // textSize add(withAssertEquals( - ARBITRARY_INTS.filter { it >= 0 }, - R.attr.textSize, - TextViewProxy::setTextSize, - StyleBuilder::textSize, - StyleBuilder::textSizeRes, - { it.textSize.toInt() } + ARBITRARY_INTS.filter { it >= 0 }, + R.attr.textSize, + TextViewProxy::setTextSize, + StyleBuilder::textSize, + StyleBuilder::textSizeRes, + { it.textSize.toInt() } )) // text add(withAssertEquals( - ARBITRARY_STRINGS, - R.attr.text, - TextViewProxy::setText, - StyleBuilder::text, - StyleBuilder::textRes, - { it.text } + ARBITRARY_STRINGS, + R.attr.text, + TextViewProxy::setText, + StyleBuilder::text, + StyleBuilder::textRes, + { it.text } )) } diff --git a/paris/src/androidTest/java/com/airbnb/paris/proxies/TextViewStyleApplier_StyleBuilderTest.kt b/paris/src/androidTest/java/com/airbnb/paris/proxies/TextViewStyleApplier_StyleBuilderTest.kt index 5130e5b9..44592e74 100644 --- a/paris/src/androidTest/java/com/airbnb/paris/proxies/TextViewStyleApplier_StyleBuilderTest.kt +++ b/paris/src/androidTest/java/com/airbnb/paris/proxies/TextViewStyleApplier_StyleBuilderTest.kt @@ -1,15 +1,17 @@ package com.airbnb.paris.proxies -import android.graphics.* -import android.graphics.drawable.* -import androidx.test.* -import androidx.test.runner.* -import android.widget.* -import android.widget.TextViewStyleApplier.* -import com.airbnb.paris.styles.* -import org.junit.* -import org.junit.Assert.* -import org.junit.runner.* +import android.graphics.Color +import android.graphics.drawable.ColorDrawable +import android.widget.TextView +import android.widget.TextViewStyleApplier.StyleBuilder +import androidx.test.InstrumentationRegistry +import androidx.test.runner.AndroidJUnit4 +import com.airbnb.paris.styles.ProgrammaticStyle +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNotEquals +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class TextViewStyleApplier_StyleBuilderTest { @@ -28,7 +30,7 @@ class TextViewStyleApplier_StyleBuilderTest { private lateinit var styleBuilder: StyleBuilder private fun programmaticStyle(builderFunctions: ProgrammaticStyle.Builder.() -> Any) = - ProgrammaticStyle.builder().debugName("test").builderFunctions() + ProgrammaticStyle.builder().debugName("test").builderFunctions() private fun ProgrammaticStyle.Builder.assertEqualsTextViewStyleBuilder(builderFunctions: StyleBuilder.() -> StyleBuilder) { assertEquals(build(), StyleBuilder().debugName("test").builderFunctions().build()) @@ -44,9 +46,9 @@ class TextViewStyleApplier_StyleBuilderTest { putDp(attrRes, value) val programmaticStyle = build() val textViewStyle = StyleBuilder() - .debugName("test") - .builderFunctions(value) - .build() + .debugName("test") + .builderFunctions(value) + .build() assertEquals(programmaticStyle, textViewStyle) } } diff --git a/paris/src/androidTest/java/com/airbnb/paris/proxies/ViewMappings.kt b/paris/src/androidTest/java/com/airbnb/paris/proxies/ViewMappings.kt index 80b8d522..35344d84 100644 --- a/paris/src/androidTest/java/com/airbnb/paris/proxies/ViewMappings.kt +++ b/paris/src/androidTest/java/com/airbnb/paris/proxies/ViewMappings.kt @@ -6,175 +6,180 @@ import android.view.ViewStyleApplier.BaseStyleBuilder import junit.framework.Assert.assertEquals internal class ViewMapping private constructor( - testValues: List, - attrRes: Int, - setProxyFunction: ViewProxy.(I) -> Unit, - setStyleBuilderValueFunction: BaseStyleBuilder<*, *>.(I) -> Any, - setStyleBuilderResFunction: BaseStyleBuilder<*, *>.(Int) -> Any, - /** - * A function which, when called, will assert that the view has been successfully modified - * by the associated proxy and/or style builder methods - */ - assertViewSet: (View, I) -> Unit) : - BaseViewMapping, ViewProxy, View, I>( - testValues, - attrRes, - setProxyFunction, - setStyleBuilderValueFunction, - setStyleBuilderResFunction, - assertViewSet) { + testValues: List, + attrRes: Int, + setProxyFunction: ViewProxy.(I) -> Unit, + setStyleBuilderValueFunction: BaseStyleBuilder<*, *>.(I) -> Any, + setStyleBuilderResFunction: BaseStyleBuilder<*, *>.(Int) -> Any, + /** + * A function which, when called, will assert that the view has been successfully modified + * by the associated proxy and/or style builder methods + */ + assertViewSet: (View, I) -> Unit +) : + BaseViewMapping, ViewProxy, View, I>( + testValues, + attrRes, + setProxyFunction, + setStyleBuilderValueFunction, + setStyleBuilderResFunction, + assertViewSet + ) { companion object { fun withCustomAssert( - testValues: List, - attrRes: Int, - setProxyFunction: ViewProxy.(I) -> Unit, - setStyleBuilderValueFunction: BaseStyleBuilder<*, *>.(I) -> Any, - setStyleBuilderResFunction: BaseStyleBuilder<*, *>.(Int) -> Any, - assertViewSet: (View, I) -> Unit): ViewMapping { + testValues: List, + attrRes: Int, + setProxyFunction: ViewProxy.(I) -> Unit, + setStyleBuilderValueFunction: BaseStyleBuilder<*, *>.(I) -> Any, + setStyleBuilderResFunction: BaseStyleBuilder<*, *>.(Int) -> Any, + assertViewSet: (View, I) -> Unit + ): ViewMapping { return ViewMapping( - testValues, - attrRes, - setProxyFunction, - setStyleBuilderValueFunction, - setStyleBuilderResFunction, - assertViewSet) + testValues, + attrRes, + setProxyFunction, + setStyleBuilderValueFunction, + setStyleBuilderResFunction, + assertViewSet + ) } fun withAssertEquals( - testValues: List, - attrRes: Int, - setProxyFunction: ViewProxy.(I) -> Unit, - setStyleBuilderValueFunction: BaseStyleBuilder<*, *>.(I) -> Any, - setStyleBuilderResFunction: BaseStyleBuilder<*, *>.(Int) -> Any, - getViewFunction: (View) -> I): ViewMapping { + testValues: List, + attrRes: Int, + setProxyFunction: ViewProxy.(I) -> Unit, + setStyleBuilderValueFunction: BaseStyleBuilder<*, *>.(I) -> Any, + setStyleBuilderResFunction: BaseStyleBuilder<*, *>.(Int) -> Any, + getViewFunction: (View) -> I + ): ViewMapping { return withCustomAssert( - testValues, - attrRes, - setProxyFunction, - setStyleBuilderValueFunction, - setStyleBuilderResFunction, - { View, input -> assertEquals(input, getViewFunction(View)) }) + testValues, + attrRes, + setProxyFunction, + setStyleBuilderValueFunction, + setStyleBuilderResFunction, + { View, input -> assertEquals(input, getViewFunction(View)) }) } } } internal val VIEW_SETUPS = listOf( - { view: View -> view.layoutDirection = View.LAYOUT_DIRECTION_LTR }, - { view: View -> view.layoutDirection = View.LAYOUT_DIRECTION_RTL } + { view: View -> view.layoutDirection = View.LAYOUT_DIRECTION_LTR }, + { view: View -> view.layoutDirection = View.LAYOUT_DIRECTION_RTL } ) internal val VIEW_MAPPINGS = ArrayList>().apply { // alpha add(ViewMapping.withAssertEquals( - listOf(-1f, -.4f, -.33333f, 0f, .2229432489f, .666f, 1f, 2f), - android.R.attr.alpha, - ViewProxy::setAlpha, - BaseStyleBuilder<*, *>::alpha, - BaseStyleBuilder<*, *>::alphaRes, - { it.alpha } + listOf(-1f, -.4f, -.33333f, 0f, .2229432489f, .666f, 1f, 2f), + android.R.attr.alpha, + ViewProxy::setAlpha, + BaseStyleBuilder<*, *>::alpha, + BaseStyleBuilder<*, *>::alphaRes, + { it.alpha } )) // elevation add(ViewMapping.withAssertEquals( - ARBITRARY_DIMENSIONS, - android.R.attr.elevation, - ViewProxy::setElevation, - BaseStyleBuilder<*, *>::elevation, - BaseStyleBuilder<*, *>::elevationRes, - { it.elevation.toInt() } + ARBITRARY_DIMENSIONS, + android.R.attr.elevation, + ViewProxy::setElevation, + BaseStyleBuilder<*, *>::elevation, + BaseStyleBuilder<*, *>::elevationRes, + { it.elevation.toInt() } )) // foreground if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { add(ViewMapping.withAssertEquals( - ARBITRARY_COLOR_DRAWABLES, - android.R.attr.foreground, - ViewProxy::setForeground, - BaseStyleBuilder<*, *>::foreground, - BaseStyleBuilder<*, *>::foregroundRes, - { it.foreground } + ARBITRARY_COLOR_DRAWABLES, + android.R.attr.foreground, + ViewProxy::setForeground, + BaseStyleBuilder<*, *>::foreground, + BaseStyleBuilder<*, *>::foregroundRes, + { it.foreground } )) } // minHeight add(ViewMapping.withAssertEquals( - ARBITRARY_DIMENSIONS, - android.R.attr.minHeight, - ViewProxy::setMinHeight, - BaseStyleBuilder<*, *>::minHeight, - BaseStyleBuilder<*, *>::minHeightRes, - { it.minimumHeight } + ARBITRARY_DIMENSIONS, + android.R.attr.minHeight, + ViewProxy::setMinHeight, + BaseStyleBuilder<*, *>::minHeight, + BaseStyleBuilder<*, *>::minHeightRes, + { it.minimumHeight } )) // minWidth add(ViewMapping.withAssertEquals( - ARBITRARY_DIMENSIONS, - android.R.attr.minWidth, - ViewProxy::setMinWidth, - BaseStyleBuilder<*, *>::minWidth, - BaseStyleBuilder<*, *>::minWidthRes, - { it.minimumWidth } + ARBITRARY_DIMENSIONS, + android.R.attr.minWidth, + ViewProxy::setMinWidth, + BaseStyleBuilder<*, *>::minWidth, + BaseStyleBuilder<*, *>::minWidthRes, + { it.minimumWidth } )) // paddingEnd add(ViewMapping.withAssertEquals( - ARBITRARY_DIMENSIONS, - android.R.attr.paddingEnd, - ViewProxy::setPaddingEnd, - BaseStyleBuilder<*, *>::paddingEnd, - BaseStyleBuilder<*, *>::paddingEndRes, - { it.paddingEnd } + ARBITRARY_DIMENSIONS, + android.R.attr.paddingEnd, + ViewProxy::setPaddingEnd, + BaseStyleBuilder<*, *>::paddingEnd, + BaseStyleBuilder<*, *>::paddingEndRes, + { it.paddingEnd } )) // paddingHorizontal add(ViewMapping.withCustomAssert( - ARBITRARY_DIMENSIONS, - android.R.attr.paddingHorizontal, - ViewProxy::setPaddingHorizontal, - BaseStyleBuilder<*, *>::paddingHorizontal, - BaseStyleBuilder<*, *>::paddingHorizontalRes, - { view, input -> - assertEquals(input, view.paddingEnd) - assertEquals(input, view.paddingLeft) - assertEquals(input, view.paddingRight) - assertEquals(input, view.paddingStart) - } + ARBITRARY_DIMENSIONS, + android.R.attr.paddingHorizontal, + ViewProxy::setPaddingHorizontal, + BaseStyleBuilder<*, *>::paddingHorizontal, + BaseStyleBuilder<*, *>::paddingHorizontalRes, + { view, input -> + assertEquals(input, view.paddingEnd) + assertEquals(input, view.paddingLeft) + assertEquals(input, view.paddingRight) + assertEquals(input, view.paddingStart) + } )) // paddingStart add(ViewMapping.withAssertEquals( - ARBITRARY_DIMENSIONS, - android.R.attr.paddingStart, - ViewProxy::setPaddingStart, - BaseStyleBuilder<*, *>::paddingStart, - BaseStyleBuilder<*, *>::paddingStartRes, - { it.paddingStart } + ARBITRARY_DIMENSIONS, + android.R.attr.paddingStart, + ViewProxy::setPaddingStart, + BaseStyleBuilder<*, *>::paddingStart, + BaseStyleBuilder<*, *>::paddingStartRes, + { it.paddingStart } )) // paddingVertical add(ViewMapping.withCustomAssert( - ARBITRARY_DIMENSIONS, - android.R.attr.paddingVertical, - ViewProxy::setPaddingVertical, - BaseStyleBuilder<*, *>::paddingVertical, - BaseStyleBuilder<*, *>::paddingVerticalRes, - { view, input -> - assertEquals(input, view.paddingBottom) - assertEquals(input, view.paddingTop) - } + ARBITRARY_DIMENSIONS, + android.R.attr.paddingVertical, + ViewProxy::setPaddingVertical, + BaseStyleBuilder<*, *>::paddingVertical, + BaseStyleBuilder<*, *>::paddingVerticalRes, + { view, input -> + assertEquals(input, view.paddingBottom) + assertEquals(input, view.paddingTop) + } )) // contentDescription add(ViewMapping.withAssertEquals( - ARBITRARY_STRINGS, - android.R.attr.contentDescription, - ViewProxy::setContentDescription, - BaseStyleBuilder<*, *>::contentDescription, - BaseStyleBuilder<*, *>::contentDescriptionRes, - { it.contentDescription } + ARBITRARY_STRINGS, + android.R.attr.contentDescription, + ViewProxy::setContentDescription, + BaseStyleBuilder<*, *>::contentDescription, + BaseStyleBuilder<*, *>::contentDescriptionRes, + { it.contentDescription } )) } diff --git a/paris/src/androidTest/java/com/airbnb/paris/proxies/ViewStyleApplier_StyleBuilderTest.kt b/paris/src/androidTest/java/com/airbnb/paris/proxies/ViewStyleApplier_StyleBuilderTest.kt index 37ca90e0..3e5a189c 100644 --- a/paris/src/androidTest/java/com/airbnb/paris/proxies/ViewStyleApplier_StyleBuilderTest.kt +++ b/paris/src/androidTest/java/com/airbnb/paris/proxies/ViewStyleApplier_StyleBuilderTest.kt @@ -1,15 +1,18 @@ package com.airbnb.paris.proxies -import androidx.test.* -import androidx.test.runner.* -import android.view.* -import android.widget.* -import android.view.ViewStyleApplier.* -import com.airbnb.paris.styles.* +import android.view.View +import android.view.ViewStyleApplier.StyleBuilder +import android.widget.TextView +import androidx.test.InstrumentationRegistry +import androidx.test.runner.AndroidJUnit4 +import com.airbnb.paris.styles.ProgrammaticStyle import com.airbnb.paris.test.R -import org.junit.* -import org.junit.Assert.* -import org.junit.runner.* +import org.junit.Assert +import org.junit.Assert.assertEquals +import org.junit.Assert.assertNull +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith @RunWith(AndroidJUnit4::class) class ViewStyleApplier_StyleBuilderTest { @@ -58,10 +61,11 @@ class ViewStyleApplier_StyleBuilderTest { fun background() { assertNull(view.background) StyleBuilder() - .backgroundRes(R.drawable.format_drawable) - .applyTo(view) + .backgroundRes(R.drawable.format_drawable) + .applyTo(view) Assert.assertEquals( - res.getDrawable(R.drawable.format_drawable).constantState, - view.background.constantState) + res.getDrawable(R.drawable.format_drawable).constantState, + view.background.constantState + ) } } diff --git a/paris/src/androidTest/java/com/airbnb/paris/spannables/SpannableBuilderTest.kt b/paris/src/androidTest/java/com/airbnb/paris/spannables/SpannableBuilderTest.kt index bf9da8fd..1b9993af 100644 --- a/paris/src/androidTest/java/com/airbnb/paris/spannables/SpannableBuilderTest.kt +++ b/paris/src/androidTest/java/com/airbnb/paris/spannables/SpannableBuilderTest.kt @@ -2,8 +2,6 @@ package com.airbnb.paris.spannables import androidx.test.InstrumentationRegistry import androidx.test.runner.AndroidJUnit4 -import com.airbnb.paris.spannables.SpannableBuilder -import com.airbnb.paris.spannables.StyleConverter import com.airbnb.paris.styles.ProgrammaticStyle import com.airbnb.paris.styles.ResourceStyle import org.hamcrest.MatcherAssert.assertThat @@ -31,13 +29,17 @@ class SpannableBuilderTest { val dummyStyle = ProgrammaticStyle.builder().build(); val builder = SpannableBuilder() - .append("Good", 1) - .append(" Morning", dummyStyle) - - assertThat(builder.markupItems, equalTo(setOf( - StyleConverter.MarkupItem(IntRange(0, 4), ResourceStyle(1)), - StyleConverter.MarkupItem(IntRange(4, 12), dummyStyle) - ))) + .append("Good", 1) + .append(" Morning", dummyStyle) + + assertThat( + builder.markupItems, equalTo( + setOf( + StyleConverter.MarkupItem(IntRange(0, 4), ResourceStyle(1)), + StyleConverter.MarkupItem(IntRange(4, 12), dummyStyle) + ) + ) + ) assertThat(builder.stringBuilder.toString(), equalTo("Good Morning")) } @@ -45,8 +47,8 @@ class SpannableBuilderTest { fun appendingNonStyledStrings() { val builder = SpannableBuilder() - .append("Good") - .append(" Morning") + .append("Good") + .append(" Morning") assertThat(builder.markupItems, equalTo(emptySet())) assertThat(builder.stringBuilder.toString(), equalTo("Good Morning")) diff --git a/paris/src/androidTest/java/com/airbnb/paris/spannables/StyleConverterTest.kt b/paris/src/androidTest/java/com/airbnb/paris/spannables/StyleConverterTest.kt index 590c60f7..0a2219cc 100644 --- a/paris/src/androidTest/java/com/airbnb/paris/spannables/StyleConverterTest.kt +++ b/paris/src/androidTest/java/com/airbnb/paris/spannables/StyleConverterTest.kt @@ -2,13 +2,17 @@ package com.airbnb.paris.spannables import android.graphics.Color import android.graphics.Typeface +import android.text.style.AbsoluteSizeSpan +import android.text.style.ForegroundColorSpan +import android.text.style.StyleSpan +import android.text.style.TextAppearanceSpan +import android.text.style.TypefaceSpan import androidx.test.InstrumentationRegistry import androidx.test.runner.AndroidJUnit4 -import android.text.style.* -import com.airbnb.paris.test.R import com.airbnb.paris.attribute_values.ColorValue import com.airbnb.paris.styles.ProgrammaticStyle import com.airbnb.paris.styles.Style +import com.airbnb.paris.test.R import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.equalTo import org.junit.Test @@ -21,11 +25,11 @@ class StyleConverterTest { private val converter = StyleConverter(InstrumentationRegistry.getTargetContext()!!) @Test - fun textSize(){ + fun textSize() { val bigTextStyle = ProgrammaticStyle.builder() - .put(android.R.attr.textSize, 30) - .build() + .put(android.R.attr.textSize, 30) + .build() assertStyleAppliesCorrectlyOnSampleString(bigTextStyle, AbsoluteSizeSpan::class) { assertThat(it.size, equalTo(30)) @@ -33,11 +37,11 @@ class StyleConverterTest { } @Test - fun textColor(){ + fun textColor() { val cyanTextStyle = ProgrammaticStyle.builder() - .put(android.R.attr.textColor, ColorValue(Color.CYAN)) - .build() + .put(android.R.attr.textColor, ColorValue(Color.CYAN)) + .build() assertStyleAppliesCorrectlyOnSampleString(cyanTextStyle, ForegroundColorSpan::class) { assertThat(it.foregroundColor, equalTo(Color.CYAN)) @@ -45,11 +49,11 @@ class StyleConverterTest { } @Test - fun textAppearance(){ + fun textAppearance() { val cyanTextStyle = ProgrammaticStyle.builder() - .put(android.R.attr.textAppearance, R.style.StyleConverterTest_MyTextAppearance) - .build() + .put(android.R.attr.textAppearance, R.style.StyleConverterTest_MyTextAppearance) + .build() assertStyleAppliesCorrectlyOnSampleString(cyanTextStyle, TextAppearanceSpan::class) { assertThat(it.textColor.defaultColor, equalTo(Color.GREEN)) @@ -58,11 +62,11 @@ class StyleConverterTest { } @Test - fun fontFamily(){ + fun fontFamily() { val cyanTextStyle = ProgrammaticStyle.builder() - .put(android.R.attr.fontFamily, "monospace") - .build() + .put(android.R.attr.fontFamily, "monospace") + .build() assertStyleAppliesCorrectlyOnSampleString(cyanTextStyle, TypefaceSpan::class) { assertThat(it.family, equalTo("monospace")) @@ -70,11 +74,11 @@ class StyleConverterTest { } @Test - fun typeFace(){ + fun typeFace() { val cyanTextStyle = ProgrammaticStyle.builder() - .put(android.R.attr.typeface, "sans-serif") - .build() + .put(android.R.attr.typeface, "sans-serif") + .build() assertStyleAppliesCorrectlyOnSampleString(cyanTextStyle, TypefaceSpan::class) { assertThat(it.family, equalTo("sans-serif")) @@ -82,21 +86,22 @@ class StyleConverterTest { } @Test - fun textStyle(){ + fun textStyle() { val cyanTextStyle = ProgrammaticStyle.builder() - .put(android.R.attr.textStyle, Typeface.BOLD_ITALIC) - .build() + .put(android.R.attr.textStyle, Typeface.BOLD_ITALIC) + .build() assertStyleAppliesCorrectlyOnSampleString(cyanTextStyle, StyleSpan::class) { assertThat(it.style, equalTo(Typeface.BOLD_ITALIC)) } } - private fun assertStyleAppliesCorrectlyOnSampleString( - style: Style, - expectedGeneratedSpanClass: KClass, - spanAssertions: ((T) -> Unit)) { + private fun assertStyleAppliesCorrectlyOnSampleString( + style: Style, + expectedGeneratedSpanClass: KClass, + spanAssertions: ((T) -> Unit) + ) { val sampleString = "Hello World" val spanned = converter.createSpannable(sampleString, setOf(StyleConverter.MarkupItem(IntRange(3, 5), style))) diff --git a/paris/src/main/java/com/airbnb/paris/StyleApplier.kt b/paris/src/main/java/com/airbnb/paris/StyleApplier.kt index 2345c4f7..1490cd78 100644 --- a/paris/src/main/java/com/airbnb/paris/StyleApplier.kt +++ b/paris/src/main/java/com/airbnb/paris/StyleApplier.kt @@ -2,12 +2,15 @@ package com.airbnb.paris -import androidx.annotation.* -import android.util.* -import android.view.* -import com.airbnb.paris.proxies.* -import com.airbnb.paris.styles.* -import com.airbnb.paris.typed_array_wrappers.* +import android.util.AttributeSet +import android.view.View +import androidx.annotation.StyleRes +import androidx.annotation.UiThread +import com.airbnb.paris.proxies.Proxy +import com.airbnb.paris.styles.AttributeSetStyle +import com.airbnb.paris.styles.ResourceStyle +import com.airbnb.paris.styles.Style +import com.airbnb.paris.typed_array_wrappers.TypedArrayWrapper @UiThread abstract class StyleApplier private constructor(val proxy: P, val view: V) { diff --git a/paris/src/main/java/com/airbnb/paris/StyleApplierUtils.kt b/paris/src/main/java/com/airbnb/paris/StyleApplierUtils.kt index d6ccf9ad..fd149446 100644 --- a/paris/src/main/java/com/airbnb/paris/StyleApplierUtils.kt +++ b/paris/src/main/java/com/airbnb/paris/StyleApplierUtils.kt @@ -2,8 +2,8 @@ package com.airbnb.paris import android.content.Context import android.content.res.Resources -import androidx.annotation.UiThread import android.view.View +import androidx.annotation.UiThread import com.airbnb.paris.styles.Style import com.airbnb.paris.typed_array_wrappers.TypedArrayWrapper diff --git a/paris/src/main/java/com/airbnb/paris/StyleBuilder.kt b/paris/src/main/java/com/airbnb/paris/StyleBuilder.kt index 0ecdef3d..eed2c851 100644 --- a/paris/src/main/java/com/airbnb/paris/StyleBuilder.kt +++ b/paris/src/main/java/com/airbnb/paris/StyleBuilder.kt @@ -1,16 +1,21 @@ package com.airbnb.paris -import androidx.annotation.StyleRes import android.util.AttributeSet -import com.airbnb.paris.styles.* +import androidx.annotation.StyleRes +import com.airbnb.paris.styles.AttributeSetStyle +import com.airbnb.paris.styles.MultiStyle +import com.airbnb.paris.styles.ProgrammaticStyle +import com.airbnb.paris.styles.ResourceStyle +import com.airbnb.paris.styles.Style /** * This class is meant to be implemented by classes generated by the framework exclusively */ @Suppress("UNCHECKED_CAST") abstract class StyleBuilder, out A : StyleApplier<*, *>> @JvmOverloads constructor( - private val applier: A? = null, - private var name: String = "a programmatic style") { + private val applier: A? = null, + private var name: String = "a programmatic style" +) { protected open var builder = ProgrammaticStyle.builder() diff --git a/paris/src/main/java/com/airbnb/paris/attribute_values/ColorValue.kt b/paris/src/main/java/com/airbnb/paris/attribute_values/ColorValue.kt index ce1a7fba..f29ebe36 100644 --- a/paris/src/main/java/com/airbnb/paris/attribute_values/ColorValue.kt +++ b/paris/src/main/java/com/airbnb/paris/attribute_values/ColorValue.kt @@ -1,6 +1,6 @@ package com.airbnb.paris.attribute_values -import androidx.annotation.* +import androidx.annotation.ColorInt internal data class ColorValue(@ColorInt val colorValue: Int) diff --git a/paris/src/main/java/com/airbnb/paris/attribute_values/DpValue.kt b/paris/src/main/java/com/airbnb/paris/attribute_values/DpValue.kt index 9bd9055e..219abe9d 100644 --- a/paris/src/main/java/com/airbnb/paris/attribute_values/DpValue.kt +++ b/paris/src/main/java/com/airbnb/paris/attribute_values/DpValue.kt @@ -1,5 +1,5 @@ package com.airbnb.paris.attribute_values -import androidx.annotation.* +import androidx.annotation.Dimension internal data class DpValue(@Dimension(unit = Dimension.DP) val dpValue: Int) diff --git a/paris/src/main/java/com/airbnb/paris/attribute_values/ResourceId.kt b/paris/src/main/java/com/airbnb/paris/attribute_values/ResourceId.kt index ca3640b8..aef15b88 100644 --- a/paris/src/main/java/com/airbnb/paris/attribute_values/ResourceId.kt +++ b/paris/src/main/java/com/airbnb/paris/attribute_values/ResourceId.kt @@ -1,5 +1,5 @@ package com.airbnb.paris.attribute_values -import androidx.annotation.* +import androidx.annotation.AnyRes internal data class ResourceId(@AnyRes val resId: Int) diff --git a/paris/src/main/java/com/airbnb/paris/attribute_values/Styles.kt b/paris/src/main/java/com/airbnb/paris/attribute_values/Styles.kt index 79a6746b..9367523b 100644 --- a/paris/src/main/java/com/airbnb/paris/attribute_values/Styles.kt +++ b/paris/src/main/java/com/airbnb/paris/attribute_values/Styles.kt @@ -1,5 +1,5 @@ package com.airbnb.paris.attribute_values -import com.airbnb.paris.styles.* +import com.airbnb.paris.styles.Style internal data class Styles(val list: MutableList