diff --git a/.github/workflows/requirements.txt b/.github/workflows/requirements.txt index 209f566630..b53f9ff051 100644 --- a/.github/workflows/requirements.txt +++ b/.github/workflows/requirements.txt @@ -4,10 +4,10 @@ Jinja2==3.1.3 livereload==2.6.3 lunr==0.7.0.post1 Markdown<3.6 -MarkupSafe==2.1.3 +MarkupSafe==2.1.4 mkdocs==1.5.3 mkdocs-macros-plugin==1.0.5 -mkdocs-material==9.5.4 +mkdocs-material==9.5.5 mkdocs-material-extensions==1.3.1 Pygments==2.17.2 pymdown-extensions==10.7 diff --git a/gradle.properties b/gradle.properties index 5453445c0d..4e67ea031c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -28,5 +28,3 @@ android.suppressUnsupportedCompileSdk=UpsideDownCakePrivacySandbox # Signals to our own plugin that we are building within the repo. app.cash.paparazzi.internal=true - -#app.cash.paparazzi.legacy.resource.loading=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8cf8fcedf7..f858ceeb91 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -62,7 +62,7 @@ trove4j = { module = "org.jetbrains.intellij.deps:trove4j", version = "1.0.20200 # Test libraries junit = { module = "junit:junit", version = "4.13.2" } testParameterInjector = { module = "com.google.testparameterinjector:test-parameter-injector", version = "1.15" } -truth = { module = "com.google.truth:truth", version = "1.2.0" } +truth = { module = "com.google.truth:truth", version = "1.3.0" } # Plugins plugin-android = { module = "com.android.tools.build:gradle", version.ref = "agp" } @@ -71,5 +71,5 @@ plugin-dokka = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version = " plugin-kotlin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } plugin-ksp = { module = "com.google.devtools.ksp:com.google.devtools.ksp.gradle.plugin", version.ref = "ksp" } plugin-mavenPublish = { module = "com.vanniktech:gradle-maven-publish-plugin", version = "0.27.0" } -plugin-spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version = "6.24.0" } -plugin-versions = { module = "com.github.ben-manes:gradle-versions-plugin", version = "0.50.0" } +plugin-spotless = { module = "com.diffplug.spotless:spotless-plugin-gradle", version = "6.25.0" } +plugin-versions = { module = "com.github.ben-manes:gradle-versions-plugin", version = "0.51.0" } diff --git a/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/PaparazziPlugin.kt b/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/PaparazziPlugin.kt index 1b939d3f99..03b7eaab36 100644 --- a/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/PaparazziPlugin.kt +++ b/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/PaparazziPlugin.kt @@ -17,6 +17,7 @@ package app.cash.paparazzi.gradle import app.cash.paparazzi.gradle.utils.artifactViewFor import app.cash.paparazzi.gradle.utils.artifactsFor +import app.cash.paparazzi.gradle.utils.relativize import com.android.build.gradle.BaseExtension import com.android.build.gradle.LibraryExtension import com.android.build.gradle.TestedExtension @@ -25,7 +26,6 @@ import com.android.build.gradle.internal.api.TestedVariant import com.android.build.gradle.internal.dsl.BaseAppModuleExtension import com.android.build.gradle.internal.dsl.DynamicFeatureExtension import com.android.build.gradle.internal.publishing.AndroidArtifacts.ArtifactType -import com.android.build.gradle.tasks.MergeSourceSetFolders import org.gradle.api.DefaultTask import org.gradle.api.DomainObjectSet import org.gradle.api.Plugin @@ -33,16 +33,12 @@ import org.gradle.api.Project import org.gradle.api.artifacts.component.ProjectComponentIdentifier import org.gradle.api.artifacts.type.ArtifactTypeDefinition import org.gradle.api.artifacts.type.ArtifactTypeDefinition.ARTIFACT_TYPE_ATTRIBUTE -import org.gradle.api.file.Directory -import org.gradle.api.file.DirectoryProperty import org.gradle.api.file.FileCollection import org.gradle.api.internal.artifacts.transform.UnzipTransform import org.gradle.api.logging.LogLevel.LIFECYCLE import org.gradle.api.plugins.JavaBasePlugin -import org.gradle.api.provider.Provider import org.gradle.api.reporting.ReportingExtension import org.gradle.api.tasks.PathSensitivity -import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.options.Option import org.gradle.api.tasks.testing.Test import org.gradle.internal.os.OperatingSystem @@ -52,49 +48,27 @@ import org.jetbrains.kotlin.gradle.plugin.KotlinAndroidPluginWrapper import org.jetbrains.kotlin.gradle.plugin.KotlinMultiplatformPluginWrapper import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinAndroidTarget import java.util.Locale -import kotlin.io.path.invariantSeparatorsPathString @Suppress("unused") class PaparazziPlugin : Plugin { override fun apply(project: Project) { - val legacyResourceLoadingEnabled = (project.findProperty("app.cash.paparazzi.legacy.resource.loading") as? String)?.toBoolean() ?: false - - if (legacyResourceLoadingEnabled) { - project.afterEvaluate { - check(!project.plugins.hasPlugin("com.android.application")) { - error( - "Currently, Paparazzi only works in Android library -- not application -- modules. " + - "See https://github.com/cashapp/paparazzi/issues/107" - ) - } - - check(project.plugins.hasPlugin("com.android.library")) { - "The Android Gradle library plugin must be applied for Paparazzi to work properly." - } - } - - project.plugins.withId("com.android.library") { - setupPaparazzi(project, project.extensions.getByType(LibraryExtension::class.java).libraryVariants) - } - } else { - val supportedPlugins = listOf("com.android.application", "com.android.library", "com.android.dynamic-feature") - project.afterEvaluate { - check(supportedPlugins.any { project.plugins.hasPlugin(it) }) { - "One of ${supportedPlugins.joinToString(", ")} must be applied for Paparazzi to work properly." - } + val supportedPlugins = listOf("com.android.application", "com.android.library", "com.android.dynamic-feature") + project.afterEvaluate { + check(supportedPlugins.any { project.plugins.hasPlugin(it) }) { + "One of ${supportedPlugins.joinToString(", ")} must be applied for Paparazzi to work properly." } + } - supportedPlugins.forEach { plugin -> - project.plugins.withId(plugin) { - val variants = when (val extension = project.extensions.getByType(TestedExtension::class.java)) { - is LibraryExtension -> extension.libraryVariants - is BaseAppModuleExtension -> extension.applicationVariants - is DynamicFeatureExtension -> extension.applicationVariants - // exhaustive to avoid potential breaking changes in future AGP releases - else -> throw IllegalStateException("${extension.javaClass.name} from $plugin is not supported in Paparazzi") - } - setupPaparazzi(project, variants) + supportedPlugins.forEach { plugin -> + project.plugins.withId(plugin) { + val variants = when (val extension = project.extensions.getByType(TestedExtension::class.java)) { + is LibraryExtension -> extension.libraryVariants + is BaseAppModuleExtension -> extension.applicationVariants + is DynamicFeatureExtension -> extension.applicationVariants + // exhaustive to avoid potential breaking changes in future AGP releases + else -> error("${extension.javaClass.name} from $plugin is not supported in Paparazzi") } + setupPaparazzi(project, variants) } } } @@ -117,10 +91,6 @@ class PaparazziPlugin : Plugin { val variantSlug = variant.name.capitalize(Locale.US) val testVariant = variant.unitTestVariant ?: return@all - val mergeResourcesOutputDir = variant.mergeResourcesProvider.flatMap { it.outputDir } - val mergeAssetsProvider = - project.tasks.named("merge${variantSlug}Assets") as TaskProvider - val mergeAssetsOutputDir = mergeAssetsProvider.flatMap { it.outputDir } val projectDirectory = project.layout.projectDirectory val buildDirectory = project.layout.buildDirectory val gradleUserHomeDir = project.gradle.gradleUserHomeDir @@ -168,14 +138,12 @@ class PaparazziPlugin : Plugin { task.packageName.set(android.packageName()) task.artifactFiles.from(packageAwareArtifactFiles) task.nonTransitiveRClassEnabled.set(nonTransitiveRClassEnabled) - task.mergeResourcesOutputDir.set(buildDirectory.asRelativePathString(mergeResourcesOutputDir)) task.targetSdkVersion.set(android.targetSdkVersion()) task.compileSdkVersion.set(android.compileSdkVersion()) - task.mergeAssetsOutputDir.set(buildDirectory.asRelativePathString(mergeAssetsOutputDir)) - task.projectResourceDirs.from(localResourceDirs) - task.moduleResourceDirs.from(moduleResourceDirs) + task.projectResourceDirs.set(localResourceDirs.relativize(projectDirectory)) + task.moduleResourceDirs.set(moduleResourceDirs.relativize(projectDirectory)) task.aarExplodedDirs.from(aarExplodedDirs) - task.projectAssetDirs.from(localAssetDirs.plus(moduleAssetDirs)) + task.projectAssetDirs.set(localAssetDirs.plus(moduleAssetDirs).relativize(projectDirectory)) task.aarAssetDirs.from(aarAssetDirs) task.paparazziResources.set(buildDirectory.file("intermediates/paparazzi/${variant.name}/resources.json")) } @@ -234,8 +202,18 @@ class PaparazziPlugin : Plugin { test.inputs.property("paparazzi.test.record", isRecordRun) test.inputs.property("paparazzi.test.verify", isVerifyRun) - test.inputs.dir(mergeResourcesOutputDir) - test.inputs.dir(mergeAssetsOutputDir) + test.inputs.files(localResourceDirs) + .withPropertyName("paparazzi.localResourceDirs") + .withPathSensitivity(PathSensitivity.RELATIVE) + test.inputs.files(moduleResourceDirs) + .withPropertyName("paparazzi.moduleResourceDirs") + .withPathSensitivity(PathSensitivity.RELATIVE) + test.inputs.files(localAssetDirs) + .withPropertyName("paparazzi.localAssetDirs") + .withPathSensitivity(PathSensitivity.RELATIVE) + test.inputs.files(moduleAssetDirs) + .withPropertyName("paparazzi.moduleAssetDirs") + .withPathSensitivity(PathSensitivity.RELATIVE) test.inputs.files(nativePlatformFileCollection) .withPropertyName("paparazzi.nativePlatform") .withPathSensitivity(PathSensitivity.NONE) @@ -323,11 +301,4 @@ class PaparazziPlugin : Plugin { } } -private fun Directory.relativize(child: Directory): String { - return asFile.toPath().relativize(child.asFile.toPath()).invariantSeparatorsPathString -} - -private fun DirectoryProperty.asRelativePathString(child: Provider): Provider = - flatMap { root -> child.map { root.relativize(it) } } - private const val DEFAULT_COMPILE_SDK_VERSION = 34 diff --git a/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/PrepareResourcesTask.kt b/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/PrepareResourcesTask.kt index 636c28d467..65dda4e4ce 100644 --- a/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/PrepareResourcesTask.kt +++ b/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/PrepareResourcesTask.kt @@ -21,6 +21,7 @@ import com.squareup.moshi.Moshi import org.gradle.api.DefaultTask import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.CacheableTask import org.gradle.api.tasks.Input @@ -35,40 +36,29 @@ abstract class PrepareResourcesTask : DefaultTask() { @get:Input abstract val packageName: Property - @Deprecated("legacy resource loading, to be removed in a future release") - @get:Input - abstract val mergeResourcesOutputDir: Property - @get:Input abstract val targetSdkVersion: Property @get:Input abstract val compileSdkVersion: Property - @get:InputFiles - @get:PathSensitive(PathSensitivity.RELATIVE) - abstract val projectResourceDirs: ConfigurableFileCollection + @get:Input + abstract val projectResourceDirs: ListProperty - @get:InputFiles - @get:PathSensitive(PathSensitivity.RELATIVE) - abstract val moduleResourceDirs: ConfigurableFileCollection + @get:Input + abstract val moduleResourceDirs: ListProperty @get:InputFiles @get:PathSensitive(PathSensitivity.RELATIVE) abstract val aarExplodedDirs: ConfigurableFileCollection - @get:InputFiles - @get:PathSensitive(PathSensitivity.RELATIVE) - abstract val projectAssetDirs: ConfigurableFileCollection + @get:Input + abstract val projectAssetDirs: ListProperty @get:InputFiles @get:PathSensitive(PathSensitivity.RELATIVE) abstract val aarAssetDirs: ConfigurableFileCollection - @Deprecated("legacy asset loading, to be removed in a future release") - @get:Input - abstract val mergeAssetsOutputDir: Property - @get:Input abstract val nonTransitiveRClassEnabled: Property @@ -84,8 +74,6 @@ abstract class PrepareResourcesTask : DefaultTask() { private val gradleUserHomeDirectory = projectDirectory.dir(project.gradle.gradleUserHomeDir.path) @TaskAction - // TODO: figure out why this can't be removed as of Kotlin 1.6+ - @OptIn(ExperimentalStdlibApi::class) fun writeResourcesFile() { val out = paparazziResources.get().asFile out.delete() @@ -104,16 +92,14 @@ abstract class PrepareResourcesTask : DefaultTask() { val config = Config( mainPackage = mainPackage, - mergeResourcesOutputDir = mergeResourcesOutputDir.get(), targetSdkVersion = targetSdkVersion.get(), // Use compileSdkVersion for system framework resources. platformDir = "platforms/android-${compileSdkVersion.get()}/", - mergeAssetsOutputDir = mergeAssetsOutputDir.get(), resourcePackageNames = resourcePackageNames, - projectResourceDirs = projectResourceDirs.relativize(projectDirectory), - moduleResourceDirs = moduleResourceDirs.relativize(projectDirectory), + projectResourceDirs = projectResourceDirs.get(), + moduleResourceDirs = moduleResourceDirs.get(), aarExplodedDirs = aarExplodedDirs.relativize(gradleUserHomeDirectory), - projectAssetDirs = projectAssetDirs.relativize(projectDirectory), + projectAssetDirs = projectAssetDirs.get(), aarAssetDirs = aarAssetDirs.relativize(gradleUserHomeDirectory) ) val moshi = Moshi.Builder().build()!! @@ -124,10 +110,8 @@ abstract class PrepareResourcesTask : DefaultTask() { @JsonClass(generateAdapter = true) data class Config( val mainPackage: String, - val mergeResourcesOutputDir: String, val targetSdkVersion: String, val platformDir: String, - val mergeAssetsOutputDir: String, val resourcePackageNames: List, val projectResourceDirs: List, val moduleResourceDirs: List, diff --git a/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/utils/FileUtils.kt b/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/utils/FileUtils.kt index b36913f7f2..29ff8b33b8 100644 --- a/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/utils/FileUtils.kt +++ b/paparazzi-gradle-plugin/src/main/java/app/cash/paparazzi/gradle/utils/FileUtils.kt @@ -15,11 +15,11 @@ */ package app.cash.paparazzi.gradle.utils -import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.Directory +import org.gradle.api.file.FileCollection import java.io.File -fun ConfigurableFileCollection.relativize(directory: Directory) = files.map(directory::relativize) +fun FileCollection.relativize(directory: Directory) = files.map(directory::relativize) fun Directory.relativize(child: File): String { return asFile.toPath().relativize(child.toPath()).toFile().invariantSeparatorsPath diff --git a/paparazzi-gradle-plugin/src/test/java/app/cash/paparazzi/gradle/PaparazziPluginTest.kt b/paparazzi-gradle-plugin/src/test/java/app/cash/paparazzi/gradle/PaparazziPluginTest.kt index aa1728ccd8..dcf4b9d0de 100644 --- a/paparazzi-gradle-plugin/src/test/java/app/cash/paparazzi/gradle/PaparazziPluginTest.kt +++ b/paparazzi-gradle-plugin/src/test/java/app/cash/paparazzi/gradle/PaparazziPluginTest.kt @@ -92,35 +92,6 @@ class PaparazziPluginTest { ) } - @Test - fun missingAndroidLibraryPluginWhenLegacyResourceLoadingIsOn() { - val fixtureRoot = File("src/test/projects/missing-library-plugin") - - val result = gradleRunner - .withArguments("preparePaparazziDebugResources", "--stacktrace") - .runFixture(fixtureRoot) { buildAndFail() } - - assertThat(result.task(":preparePaparazziDebugResources")).isNull() - assertThat(result.output).contains( - "The Android Gradle library plugin must be applied for Paparazzi to work properly." - ) - } - - @Test - fun invalidAndroidApplicationPluginWhenLegacyResourceLoadingIsOn() { - val fixtureRoot = File("src/test/projects/invalid-application-plugin") - - val result = gradleRunner - .withArguments("preparePaparazziDebugResources", "--stacktrace") - .runFixture(fixtureRoot) { buildAndFail() } - - assertThat(result.task(":preparePaparazziDebugResources")).isNull() - assertThat(result.output).contains( - "Currently, Paparazzi only works in Android library -- not application -- modules. " + - "See https://github.com/cashapp/paparazzi/issues/107" - ) - } - @Test fun declareAndroidPluginAfter() { val fixtureRoot = File("src/test/projects/declare-android-plugin-after") @@ -319,46 +290,6 @@ class PaparazziPluginTest { assertThat(result.output).contains("Objects still linked from the DelegateManager:") } - @Test - fun flagLegacyResourceLoadingIsOn() { - val fixtureRoot = File("src/test/projects/flag-legacy-resource-loading-on") - - val result = gradleRunner - .withArguments("testDebug", "--stacktrace") - .runFixture(fixtureRoot) { build() } - - assertThat(result.task(":preparePaparazziDebugResources")).isNotNull() - assertThat(result.task(":testDebugUnitTest")).isNotNull() - - val snapshotsDir = File(fixtureRoot, "build/reports/paparazzi/debug/images") - val snapshots = snapshotsDir.listFiles() - assertThat(snapshots!!).hasLength(1) - - val snapshotImage = snapshots[0] - val goldenImage = File(fixtureRoot, "src/test/resources/launch.png") - assertThat(snapshotImage).isSimilarTo(goldenImage).withDefaultThreshold() - } - - @Test - fun flagLegacyResourceLoadingIsOff() { - val fixtureRoot = File("src/test/projects/flag-legacy-resource-loading-off") - - val result = gradleRunner - .withArguments("testDebug", "--stacktrace") - .runFixture(fixtureRoot) { build() } - - assertThat(result.task(":preparePaparazziDebugResources")).isNotNull() - assertThat(result.task(":testDebugUnitTest")).isNotNull() - - val snapshotsDir = File(fixtureRoot, "build/reports/paparazzi/debug/images") - val snapshots = snapshotsDir.listFiles() - assertThat(snapshots!!).hasLength(1) - - val snapshotImage = snapshots[0] - val goldenImage = File(fixtureRoot, "src/test/resources/launch.png") - assertThat(snapshotImage).isSimilarTo(goldenImage).withDefaultThreshold() - } - @Test fun cacheable() { val fixtureRoot = File("src/test/projects/cacheable") @@ -821,8 +752,6 @@ class PaparazziPluginTest { val config = resourcesFile.loadConfig() assertThat(config.mainPackage).isEqualTo("app.cash.paparazzi.plugin.test") - assertThat(config.mergeResourcesOutputDir).isEqualTo("intermediates/merged_res/debug") - assertThat(config.mergeAssetsOutputDir).isEqualTo("intermediates/assets/debug") assertThat(config.resourcePackageNames).containsExactly( "app.cash.paparazzi.plugin.test", "com.example.mylibrary", @@ -854,8 +783,6 @@ class PaparazziPluginTest { val config = resourcesFile.loadConfig() assertThat(config.mainPackage).isEqualTo("app.cash.paparazzi.plugin.test") - assertThat(config.mergeResourcesOutputDir).isEqualTo("intermediates/merged_res/debug") - assertThat(config.mergeAssetsOutputDir).isEqualTo("intermediates/assets/debug") assertThat(config.resourcePackageNames).containsExactly( "app.cash.paparazzi.plugin.test", "com.example.mylibrary", @@ -887,7 +814,7 @@ class PaparazziPluginTest { firstResourceFile.copyTo(destResourceFile, overwrite = false) val firstRun = gradleRunner - .withArguments(":preparePaparazziDebugResources", "--build-cache", "--stacktrace") + .withArguments("testDebug", "--build-cache", "--stacktrace") .forwardOutput() .runFixture(fixtureRoot) { build() } @@ -895,6 +822,10 @@ class PaparazziPluginTest { assertThat(this).isNotNull() assertThat(this!!.outcome).isEqualTo(SUCCESS) } + with(firstRun.task(":testDebugUnitTest")) { + assertThat(this).isNotNull() + assertThat(this!!.outcome).isEqualTo(SUCCESS) + } val resourcesFile = File(fixtureRoot, "build/intermediates/paparazzi/debug/resources.json") @@ -907,13 +838,17 @@ class PaparazziPluginTest { secondResourceFile.copyTo(destResourceFile, overwrite = true) val secondRun = gradleRunner - .withArguments(":preparePaparazziDebugResources", "--build-cache", "--stacktrace") + .withArguments(":testDebug", "--build-cache", "--stacktrace") .forwardOutput() .runFixture(fixtureRoot) { build() } with(secondRun.task(":preparePaparazziDebugResources")) { assertThat(this).isNotNull() - assertThat(this!!.outcome).isEqualTo(SUCCESS) + assertThat(this!!.outcome).isEqualTo(FROM_CACHE) // paths didn't change + } + with(secondRun.task(":testDebugUnitTest")) { + assertThat(this).isNotNull() + assertThat(this!!.outcome).isEqualTo(SUCCESS) // but contents did } config = resourcesFile.loadConfig() @@ -938,7 +873,7 @@ class PaparazziPluginTest { firstResourceFile.copyTo(destResourceFile, overwrite = false) val firstRun = gradleRunner - .withArguments(":consumer:preparePaparazziDebugResources", "--build-cache", "--stacktrace") + .withArguments(":consumer:testDebug", "--build-cache", "--stacktrace") .forwardOutput() .runFixture(fixtureRoot) { build() } @@ -946,6 +881,10 @@ class PaparazziPluginTest { assertThat(this).isNotNull() assertThat(this!!.outcome).isEqualTo(SUCCESS) } + with(firstRun.task(":consumer:testDebugUnitTest")) { + assertThat(this).isNotNull() + assertThat(this!!.outcome).isEqualTo(SUCCESS) + } val resourcesFile = File(consumerModuleRoot, "build/intermediates/paparazzi/debug/resources.json") @@ -958,13 +897,17 @@ class PaparazziPluginTest { secondResourceFile.copyTo(destResourceFile, overwrite = true) val secondRun = gradleRunner - .withArguments(":consumer:preparePaparazziDebugResources", "--build-cache", "--stacktrace") + .withArguments(":consumer:testDebug", "--build-cache", "--stacktrace") .forwardOutput() .runFixture(fixtureRoot) { build() } with(secondRun.task(":consumer:preparePaparazziDebugResources")) { assertThat(this).isNotNull() - assertThat(this!!.outcome).isEqualTo(SUCCESS) + assertThat(this!!.outcome).isEqualTo(FROM_CACHE) // paths didn't change + } + with(secondRun.task(":consumer:testDebugUnitTest")) { + assertThat(this).isNotNull() + assertThat(this!!.outcome).isEqualTo(SUCCESS) // but contents did } config = resourcesFile.loadConfig() @@ -1031,7 +974,7 @@ class PaparazziPluginTest { firstAssetFile.copyTo(destAssetFile, overwrite = false) val firstRun = gradleRunner - .withArguments(":preparePaparazziDebugResources", "--build-cache", "--stacktrace") + .withArguments("testDebug", "--build-cache", "--stacktrace") .forwardOutput() .runFixture(fixtureRoot) { build() } @@ -1040,6 +983,11 @@ class PaparazziPluginTest { assertThat(this!!.outcome).isEqualTo(SUCCESS) } + with(firstRun.task(":testDebugUnitTest")) { + assertThat(this).isNotNull() + assertThat(this!!.outcome).isEqualTo(SUCCESS) + } + val resourcesFile = File(fixtureRoot, "build/intermediates/paparazzi/debug/resources.json") var config = resourcesFile.loadConfig() @@ -1051,13 +999,18 @@ class PaparazziPluginTest { secondAssetFile.copyTo(destAssetFile, overwrite = true) val secondRun = gradleRunner - .withArguments(":preparePaparazziDebugResources", "--build-cache", "--stacktrace") + .withArguments(":testDebug", "--build-cache", "--stacktrace") .forwardOutput() .runFixture(fixtureRoot) { build() } with(secondRun.task(":preparePaparazziDebugResources")) { assertThat(this).isNotNull() - assertThat(this!!.outcome).isEqualTo(SUCCESS) + assertThat(this!!.outcome).isEqualTo(FROM_CACHE) // paths didn't change + } + + with(secondRun.task(":testDebugUnitTest")) { + assertThat(this).isNotNull() + assertThat(this!!.outcome).isEqualTo(SUCCESS) // but contents did } config = resourcesFile.loadConfig() @@ -1082,7 +1035,7 @@ class PaparazziPluginTest { firstAssetFile.copyTo(destAssetFile, overwrite = false) val firstRun = gradleRunner - .withArguments(":consumer:preparePaparazziDebugResources", "--build-cache", "--stacktrace") + .withArguments(":consumer:testDebug", "--build-cache", "--stacktrace") .forwardOutput() .runFixture(fixtureRoot) { build() } @@ -1091,6 +1044,11 @@ class PaparazziPluginTest { assertThat(this!!.outcome).isEqualTo(SUCCESS) } + with(firstRun.task(":consumer:testDebugUnitTest")) { + assertThat(this).isNotNull() + assertThat(this!!.outcome).isEqualTo(SUCCESS) + } + val resourcesFile = File(consumerModuleRoot, "build/intermediates/paparazzi/debug/resources.json") var config = resourcesFile.loadConfig() @@ -1106,13 +1064,18 @@ class PaparazziPluginTest { secondAssetFile.copyTo(destAssetFile, overwrite = true) val secondRun = gradleRunner - .withArguments(":consumer:preparePaparazziDebugResources", "--build-cache", "--stacktrace") + .withArguments(":consumer:testDebug", "--build-cache", "--stacktrace") .forwardOutput() .runFixture(fixtureRoot) { build() } with(secondRun.task(":consumer:preparePaparazziDebugResources")) { assertThat(this).isNotNull() - assertThat(this!!.outcome).isEqualTo(SUCCESS) + assertThat(this!!.outcome).isEqualTo(FROM_CACHE) // paths didn't change + } + + with(secondRun.task(":consumer:testDebugUnitTest")) { + assertThat(this).isNotNull() + assertThat(this!!.outcome).isEqualTo(SUCCESS) // but contents did } config = resourcesFile.loadConfig() @@ -1205,17 +1168,8 @@ class PaparazziPluginTest { } @Test - fun verifyOpenAssetsLegacyAssetLoadingIsOff() { - val fixtureRoot = File("src/test/projects/open-assets-legacy-asset-loading-off") - - gradleRunner - .withArguments("consumer:testDebug", "--stacktrace") - .runFixture(fixtureRoot) { build() } - } - - @Test - fun verifyOpenAssetsLegacyAssetLoadingIsOn() { - val fixtureRoot = File("src/test/projects/open-assets-legacy-asset-loading-on") + fun verifyOpenAssets() { + val fixtureRoot = File("src/test/projects/open-assets") gradleRunner .withArguments("consumer:testDebug", "--stacktrace") @@ -1712,7 +1666,49 @@ class PaparazziPluginTest { assertThat(jacocoExecutionData.exists()).isTrue() } - private fun File.loadConfig() = CONFIG_ADAPTER.fromJson(source().buffer())!! + @Test + fun screenOrientation() { + val fixtureRoot = File("src/test/projects/verify-orientation") + + gradleRunner + .withArguments("testDebug", "--stacktrace") + .runFixture(fixtureRoot) { build() } + + val snapshotsDir = File(fixtureRoot, "build/reports/paparazzi/debug/images") + val snapshots = snapshotsDir.listFiles()?.sortedBy { it.lastModified() } + assertThat(snapshots!!).hasSize(2) + + val portraitSnapshotImage = snapshots[0] + val portraitGoldenImage = File(fixtureRoot, "src/test/resources/portrait_orientation.png") + assertThat(portraitSnapshotImage).isSimilarTo(portraitGoldenImage).withDefaultThreshold() + + val landscapeSnapshotImage = snapshots[1] + val landscapeGoldenImage = File(fixtureRoot, "src/test/resources/landscape_orientation.png") + assertThat(landscapeSnapshotImage).isSimilarTo(landscapeGoldenImage).withDefaultThreshold() + } + + @Test + fun screenRound() { + val fixtureRoot = File("src/test/projects/verify-screen-round") + + gradleRunner + .withArguments("testDebug", "--stacktrace") + .runFixture(fixtureRoot) { build() } + + val snapshotsDir = File(fixtureRoot, "build/reports/paparazzi/debug/images") + val snapshots = snapshotsDir.listFiles()?.sortedBy { it.lastModified() } + assertThat(snapshots!!).hasSize(2) + + val roundSnapshot = snapshots[0] + val roundGoldenImage = File(fixtureRoot, "src/test/resources/round.png") + assertThat(roundSnapshot).isSimilarTo(roundGoldenImage).withDefaultThreshold() + + val notRoundSnapshot = snapshots[1] + val notRoundGoldenImage = File(fixtureRoot, "src/test/resources/not_round.png") + assertThat(notRoundSnapshot).isSimilarTo(notRoundGoldenImage).withDefaultThreshold() + } + + private fun File.loadConfig() = source().buffer().use { CONFIG_ADAPTER.fromJson(it)!! } private fun GradleRunner.runFixture( projectRoot: File, diff --git a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-off/src/main/res/drawable/camera.png b/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-off/src/main/res/drawable/camera.png deleted file mode 100644 index 6f37e30a98..0000000000 Binary files a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-off/src/main/res/drawable/camera.png and /dev/null differ diff --git a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-off/src/main/res/layout/launch.xml b/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-off/src/main/res/layout/launch.xml deleted file mode 100644 index 058b420e75..0000000000 --- a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-off/src/main/res/layout/launch.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - diff --git a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-off/src/main/res/values/colors.xml b/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-off/src/main/res/values/colors.xml deleted file mode 100644 index cc0f1ba2db..0000000000 --- a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-off/src/main/res/values/colors.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - #dcdccc - #332F21 - diff --git a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-off/src/test/java/app/cash/paparazzi/plugin/test/LaunchViewTest.kt b/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-off/src/test/java/app/cash/paparazzi/plugin/test/LaunchViewTest.kt deleted file mode 100644 index 407a87ec32..0000000000 --- a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-off/src/test/java/app/cash/paparazzi/plugin/test/LaunchViewTest.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2019 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package app.cash.paparazzi.plugin.test - -import android.widget.LinearLayout -import app.cash.paparazzi.Paparazzi -import org.junit.Rule -import org.junit.Test - -class LaunchViewTest { - @get:Rule - val paparazzi = Paparazzi() - - @Test - fun testViews() { - val launch = paparazzi.inflate(R.layout.launch) - paparazzi.snapshot(launch, "launch") - } -} diff --git a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-off/src/test/resources/launch.png b/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-off/src/test/resources/launch.png deleted file mode 100644 index 8efeabbbab..0000000000 Binary files a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-off/src/test/resources/launch.png and /dev/null differ diff --git a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-on/gradle.properties b/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-on/gradle.properties deleted file mode 100644 index 3de9cb5c45..0000000000 --- a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-on/gradle.properties +++ /dev/null @@ -1,2 +0,0 @@ -android.useAndroidX=true -app.cash.paparazzi.legacy.resource.loading=true diff --git a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-on/src/main/res/drawable/camera.png b/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-on/src/main/res/drawable/camera.png deleted file mode 100644 index 6f37e30a98..0000000000 Binary files a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-on/src/main/res/drawable/camera.png and /dev/null differ diff --git a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-on/src/main/res/layout/launch.xml b/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-on/src/main/res/layout/launch.xml deleted file mode 100644 index 058b420e75..0000000000 --- a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-on/src/main/res/layout/launch.xml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - diff --git a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-on/src/main/res/values/colors.xml b/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-on/src/main/res/values/colors.xml deleted file mode 100644 index cc0f1ba2db..0000000000 --- a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-on/src/main/res/values/colors.xml +++ /dev/null @@ -1,5 +0,0 @@ - - - #dcdccc - #332F21 - diff --git a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-on/src/test/java/app/cash/paparazzi/plugin/test/LaunchViewTest.kt b/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-on/src/test/java/app/cash/paparazzi/plugin/test/LaunchViewTest.kt deleted file mode 100644 index 407a87ec32..0000000000 --- a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-on/src/test/java/app/cash/paparazzi/plugin/test/LaunchViewTest.kt +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (C) 2019 Square, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package app.cash.paparazzi.plugin.test - -import android.widget.LinearLayout -import app.cash.paparazzi.Paparazzi -import org.junit.Rule -import org.junit.Test - -class LaunchViewTest { - @get:Rule - val paparazzi = Paparazzi() - - @Test - fun testViews() { - val launch = paparazzi.inflate(R.layout.launch) - paparazzi.snapshot(launch, "launch") - } -} diff --git a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-on/src/test/resources/launch.png b/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-on/src/test/resources/launch.png deleted file mode 100644 index 8efeabbbab..0000000000 Binary files a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-on/src/test/resources/launch.png and /dev/null differ diff --git a/paparazzi-gradle-plugin/src/test/projects/invalid-application-plugin/build.gradle b/paparazzi-gradle-plugin/src/test/projects/invalid-application-plugin/build.gradle deleted file mode 100644 index 896551eae1..0000000000 --- a/paparazzi-gradle-plugin/src/test/projects/invalid-application-plugin/build.gradle +++ /dev/null @@ -1,6 +0,0 @@ -plugins { - id 'com.android.application' - id 'app.cash.paparazzi' -} - -apply from: '../guava-fix.gradle' diff --git a/paparazzi-gradle-plugin/src/test/projects/invalid-application-plugin/gradle.properties b/paparazzi-gradle-plugin/src/test/projects/invalid-application-plugin/gradle.properties deleted file mode 100644 index 3de9cb5c45..0000000000 --- a/paparazzi-gradle-plugin/src/test/projects/invalid-application-plugin/gradle.properties +++ /dev/null @@ -1,2 +0,0 @@ -android.useAndroidX=true -app.cash.paparazzi.legacy.resource.loading=true diff --git a/paparazzi-gradle-plugin/src/test/projects/missing-library-plugin/build.gradle b/paparazzi-gradle-plugin/src/test/projects/missing-library-plugin/build.gradle deleted file mode 100644 index 420422a2cc..0000000000 --- a/paparazzi-gradle-plugin/src/test/projects/missing-library-plugin/build.gradle +++ /dev/null @@ -1,5 +0,0 @@ -plugins { - id 'app.cash.paparazzi' -} - -apply from: '../guava-fix.gradle' diff --git a/paparazzi-gradle-plugin/src/test/projects/missing-library-plugin/gradle.properties b/paparazzi-gradle-plugin/src/test/projects/missing-library-plugin/gradle.properties deleted file mode 100644 index 3de9cb5c45..0000000000 --- a/paparazzi-gradle-plugin/src/test/projects/missing-library-plugin/gradle.properties +++ /dev/null @@ -1,2 +0,0 @@ -android.useAndroidX=true -app.cash.paparazzi.legacy.resource.loading=true diff --git a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/consumer/build.gradle b/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/consumer/build.gradle deleted file mode 100644 index 2bcf414032..0000000000 --- a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/consumer/build.gradle +++ /dev/null @@ -1,29 +0,0 @@ -plugins { - id 'com.android.library' - id 'kotlin-android' - id 'app.cash.paparazzi' -} - -android { - namespace 'app.cash.paparazzi.plugin.test.consumer' - compileSdk libs.versions.compileSdk.get() as int - defaultConfig { - minSdk libs.versions.minSdk.get() as int - } - compileOptions { - sourceCompatibility = libs.versions.javaTarget.get() - targetCompatibility = libs.versions.javaTarget.get() - } - kotlinOptions { - jvmTarget = libs.versions.javaTarget.get() - } -} - -dependencies { - implementation files('libs/external.aar') - implementation projects.producer1 - implementation projects.producer2 - testImplementation libs.truth -} - -apply from: '../../guava-fix.gradle' diff --git a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/consumer/libs/external.aar b/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/consumer/libs/external.aar deleted file mode 100644 index c2354a94fd..0000000000 Binary files a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/consumer/libs/external.aar and /dev/null differ diff --git a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/consumer/src/main/assets/consumer/secret.txt b/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/consumer/src/main/assets/consumer/secret.txt deleted file mode 100644 index 24a7421a2e..0000000000 --- a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/consumer/src/main/assets/consumer/secret.txt +++ /dev/null @@ -1 +0,0 @@ -consumer \ No newline at end of file diff --git a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/consumer/src/test/java/app/cash/paparazzi/plugin/test/AssetAccessTest.kt b/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/consumer/src/test/java/app/cash/paparazzi/plugin/test/AssetAccessTest.kt deleted file mode 100644 index e99d982086..0000000000 --- a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/consumer/src/test/java/app/cash/paparazzi/plugin/test/AssetAccessTest.kt +++ /dev/null @@ -1,27 +0,0 @@ -package app.cash.paparazzi.plugin.test - -import app.cash.paparazzi.Paparazzi -import com.google.common.truth.Truth.assertThat -import org.junit.Rule -import org.junit.Test - -class AssetAccessTest { - @get:Rule - val paparazzi = Paparazzi() - - @Test - fun testViews() { - val pairs = mapOf( - "consumer/secret.txt" to "consumer", - "producer1/secret.txt" to "producer1", - "producer2/secret.txt" to "producer2", - "external/secret.txt" to "external" - ) - - pairs.forEach { (key, value) -> - val contents = - paparazzi.context.assets.open(key).bufferedReader().use { it.readText() } - assertThat(contents).isEqualTo(value) - } - } -} diff --git a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/gradle.properties b/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/gradle.properties deleted file mode 100644 index a2dfe3aa9b..0000000000 --- a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/gradle.properties +++ /dev/null @@ -1,2 +0,0 @@ -android.useAndroidX=true -app.cash.paparazzi.legacy.asset.loading=true diff --git a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/producer1/build.gradle b/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/producer1/build.gradle deleted file mode 100644 index a701fe70aa..0000000000 --- a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/producer1/build.gradle +++ /dev/null @@ -1,19 +0,0 @@ -plugins { - id 'com.android.library' - id 'kotlin-android' -} - -android { - namespace 'app.cash.paparazzi.plugin.test.producer1' - compileSdk libs.versions.compileSdk.get() as int - defaultConfig { - minSdk libs.versions.minSdk.get() as int - } - compileOptions { - sourceCompatibility = libs.versions.javaTarget.get() - targetCompatibility = libs.versions.javaTarget.get() - } - kotlinOptions { - jvmTarget = libs.versions.javaTarget.get() - } -} diff --git a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/producer1/src/main/assets/producer1/secret.txt b/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/producer1/src/main/assets/producer1/secret.txt deleted file mode 100644 index 602a305e1e..0000000000 --- a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/producer1/src/main/assets/producer1/secret.txt +++ /dev/null @@ -1 +0,0 @@ -producer1 \ No newline at end of file diff --git a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/producer2/build.gradle b/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/producer2/build.gradle deleted file mode 100644 index 35ca4059a2..0000000000 --- a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/producer2/build.gradle +++ /dev/null @@ -1,19 +0,0 @@ -plugins { - id 'com.android.library' - id 'kotlin-android' -} - -android { - namespace 'app.cash.paparazzi.plugin.test.producer2' - compileSdk libs.versions.compileSdk.get() as int - defaultConfig { - minSdk libs.versions.minSdk.get() as int - } - compileOptions { - sourceCompatibility = libs.versions.javaTarget.get() - targetCompatibility = libs.versions.javaTarget.get() - } - kotlinOptions { - jvmTarget = libs.versions.javaTarget.get() - } -} diff --git a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/producer2/src/main/assets/producer2/secret.txt b/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/producer2/src/main/assets/producer2/secret.txt deleted file mode 100644 index e5c372f0e2..0000000000 --- a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/producer2/src/main/assets/producer2/secret.txt +++ /dev/null @@ -1 +0,0 @@ -producer2 \ No newline at end of file diff --git a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/settings.gradle b/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/settings.gradle deleted file mode 100644 index 41f133f67c..0000000000 --- a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-on/settings.gradle +++ /dev/null @@ -1,5 +0,0 @@ -apply from: '../test.settings.gradle' - -include ':consumer' -include ':producer1' -include ':producer2' diff --git a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-off/consumer/build.gradle b/paparazzi-gradle-plugin/src/test/projects/open-assets/consumer/build.gradle similarity index 100% rename from paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-off/consumer/build.gradle rename to paparazzi-gradle-plugin/src/test/projects/open-assets/consumer/build.gradle diff --git a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-off/consumer/libs/external.aar b/paparazzi-gradle-plugin/src/test/projects/open-assets/consumer/libs/external.aar similarity index 100% rename from paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-off/consumer/libs/external.aar rename to paparazzi-gradle-plugin/src/test/projects/open-assets/consumer/libs/external.aar diff --git a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-off/consumer/src/main/assets/consumer/secret.txt b/paparazzi-gradle-plugin/src/test/projects/open-assets/consumer/src/main/assets/consumer/secret.txt similarity index 100% rename from paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-off/consumer/src/main/assets/consumer/secret.txt rename to paparazzi-gradle-plugin/src/test/projects/open-assets/consumer/src/main/assets/consumer/secret.txt diff --git a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-off/consumer/src/test/java/app/cash/paparazzi/plugin/test/AssetAccessTest.kt b/paparazzi-gradle-plugin/src/test/projects/open-assets/consumer/src/test/java/app/cash/paparazzi/plugin/test/AssetAccessTest.kt similarity index 100% rename from paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-off/consumer/src/test/java/app/cash/paparazzi/plugin/test/AssetAccessTest.kt rename to paparazzi-gradle-plugin/src/test/projects/open-assets/consumer/src/test/java/app/cash/paparazzi/plugin/test/AssetAccessTest.kt diff --git a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-off/producer1/build.gradle b/paparazzi-gradle-plugin/src/test/projects/open-assets/producer1/build.gradle similarity index 100% rename from paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-off/producer1/build.gradle rename to paparazzi-gradle-plugin/src/test/projects/open-assets/producer1/build.gradle diff --git a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-off/producer1/src/main/assets/producer1/secret.txt b/paparazzi-gradle-plugin/src/test/projects/open-assets/producer1/src/main/assets/producer1/secret.txt similarity index 100% rename from paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-off/producer1/src/main/assets/producer1/secret.txt rename to paparazzi-gradle-plugin/src/test/projects/open-assets/producer1/src/main/assets/producer1/secret.txt diff --git a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-off/producer2/build.gradle b/paparazzi-gradle-plugin/src/test/projects/open-assets/producer2/build.gradle similarity index 100% rename from paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-off/producer2/build.gradle rename to paparazzi-gradle-plugin/src/test/projects/open-assets/producer2/build.gradle diff --git a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-off/producer2/src/main/assets/producer2/secret.txt b/paparazzi-gradle-plugin/src/test/projects/open-assets/producer2/src/main/assets/producer2/secret.txt similarity index 100% rename from paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-off/producer2/src/main/assets/producer2/secret.txt rename to paparazzi-gradle-plugin/src/test/projects/open-assets/producer2/src/main/assets/producer2/secret.txt diff --git a/paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-off/settings.gradle b/paparazzi-gradle-plugin/src/test/projects/open-assets/settings.gradle similarity index 100% rename from paparazzi-gradle-plugin/src/test/projects/open-assets-legacy-asset-loading-off/settings.gradle rename to paparazzi-gradle-plugin/src/test/projects/open-assets/settings.gradle diff --git a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-on/build.gradle b/paparazzi-gradle-plugin/src/test/projects/verify-orientation/build.gradle similarity index 88% rename from paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-on/build.gradle rename to paparazzi-gradle-plugin/src/test/projects/verify-orientation/build.gradle index bf279f74e7..9bdb01581c 100644 --- a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-on/build.gradle +++ b/paparazzi-gradle-plugin/src/test/projects/verify-orientation/build.gradle @@ -19,4 +19,8 @@ android { } } +dependencies { + testImplementation libs.testParameterInjector +} + apply from: '../guava-fix.gradle' diff --git a/paparazzi-gradle-plugin/src/test/projects/verify-orientation/src/test/java/app/cash/paparazzi/plugin/test/ScreenOrientationTest.kt b/paparazzi-gradle-plugin/src/test/projects/verify-orientation/src/test/java/app/cash/paparazzi/plugin/test/ScreenOrientationTest.kt new file mode 100644 index 0000000000..a8b88b3d79 --- /dev/null +++ b/paparazzi-gradle-plugin/src/test/projects/verify-orientation/src/test/java/app/cash/paparazzi/plugin/test/ScreenOrientationTest.kt @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2019 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package app.cash.paparazzi.plugin.test + +import android.widget.FrameLayout +import app.cash.paparazzi.DeviceConfig.Companion.PIXEL_C +import app.cash.paparazzi.Paparazzi +import com.android.resources.ScreenOrientation +import com.google.testing.junit.testparameterinjector.TestParameter +import com.google.testing.junit.testparameterinjector.TestParameterInjector +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(TestParameterInjector::class) +class ScreenOrientationTest( + @TestParameter val configuration: OrientationTestConfiguration +) { + @get:Rule + val paparazzi = Paparazzi(deviceConfig = PIXEL_C.copy(orientation = configuration.orientation)) + + @Test + fun test() { + paparazzi.snapshot( + view = FrameLayout(paparazzi.context).apply { + setBackgroundColor(configuration.backgroundColor) + } + ) + } +} + +enum class OrientationTestConfiguration( + val orientation: ScreenOrientation, + val backgroundColor: Int +) { + PORTRAIT( + orientation = ScreenOrientation.PORTRAIT, + backgroundColor = -1 + ), + LANDSCAPE( + orientation = ScreenOrientation.LANDSCAPE, + backgroundColor = 0 + ) +} diff --git a/paparazzi-gradle-plugin/src/test/projects/verify-orientation/src/test/resources/landscape_orientation.png b/paparazzi-gradle-plugin/src/test/projects/verify-orientation/src/test/resources/landscape_orientation.png new file mode 100644 index 0000000000..f81b93158f Binary files /dev/null and b/paparazzi-gradle-plugin/src/test/projects/verify-orientation/src/test/resources/landscape_orientation.png differ diff --git a/paparazzi-gradle-plugin/src/test/projects/verify-orientation/src/test/resources/portrait_orientation.png b/paparazzi-gradle-plugin/src/test/projects/verify-orientation/src/test/resources/portrait_orientation.png new file mode 100644 index 0000000000..facc80319c Binary files /dev/null and b/paparazzi-gradle-plugin/src/test/projects/verify-orientation/src/test/resources/portrait_orientation.png differ diff --git a/paparazzi-gradle-plugin/src/test/projects/verify-orientation/src/test/snapshots/images/app.cash.paparazzi.plugin.test_ScreenOrientationTest_test[LANDSCAPE].png b/paparazzi-gradle-plugin/src/test/projects/verify-orientation/src/test/snapshots/images/app.cash.paparazzi.plugin.test_ScreenOrientationTest_test[LANDSCAPE].png new file mode 100644 index 0000000000..f81b93158f Binary files /dev/null and b/paparazzi-gradle-plugin/src/test/projects/verify-orientation/src/test/snapshots/images/app.cash.paparazzi.plugin.test_ScreenOrientationTest_test[LANDSCAPE].png differ diff --git a/paparazzi-gradle-plugin/src/test/projects/verify-orientation/src/test/snapshots/images/app.cash.paparazzi.plugin.test_ScreenOrientationTest_test[PORTRAIT].png b/paparazzi-gradle-plugin/src/test/projects/verify-orientation/src/test/snapshots/images/app.cash.paparazzi.plugin.test_ScreenOrientationTest_test[PORTRAIT].png new file mode 100644 index 0000000000..facc80319c Binary files /dev/null and b/paparazzi-gradle-plugin/src/test/projects/verify-orientation/src/test/snapshots/images/app.cash.paparazzi.plugin.test_ScreenOrientationTest_test[PORTRAIT].png differ diff --git a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-off/build.gradle b/paparazzi-gradle-plugin/src/test/projects/verify-screen-round/build.gradle similarity index 85% rename from paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-off/build.gradle rename to paparazzi-gradle-plugin/src/test/projects/verify-screen-round/build.gradle index d7ac752a69..9bdb01581c 100644 --- a/paparazzi-gradle-plugin/src/test/projects/flag-legacy-resource-loading-off/build.gradle +++ b/paparazzi-gradle-plugin/src/test/projects/verify-screen-round/build.gradle @@ -19,10 +19,8 @@ android { } } -tasks.withType(Test).configureEach { - testLogging { - showStandardStreams true - } +dependencies { + testImplementation libs.testParameterInjector } apply from: '../guava-fix.gradle' diff --git a/paparazzi-gradle-plugin/src/test/projects/verify-screen-round/src/main/res/layout-round/custom_view.xml b/paparazzi-gradle-plugin/src/test/projects/verify-screen-round/src/main/res/layout-round/custom_view.xml new file mode 100644 index 0000000000..13e097e268 --- /dev/null +++ b/paparazzi-gradle-plugin/src/test/projects/verify-screen-round/src/main/res/layout-round/custom_view.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/paparazzi-gradle-plugin/src/test/projects/verify-screen-round/src/main/res/layout/custom_view.xml b/paparazzi-gradle-plugin/src/test/projects/verify-screen-round/src/main/res/layout/custom_view.xml new file mode 100644 index 0000000000..af764aff10 --- /dev/null +++ b/paparazzi-gradle-plugin/src/test/projects/verify-screen-round/src/main/res/layout/custom_view.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/paparazzi-gradle-plugin/src/test/projects/verify-screen-round/src/test/java/app/cash/paparazzi/plugin/test/RoundViewTest.kt b/paparazzi-gradle-plugin/src/test/projects/verify-screen-round/src/test/java/app/cash/paparazzi/plugin/test/RoundViewTest.kt new file mode 100644 index 0000000000..a9ee401eef --- /dev/null +++ b/paparazzi-gradle-plugin/src/test/projects/verify-screen-round/src/test/java/app/cash/paparazzi/plugin/test/RoundViewTest.kt @@ -0,0 +1,43 @@ +package app.cash.paparazzi.plugin.test + +import app.cash.paparazzi.DeviceConfig +import app.cash.paparazzi.Paparazzi +import com.android.resources.ScreenRound +import com.google.testing.junit.testparameterinjector.TestParameter +import com.google.testing.junit.testparameterinjector.TestParameterInjector +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(TestParameterInjector::class) +class RoundViewTest( + @TestParameter val configuration: ScreenRoundTestConfiguration +) { + @get:Rule + val paparazzi = Paparazzi( + deviceConfig = DeviceConfig.GALAXY_WATCH4_CLASSIC_LARGE.copy(screenRound = configuration.round), + theme = "android:Theme.Material.Light.NoActionBar" + ) + + @Test + fun test() { + paparazzi.snapshot( + view = paparazzi.inflate(R.layout.custom_view), + name = configuration.testName + ) + } +} + +enum class ScreenRoundTestConfiguration( + val round: ScreenRound, + val testName: String +) { + ROUND( + round = ScreenRound.ROUND, + testName = "round" + ), + NOT_ROUND( + round = ScreenRound.NOTROUND, + testName = "not_round" + ) +} diff --git a/paparazzi-gradle-plugin/src/test/projects/verify-screen-round/src/test/resources/not_round.png b/paparazzi-gradle-plugin/src/test/projects/verify-screen-round/src/test/resources/not_round.png new file mode 100644 index 0000000000..1077169523 Binary files /dev/null and b/paparazzi-gradle-plugin/src/test/projects/verify-screen-round/src/test/resources/not_round.png differ diff --git a/paparazzi-gradle-plugin/src/test/projects/verify-screen-round/src/test/resources/round.png b/paparazzi-gradle-plugin/src/test/projects/verify-screen-round/src/test/resources/round.png new file mode 100644 index 0000000000..0b6bcf6542 Binary files /dev/null and b/paparazzi-gradle-plugin/src/test/projects/verify-screen-round/src/test/resources/round.png differ diff --git a/paparazzi-gradle-plugin/src/test/projects/verify-update-local-assets-change/build.gradle b/paparazzi-gradle-plugin/src/test/projects/verify-update-local-assets-change/build.gradle index ce7a5a92ac..411ab6a9d7 100644 --- a/paparazzi-gradle-plugin/src/test/projects/verify-update-local-assets-change/build.gradle +++ b/paparazzi-gradle-plugin/src/test/projects/verify-update-local-assets-change/build.gradle @@ -14,4 +14,7 @@ android { sourceCompatibility = libs.versions.javaTarget.get() targetCompatibility = libs.versions.javaTarget.get() } + kotlinOptions { + jvmTarget = libs.versions.javaTarget.get() + } } diff --git a/paparazzi-gradle-plugin/src/test/projects/verify-update-local-assets-change/src/test/java/app/cash/paparazzi/plugin/test/DummyTest.kt b/paparazzi-gradle-plugin/src/test/projects/verify-update-local-assets-change/src/test/java/app/cash/paparazzi/plugin/test/DummyTest.kt new file mode 100644 index 0000000000..acfed3604f --- /dev/null +++ b/paparazzi-gradle-plugin/src/test/projects/verify-update-local-assets-change/src/test/java/app/cash/paparazzi/plugin/test/DummyTest.kt @@ -0,0 +1,7 @@ +package app.cash.paparazzi.plugin.test + +import org.junit.Test + +class DummyTest { + @Test fun dummy() {} +} diff --git a/paparazzi-gradle-plugin/src/test/projects/verify-update-local-resources-change/build.gradle b/paparazzi-gradle-plugin/src/test/projects/verify-update-local-resources-change/build.gradle index ce7a5a92ac..411ab6a9d7 100644 --- a/paparazzi-gradle-plugin/src/test/projects/verify-update-local-resources-change/build.gradle +++ b/paparazzi-gradle-plugin/src/test/projects/verify-update-local-resources-change/build.gradle @@ -14,4 +14,7 @@ android { sourceCompatibility = libs.versions.javaTarget.get() targetCompatibility = libs.versions.javaTarget.get() } + kotlinOptions { + jvmTarget = libs.versions.javaTarget.get() + } } diff --git a/paparazzi-gradle-plugin/src/test/projects/verify-update-local-resources-change/src/test/java/app/cash/paparazzi/plugin/test/DummyTest.kt b/paparazzi-gradle-plugin/src/test/projects/verify-update-local-resources-change/src/test/java/app/cash/paparazzi/plugin/test/DummyTest.kt new file mode 100644 index 0000000000..acfed3604f --- /dev/null +++ b/paparazzi-gradle-plugin/src/test/projects/verify-update-local-resources-change/src/test/java/app/cash/paparazzi/plugin/test/DummyTest.kt @@ -0,0 +1,7 @@ +package app.cash.paparazzi.plugin.test + +import org.junit.Test + +class DummyTest { + @Test fun dummy() {} +} diff --git a/paparazzi-gradle-plugin/src/test/projects/verify-update-module-assets-change/consumer/src/test/java/app/cash/paparazzi/plugin/test/DummyTest.kt b/paparazzi-gradle-plugin/src/test/projects/verify-update-module-assets-change/consumer/src/test/java/app/cash/paparazzi/plugin/test/DummyTest.kt new file mode 100644 index 0000000000..acfed3604f --- /dev/null +++ b/paparazzi-gradle-plugin/src/test/projects/verify-update-module-assets-change/consumer/src/test/java/app/cash/paparazzi/plugin/test/DummyTest.kt @@ -0,0 +1,7 @@ +package app.cash.paparazzi.plugin.test + +import org.junit.Test + +class DummyTest { + @Test fun dummy() {} +} diff --git a/paparazzi-gradle-plugin/src/test/projects/verify-update-module-resources-change/consumer/src/test/java/app/cash/paparazzi/plugin/test/DummyTest.kt b/paparazzi-gradle-plugin/src/test/projects/verify-update-module-resources-change/consumer/src/test/java/app/cash/paparazzi/plugin/test/DummyTest.kt new file mode 100644 index 0000000000..acfed3604f --- /dev/null +++ b/paparazzi-gradle-plugin/src/test/projects/verify-update-module-resources-change/consumer/src/test/java/app/cash/paparazzi/plugin/test/DummyTest.kt @@ -0,0 +1,7 @@ +package app.cash.paparazzi.plugin.test + +import org.junit.Test + +class DummyTest { + @Test fun dummy() {} +} diff --git a/paparazzi/src/main/java/app/cash/paparazzi/DeviceConfig.kt b/paparazzi/src/main/java/app/cash/paparazzi/DeviceConfig.kt index 3014fb1aea..8e8e3f9ccb 100644 --- a/paparazzi/src/main/java/app/cash/paparazzi/DeviceConfig.kt +++ b/paparazzi/src/main/java/app/cash/paparazzi/DeviceConfig.kt @@ -30,6 +30,7 @@ import com.android.ide.common.resources.configuration.NightModeQualifier import com.android.ide.common.resources.configuration.ScreenDimensionQualifier import com.android.ide.common.resources.configuration.ScreenOrientationQualifier import com.android.ide.common.resources.configuration.ScreenRatioQualifier +import com.android.ide.common.resources.configuration.ScreenRoundQualifier import com.android.ide.common.resources.configuration.ScreenSizeQualifier import com.android.ide.common.resources.configuration.TextInputMethodQualifier import com.android.ide.common.resources.configuration.TouchScreenQualifier @@ -54,7 +55,10 @@ import org.xmlpull.v1.XmlPullParserFactory import java.io.File import java.io.FileInputStream import java.io.IOException +import java.lang.UnsupportedOperationException import java.util.Properties +import kotlin.math.max +import kotlin.math.min /** * Provides [FolderConfiguration] and [HardwareConfig] for various devices. Also provides utility @@ -81,7 +85,7 @@ data class DeviceConfig( val keyboardState: KeyboardState = KeyboardState.SOFT, val softButtons: Boolean = true, val navigation: Navigation = Navigation.NONAV, - val screenRound: com.android.resources.ScreenRound? = null, + val screenRound: ScreenRound? = null, val released: String = "November 13, 2012" ) { val folderConfiguration: FolderConfiguration @@ -108,11 +112,25 @@ data class DeviceConfig( networkCodeQualifier = NetworkCodeQualifier() localeQualifier = if (locale != null) LocaleQualifier.getQualifier(locale) else LocaleQualifier() versionQualifier = VersionQualifier() + screenRoundQualifier = ScreenRoundQualifier(screenRound) } + private val currentWidth: Int + get() = when (orientation) { + ScreenOrientation.PORTRAIT -> min(screenWidth, screenHeight) + ScreenOrientation.LANDSCAPE -> max(screenWidth, screenHeight) + else -> throw UnsupportedOperationException("Only Portrait or Landscape orientations are supported") + } + private val currentHeight: Int + get() = when (orientation) { + ScreenOrientation.PORTRAIT -> max(screenWidth, screenHeight) + ScreenOrientation.LANDSCAPE -> min(screenWidth, screenHeight) + else -> throw UnsupportedOperationException("Only Portrait or Landscape orientations are supported") + } + val hardwareConfig: HardwareConfig get() = HardwareConfig( - screenWidth, screenHeight, density, xdpi.toFloat(), ydpi.toFloat(), size, + currentWidth, currentHeight, density, xdpi.toFloat(), ydpi.toFloat(), size, orientation, screenRound, softButtons ) diff --git a/paparazzi/src/main/java/app/cash/paparazzi/Environment.kt b/paparazzi/src/main/java/app/cash/paparazzi/Environment.kt index bcef135610..0d8eb98379 100644 --- a/paparazzi/src/main/java/app/cash/paparazzi/Environment.kt +++ b/paparazzi/src/main/java/app/cash/paparazzi/Environment.kt @@ -29,8 +29,6 @@ import kotlin.io.path.exists data class Environment( val platformDir: String, val appTestDir: String, - val resDir: String, - val assetsDir: String, val packageName: String, val compileSdkVersion: Int, val resourcePackageNames: List, @@ -66,13 +64,12 @@ fun detectEnvironment(): Environment { val resourcesFile = File(System.getProperty("paparazzi.test.resources")) val moshi = Moshi.Builder().build()!! - val config = moshi.adapter(Config::class.java).fromJson(resourcesFile.source().buffer())!! + val config = + resourcesFile.source().buffer().use { moshi.adapter(Config::class.java).fromJson(it)!! } return Environment( platformDir = androidHome.resolve(config.platformDir).toString(), appTestDir = appTestDir.toString(), - resDir = appTestDir.resolve(config.mergeResourcesOutputDir).toString(), - assetsDir = appTestDir.resolve(config.mergeAssetsOutputDir).toString(), packageName = config.mainPackage, compileSdkVersion = config.targetSdkVersion.toInt(), resourcePackageNames = config.resourcePackageNames, @@ -87,10 +84,8 @@ fun detectEnvironment(): Environment { @JsonClass(generateAdapter = true) data class Config( val mainPackage: String, - val mergeResourcesOutputDir: String, val targetSdkVersion: String, val platformDir: String, - val mergeAssetsOutputDir: String, val resourcePackageNames: List, val projectResourceDirs: List, val moduleResourceDirs: List, diff --git a/paparazzi/src/main/java/app/cash/paparazzi/Flags.kt b/paparazzi/src/main/java/app/cash/paparazzi/Flags.kt index 85bad341f3..62c0b620a0 100644 --- a/paparazzi/src/main/java/app/cash/paparazzi/Flags.kt +++ b/paparazzi/src/main/java/app/cash/paparazzi/Flags.kt @@ -2,6 +2,4 @@ package app.cash.paparazzi object Flags { const val DEBUG_LINKED_OBJECTS = "app.cash.paparazzi.debug.linked.objects" - const val LEGACY_RESOURCE_LOADING = "app.cash.paparazzi.legacy.resource.loading" - const val LEGACY_ASSET_LOADING = "app.cash.paparazzi.legacy.asset.loading" } diff --git a/paparazzi/src/main/java/app/cash/paparazzi/Paparazzi.kt b/paparazzi/src/main/java/app/cash/paparazzi/Paparazzi.kt index 352e1bae93..01946b5d0e 100644 --- a/paparazzi/src/main/java/app/cash/paparazzi/Paparazzi.kt +++ b/paparazzi/src/main/java/app/cash/paparazzi/Paparazzi.kt @@ -69,6 +69,7 @@ import com.android.layoutlib.bridge.Bridge.prepareThread import com.android.layoutlib.bridge.BridgeRenderSession import com.android.layoutlib.bridge.impl.RenderAction import com.android.layoutlib.bridge.impl.RenderSessionImpl +import com.android.resources.ScreenOrientation import com.android.resources.ScreenRound import com.android.tools.idea.validator.LayoutValidator import com.android.tools.idea.validator.ValidatorData.Level @@ -616,9 +617,12 @@ class Paparazzi @JvmOverloads constructor( private fun DeviceConfig.updateIfAccessibilityTest(): DeviceConfig = if (renderExtensions.any { it is AccessibilityRenderExtension }) { + val newWidth = screenWidth * 2 + val newOrientation = if (newWidth > screenHeight) ScreenOrientation.LANDSCAPE else ScreenOrientation.PORTRAIT copy( screenWidth = screenWidth * 2, - softButtons = false + softButtons = false, + orientation = newOrientation ) } else { this diff --git a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/FrameworkResourceItem.java b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/FrameworkResourceItem.java deleted file mode 100644 index f2c07487ea..0000000000 --- a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/FrameworkResourceItem.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated; - -/** - * @deprecated This class is part of an obsolete resource repository system that is no longer used - * in production code. The class is preserved temporarily for LayoutLib tests. - */ -@Deprecated -class FrameworkResourceItem extends ResourceItem { - - FrameworkResourceItem(String name) { - super(name); - } - - @Override - public boolean isEditableDirectly() { - return false; - } - - @Override - public String toString() { - return "FrameworkResourceItem [mName=" + getName() + ", mFiles=" //$NON-NLS-1$ //$NON-NLS-2$ - + getSourceFileList() + "]"; //$NON-NLS-1$ - } -} diff --git a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/FrameworkResources.java b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/FrameworkResources.java deleted file mode 100644 index 792cf914e1..0000000000 --- a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/FrameworkResources.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated; - -import app.cash.paparazzi.deprecated.com.android.io.IAbstractFile; -import app.cash.paparazzi.deprecated.com.android.io.IAbstractFolder; -import com.android.SdkConstants; -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.resources.ResourceType; -import com.android.utils.ILogger; -import com.google.common.base.Charsets; -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumMap; -import java.util.List; -import java.util.Map; -import org.kxml2.io.KXmlParser; -import org.xmlpull.v1.XmlPullParser; - -/** - * @deprecated This class is part of an obsolete resource repository system that is no longer used - * in production code. The class is preserved temporarily for LayoutLib tests. - */ -@Deprecated -public class FrameworkResources extends ResourceRepository { - - /** - * Map of {@link ResourceType} to list of items. It is guaranteed to contain a list for all - * possible values of ResourceType. - */ - protected final Map> mPublicResourceMap = - new EnumMap>(ResourceType.class); - - public FrameworkResources(@NonNull IAbstractFolder resFolder) { - super(resFolder, true /*isFrameworkRepository*/); - } - - /** - * Returns a {@link Collection} (always non null, but can be empty) of public - * {@link ResourceItem} matching a given {@link ResourceType}. - * - * @param type the type of the resources to return - * @return a collection of items, possibly empty. - */ - @Override - @NonNull - public List getResourceItemsOfType(@NonNull ResourceType type) { - return mPublicResourceMap.get(type); - } - - /** - * Returns whether the repository has public resources of a given {@link ResourceType}. - * @param type the type of resource to check. - * @return true if the repository contains resources of the given type, false otherwise. - */ - @Override - public boolean hasResourcesOfType(@NonNull ResourceType type) { - return !mPublicResourceMap.get(type).isEmpty(); - } - - @Override - @NonNull - protected ResourceItem createResourceItem(@NonNull String name) { - return new FrameworkResourceItem(name); - } - - /** - * Reads the public.xml file in data/res/values/ for a given resource folder and builds up - * a map of public resources. - * - * This map is a subset of the full resource map that only contains framework resources - * that are public. - * - * @param logger a logger to report issues to - */ - public void loadPublicResources(@Nullable ILogger logger) { - IAbstractFolder valueFolder = getResFolder().getFolder(SdkConstants.FD_RES_VALUES); - if (!valueFolder.exists()) { - return; - } - - IAbstractFile publicXmlFile = valueFolder.getFile("public.xml"); //$NON-NLS-1$ - if (publicXmlFile.exists()) { - Reader reader = null; - try { - reader = new BufferedReader(new InputStreamReader(publicXmlFile.getContents(), - Charsets.UTF_8)); - KXmlParser parser = new KXmlParser(); - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false); - parser.setInput(reader); - - ResourceType lastType = null; - String lastTypeName = ""; - while (true) { - int event = parser.next(); - if (event == XmlPullParser.START_TAG) { - // As of API 15 there are a number of "java-symbol" entries here - if (!parser.getName().equals("public")) { //$NON-NLS-1$ - continue; - } - - String name = null; - String typeName = null; - for (int i = 0, n = parser.getAttributeCount(); i < n; i++) { - String attribute = parser.getAttributeName(i); - - if (attribute.equals("name")) { //$NON-NLS-1$ - name = parser.getAttributeValue(i); - if (typeName != null) { - // Skip id attribute processing - break; - } - } else if (attribute.equals("type")) { //$NON-NLS-1$ - typeName = parser.getAttributeValue(i); - } - } - - if (name != null && typeName != null) { - ResourceType type = null; - if (typeName.equals(lastTypeName)) { - type = lastType; - } else { - type = ResourceType.fromXmlValue(typeName); - lastType = type; - lastTypeName = typeName; - } - if (type != null) { - ResourceItem match = null; - Map map = mResourceMap.get(type); - if (map != null) { - match = map.get(name); - } - - if (match != null) { - List publicList = mPublicResourceMap.get(type); - if (publicList == null) { - // Pick initial size for the list to hold the public - // resources. We could just use map.size() here, - // but they're usually much bigger; for example, - // in one platform version, there are 1500 drawables - // and 1200 strings but only 175 and 25 public ones - // respectively. - int size; - switch (type) { - case STYLE: size = 500; break; - case ATTR: size = 1050; break; - case DRAWABLE: size = 200; break; - case ID: size = 50; break; - case LAYOUT: - case COLOR: - case STRING: - case ANIM: - case INTERPOLATOR: - size = 30; - break; - default: - size = 10; - break; - } - publicList = new ArrayList(size); - mPublicResourceMap.put(type, publicList); - } - - publicList.add(match); - } else { - // log that there's a public resource that doesn't actually - // exist? - } - } else { - // log that there was a reference to a typo that doesn't actually - // exist? - } - } - } else if (event == XmlPullParser.END_DOCUMENT) { - break; - } - } - } catch (Exception e) { - if (logger != null) { - logger.error(e, "Can't read and parse public attribute list"); - } - } finally { - if (reader != null) { - try { - reader.close(); - } catch (IOException e) { - // Nothing to be done here - we don't care if it closed or not. - } - } - } - } - - // put unmodifiable list for all res type in the public resource map - // this will simplify access - for (ResourceType type : ResourceType.values()) { - List list = mPublicResourceMap.get(type); - if (list == null) { - list = Collections.emptyList(); - } else { - list = Collections.unmodifiableList(list); - } - - // put the new list in the map - mPublicResourceMap.put(type, list); - } - } -} - diff --git a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/IdGeneratingResourceFile.java b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/IdGeneratingResourceFile.java deleted file mode 100644 index 6756f8480b..0000000000 --- a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/IdGeneratingResourceFile.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated; - -import app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated.ValueResourceParser.IValueResourceRepository; -import app.cash.paparazzi.deprecated.com.android.io.IAbstractFile; -import app.cash.paparazzi.deprecated.com.android.io.StreamException; -import com.android.ide.common.rendering.api.DensityBasedResourceValueImpl; -import com.android.ide.common.rendering.api.ResourceNamespace; -import com.android.ide.common.rendering.api.ResourceReference; -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.rendering.api.ResourceValueImpl; -import com.android.ide.common.resources.ResourceValueMap; -import com.android.ide.common.resources.configuration.DensityQualifier; -import com.android.ide.common.resources.configuration.ResourceQualifier; -import com.android.resources.ResourceType; -import java.io.IOException; -import java.util.Collection; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.Set; - -/** - * @deprecated This class is part of an obsolete resource repository system that is no longer used - * in production code. The class is preserved temporarily for LayoutLib tests. - */ -@Deprecated -public final class IdGeneratingResourceFile extends ResourceFile - implements IValueResourceRepository { - - private final ResourceValueMap mIdResources = ResourceValueMap.create(); - - private final Collection mResourceTypeList; - - private final String mFileName; - - private final ResourceType mFileType; - - private final ResourceValue mFileValue; - - public IdGeneratingResourceFile(IAbstractFile file, ResourceFolder folder, ResourceType type) { - super(file, folder); - - mFileType = type; - - // Set up our resource types - mResourceTypeList = EnumSet.of(mFileType, ResourceType.ID); - - // compute the resource name - mFileName = getFileName(type); - - // Get the resource value of this file as a whole layout - mFileValue = getFileValue(file, folder); - } - - @Override - protected void load(ScanningContext context) { - // Parse the file and look for @+id/ entries - parseFileForIds(context); - - // create the resource items in the repository - updateResourceItems(context); - } - - @Override - protected void update(ScanningContext context) { - // Copy the previous list of ID names - Set oldIdNames = new HashSet(mIdResources.keySet()); - - // reset current content. - mIdResources.clear(); - - // need to parse the file and find the IDs. - if (!parseFileForIds(context)) { - context.requestFullAapt(); - // Continue through to updating the resource item here since it - // will make for example layout rendering more accurate until - // aapt is re-run - } - - // We only need to update the repository if our IDs have changed - Set keySet = mIdResources.keySet(); - assert keySet != oldIdNames; - if (oldIdNames.equals(keySet) == false) { - updateResourceItems(context); - } - } - - @Override - protected void dispose(ScanningContext context) { - ResourceRepository repository = getRepository(); - - // Remove declarations from this file from the repository - repository.removeFile(mResourceTypeList, this); - - // Ask for an ID refresh since we'll be taking away ID generating items - context.requestFullAapt(); - } - - @Override - public Collection getResourceTypes() { - return mResourceTypeList; - } - - @Override - public boolean hasResources(ResourceType type) { - return (type == mFileType) || (type == ResourceType.ID && !mIdResources.isEmpty()); - } - - @Override - public ResourceValue getValue(ResourceType type, String name) { - // Check to see if they're asking for one of the right types: - if (type != mFileType && type != ResourceType.ID) { - return null; - } - - // If they're looking for a resource of this type with this name give them the whole file - if (type == mFileType && name.equals(mFileName)) { - return mFileValue; - } else { - // Otherwise try to return them an ID - // the map will return null if it's not found - return mIdResources.get(name); - } - } - - /** - * Looks through the file represented for Ids and adds them to - * our id repository - * - * @return true if parsing succeeds and false if it fails - */ - private boolean parseFileForIds(ScanningContext context) { - IdResourceParser parser = new IdResourceParser(this, context, isFramework()); - try { - IAbstractFile file = getFile(); - return parser.parse(mFileType, file.getOsLocation(), file.getContents()); - } catch (IOException e) { - // Pass - } catch (StreamException e) { - // Pass - } - - return false; - } - - /** - * Add the resources represented by this file to the repository - */ - private void updateResourceItems(ScanningContext context) { - ResourceRepository repository = getRepository(); - - // remove this file from all existing ResourceItem. - repository.removeFile(mResourceTypeList, this); - - // First add this as a layout file - ResourceItem item = repository.getResourceItem(mFileType, mFileName); - item.add(this); - - // Now iterate through our IDs and add - for (String idName : mIdResources.keySet()) { - item = repository.getResourceItem(ResourceType.ID, idName); - // add this file to the list of files generating ID resources. - item.add(this); - } - - // Ask the repository for an ID refresh - context.requestFullAapt(); - } - - /** - * Returns the resource value associated with this whole file as a layout resource - * @param file the file handler that represents this file - * @param folder the folder this file is under - * @return a resource value associated with this layout - */ - private ResourceValue getFileValue(IAbstractFile file, ResourceFolder folder) { - // test if there's a density qualifier associated with the resource - DensityQualifier qualifier = folder.getConfiguration().getDensityQualifier(); - - ResourceValue value; - if (!ResourceQualifier.isValid(qualifier)) { - value = - new ResourceValueImpl( - new ResourceReference( - ResourceNamespace.fromBoolean(isFramework()), - mFileType, - mFileName), - file.getOsLocation()); - } else { - value = - new DensityBasedResourceValueImpl( - new ResourceReference( - ResourceNamespace.fromBoolean(isFramework()), - mFileType, - mFileName), - file.getOsLocation(), - qualifier.getValue()); - } - return value; - } - - - /** - * Returns the name of this resource. - */ - private String getFileName(ResourceType type) { - // get the name from the filename. - String name = getFile().getName(); - - int pos = name.indexOf('.'); - if (pos != -1) { - name = name.substring(0, pos); - } - - return name; - } - - @Override - public void addResourceValue(ResourceValue value) { - // Just overwrite collisions. We're only interested in the unique - // IDs declared - mIdResources.put(value.getName(), value); - } -} diff --git a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/IdResourceParser.java b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/IdResourceParser.java deleted file mode 100644 index 53a971f625..0000000000 --- a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/IdResourceParser.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated; - -import app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated.ValueResourceParser.IValueResourceRepository; -import com.android.SdkConstants; -import com.android.annotations.NonNull; -import com.android.ide.common.rendering.api.ResourceNamespace; -import com.android.ide.common.rendering.api.ResourceReference; -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.rendering.api.ResourceValueImpl; -import com.android.resources.ResourceType; -import com.google.common.io.Closeables; -import java.io.BufferedInputStream; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import org.kxml2.io.KXmlParser; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -/** - * @deprecated This class is part of an obsolete resource repository system that is no longer used - * in production code. The class is preserved temporarily for LayoutLib tests. - */ -@Deprecated -public class IdResourceParser { - private final IValueResourceRepository mRepository; - private final boolean mIsFramework; - private ScanningContext mContext; - - /** - * Creates a new {@link IdResourceParser} - * - * @param repository value repository for registering resource declaration - * @param context a context object with state for the current update, such - * as a place to stash errors encountered - * @param isFramework true if scanning a framework resource - */ - public IdResourceParser( - @NonNull IValueResourceRepository repository, - @NonNull ScanningContext context, - boolean isFramework) { - mRepository = repository; - mContext = context; - mIsFramework = isFramework; - } - - /** - * Parse the given input and register ids with the given - * {@link IValueResourceRepository}. - * - * @param type the type of resource being scanned - * @param path the full OS path to the file being parsed - * @param input the input stream of the XML to be parsed (will be closed by this method) - * @return true if parsing succeeds and false if it fails - * @throws IOException if reading the contents fails - */ - public boolean parse(ResourceType type, final String path, InputStream input) - throws IOException { - KXmlParser parser = new KXmlParser(); - try { - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - - if (input instanceof FileInputStream) { - input = new BufferedInputStream(input); - } - parser.setInput(input, SdkConstants.UTF_8); - - return parse(type, path, parser); - } catch (XmlPullParserException e) { - String message = e.getMessage(); - - // Strip off position description - int index = message.indexOf("(position:"); //$NON-NLS-1$ (Hardcoded in KXml) - if (index != -1) { - message = message.substring(0, index); - } - - String error = String.format("%1$s:%2$d: Error: %3$s", //$NON-NLS-1$ - path, parser.getLineNumber(), message); - mContext.addError(error); - return false; - } catch (RuntimeException e) { - // Some exceptions are thrown by the KXmlParser that are not XmlPullParserExceptions, - // such as this one: - // java.lang.RuntimeException: Undefined Prefix: w in org.kxml2.io.KXmlParser@... - // at org.kxml2.io.KXmlParser.adjustNsp(Unknown Source) - // at org.kxml2.io.KXmlParser.parseStartTag(Unknown Source) - String message = e.getMessage(); - String error = String.format("%1$s:%2$d: Error: %3$s", //$NON-NLS-1$ - path, parser.getLineNumber(), message); - mContext.addError(error); - return false; - } finally { - try { - Closeables.close(input, true /* swallowIOException */); - } catch (IOException e) { - // cannot happen - } - } - } - - private boolean parse(ResourceType type, String path, KXmlParser parser) - throws XmlPullParserException, IOException { - boolean valid = true; - boolean checkForErrors = !mIsFramework && !mContext.needsFullAapt(); - - while (true) { - int event = parser.next(); - if (event == XmlPullParser.START_TAG) { - for (int i = 0, n = parser.getAttributeCount(); i < n; i++) { - String attribute = parser.getAttributeName(i); - String value = parser.getAttributeValue(i); - assert value != null : attribute; - - if (checkForErrors) { - String uri = parser.getAttributeNamespace(i); - if (!mContext.checkValue(uri, attribute, value)) { - mContext.requestFullAapt(); - checkForErrors = false; - valid = false; - } - } - - if (value.startsWith("@+")) { //$NON-NLS-1$ - // Strip out the @+id/ or @+android:id/ section - String id = value.substring(value.indexOf('/') + 1); - ResourceValue newId = - new ResourceValueImpl( - new ResourceReference( - ResourceNamespace.fromBoolean(mIsFramework), - ResourceType.ID, - id), - null); - mRepository.addResourceValue(newId); - } - } - } else if (event == XmlPullParser.END_DOCUMENT) { - break; - } - } - - return valid; - } -} diff --git a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/MultiResourceFile.java b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/MultiResourceFile.java deleted file mode 100644 index 27abe8b8d5..0000000000 --- a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/MultiResourceFile.java +++ /dev/null @@ -1,222 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated; - -import app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated.ValueResourceParser.IValueResourceRepository; -import app.cash.paparazzi.deprecated.com.android.io.IAbstractFile; -import app.cash.paparazzi.deprecated.com.android.io.StreamException; - -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.rendering.api.ResourceValueImpl; -import com.android.ide.common.resources.ResourceValueMap; -import com.android.resources.ResourceType; -import com.android.utils.XmlUtils; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumMap; -import java.util.Map; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; - -import org.xml.sax.InputSource; -import org.xml.sax.SAXException; - -/** - * @deprecated This class is part of an obsolete resource repository system that is no longer used - * in production code. The class is preserved temporarily for LayoutLib tests. - */ -@Deprecated -public final class MultiResourceFile extends ResourceFile implements IValueResourceRepository { - - private static final SAXParserFactory sParserFactory = XmlUtils.configureSaxFactory( - SAXParserFactory.newInstance(), false, false); - - private final Map mResourceItems = - new EnumMap(ResourceType.class); - - private Collection mResourceTypeList = null; - - public MultiResourceFile(IAbstractFile file, ResourceFolder folder) { - super(file, folder); - } - - // Boolean flag to track whether a named element has been added or removed, thus requiring - // a new ID table to be generated - private boolean mNeedIdRefresh; - - @Override - protected void load(ScanningContext context) { - // need to parse the file and find the content. - parseFile(); - - // create new ResourceItems for the new content. - mResourceTypeList = Collections.unmodifiableCollection(mResourceItems.keySet()); - - // We need an ID generation step - mNeedIdRefresh = true; - - // create/update the resource items. - updateResourceItems(context); - } - - @Override - protected void update(ScanningContext context) { - // Reset the ID generation flag - mNeedIdRefresh = false; - - // Copy the previous version of our list of ResourceItems and types - Map oldResourceItems - = new EnumMap(mResourceItems); - - // reset current content. - mResourceItems.clear(); - - // need to parse the file and find the content. - parseFile(); - - // create new ResourceItems for the new content. - mResourceTypeList = Collections.unmodifiableCollection(mResourceItems.keySet()); - - // Check to see if any names have changed. If so, mark the flag so updateResourceItems - // can notify the ResourceRepository that an ID refresh is needed - if (oldResourceItems.keySet().equals(mResourceItems.keySet())) { - for (ResourceType type : mResourceTypeList) { - // We just need to check the names of the items. - // If there are new or removed names then we'll have to regenerate IDs - if (mResourceItems.get(type).keySet() - .equals(oldResourceItems.get(type).keySet()) == false) { - mNeedIdRefresh = true; - } - } - } else { - // If our type list is different, obviously the names will be different - mNeedIdRefresh = true; - } - // create/update the resource items. - updateResourceItems(context); - } - - @Override - protected void dispose(ScanningContext context) { - ResourceRepository repository = getRepository(); - - // only remove this file from all existing ResourceItem. - repository.removeFile(mResourceTypeList, this); - - // We'll need an ID refresh because we deleted items - context.requestFullAapt(); - - // don't need to touch the content, it'll get reclaimed as this objects disappear. - // In the mean time other objects may need to access it. - } - - @Override - public Collection getResourceTypes() { - return mResourceTypeList; - } - - @Override - public boolean hasResources(ResourceType type) { - ResourceValueMap list = mResourceItems.get(type); - return (list != null && !list.isEmpty()); - } - - private void updateResourceItems(ScanningContext context) { - ResourceRepository repository = getRepository(); - - // remove this file from all existing ResourceItem. - repository.removeFile(mResourceTypeList, this); - - for (ResourceType type : mResourceTypeList) { - ResourceValueMap list = mResourceItems.get(type); - - if (list != null) { - Collection values = list.values(); - for (ResourceValue res : values) { - ResourceItem item = repository.getResourceItem(type, res.getName()); - - // add this file to the list of files generating this resource item. - item.add(this); - } - } - } - - // If we need an ID refresh, ask the repository for that now - if (mNeedIdRefresh) { - context.requestFullAapt(); - } - } - - /** - * Parses the file and creates a list of {@link ResourceType}. - */ - private void parseFile() { - try { - SAXParser parser = XmlUtils.createSaxParser(sParserFactory); - InputSource source = new InputSource(getFile().getContents()); - source.setEncoding(StandardCharsets.UTF_8.name()); - parser.parse(source, new ValueResourceParser(this, isFramework(), null)); - } catch (ParserConfigurationException e) { - } catch (SAXException e) { - } catch (IOException e) { - } catch (StreamException e) { - } - } - - /** - * Adds a resource item to the list - * @param value The value of the resource. - */ - @Override - public void addResourceValue(ResourceValue value) { - ResourceType resType = value.getResourceType(); - - ResourceValueMap list = mResourceItems.get(resType); - - // if the list does not exist, create it. - if (list == null) { - list = ResourceValueMap.create(); - mResourceItems.put(resType, list); - } else { - // look for a possible value already existing. - ResourceValue oldValue = list.get(value.getName()); - - if (oldValue instanceof ResourceValueImpl) { - ((ResourceValueImpl) oldValue).replaceWith(value); - return; - } - } - - // empty list or no match found? add the given resource - list.put(value.getName(), value); - } - - @Override - public ResourceValue getValue(ResourceType type, String name) { - // get the list for the given type - ResourceValueMap list = mResourceItems.get(type); - - if (list != null) { - return list.get(name); - } - - return null; - } -} diff --git a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceDeltaKind.java b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceDeltaKind.java deleted file mode 100644 index 7cb2fddac0..0000000000 --- a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceDeltaKind.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated; - -/** - * @deprecated This class is part of an obsolete resource repository system that is no longer used - * in production code. The class is preserved temporarily for LayoutLib tests. - */ -@Deprecated -public enum ResourceDeltaKind { - CHANGED, ADDED, REMOVED -} diff --git a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceFile.java b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceFile.java deleted file mode 100644 index 63a48654ce..0000000000 --- a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceFile.java +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated; - -import app.cash.paparazzi.deprecated.com.android.io.IAbstractFile; -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.resources.configuration.Configurable; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.resources.ResourceType; -import java.util.Collection; - -/** - * @deprecated This class is part of an obsolete resource repository system that is no longer used - * in production code. The class is preserved temporarily for LayoutLib tests. - */ -@Deprecated -public abstract class ResourceFile implements Configurable { - - private final IAbstractFile mFile; - private final ResourceFolder mFolder; - - protected ResourceFile(IAbstractFile file, ResourceFolder folder) { - mFile = file; - mFolder = folder; - } - - protected abstract void load(ScanningContext context); - protected abstract void update(ScanningContext context); - protected abstract void dispose(ScanningContext context); - - @Override - public FolderConfiguration getConfiguration() { - return mFolder.getConfiguration(); - } - - /** - * Returns the IFile associated with the ResourceFile. - */ - public final IAbstractFile getFile() { - return mFile; - } - - /** - * Returns the parent folder as a {@link ResourceFolder}. - */ - public final ResourceFolder getFolder() { - return mFolder; - } - - public final ResourceRepository getRepository() { - return mFolder.getRepository(); - } - - /** - * Returns whether the resource is a framework resource. - */ - public final boolean isFramework() { - return mFolder.getRepository().isFrameworkRepository(); - } - - /** - * Returns the list of {@link ResourceType} generated by the file. This is never null. - */ - public abstract Collection getResourceTypes(); - - /** - * Returns whether the file generated a resource of a specific type. - * @param type The {@link ResourceType} - */ - public abstract boolean hasResources(ResourceType type); - - /** - * Returns the value of a resource generated by this file by {@link ResourceType} and name. - *

If no resource match, null is returned. - * @param type the type of the resource. - * @param name the name of the resource. - */ - public abstract ResourceValue getValue(ResourceType type, String name); - - @Override - public String toString() { - return mFile.toString(); - } -} - diff --git a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceFolder.java b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceFolder.java deleted file mode 100644 index f73f210bbd..0000000000 --- a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceFolder.java +++ /dev/null @@ -1,353 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated; - -import app.cash.paparazzi.deprecated.com.android.io.IAbstractFile; -import app.cash.paparazzi.deprecated.com.android.io.IAbstractFolder; -import com.android.SdkConstants; -import com.android.ide.common.resources.configuration.Configurable; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.resources.FolderTypeRelationship; -import com.android.resources.ResourceFolderType; -import com.android.resources.ResourceType; -import com.android.utils.SdkUtils; -import com.google.common.annotations.VisibleForTesting; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * @deprecated This class is part of an obsolete resource repository system that is no longer used - * in production code. The class is preserved temporarily for LayoutLib tests. - */ -@Deprecated -public final class ResourceFolder implements Configurable { - final ResourceFolderType mType; - final FolderConfiguration mConfiguration; - IAbstractFolder mFolder; - List mFiles; - Map mNames; - private final ResourceRepository mRepository; - - /** - * Creates a new {@link ResourceFolder} - * @param type The type of the folder - * @param config The configuration of the folder - * @param folder The associated {@link IAbstractFolder} object. - * @param repository The associated {@link ResourceRepository} - */ - protected ResourceFolder(ResourceFolderType type, FolderConfiguration config, - app.cash.paparazzi.deprecated.com.android.io.IAbstractFolder folder, ResourceRepository repository) { - mType = type; - mConfiguration = config; - mFolder = folder; - mRepository = repository; - } - - /** - * Processes a file and adds it to its parent folder resource. - * - * @param file the underlying resource file. - * @param kind the file change kind. - * @param context a context object with state for the current update, such - * as a place to stash errors encountered - * @return the {@link ResourceFile} that was created. - */ - public ResourceFile processFile(IAbstractFile file, ResourceDeltaKind kind, - ScanningContext context) { - // look for this file if it's already been created - ResourceFile resFile = getFile(file, context); - - if (resFile == null) { - if (kind != ResourceDeltaKind.REMOVED) { - // create a ResourceFile for it. - - resFile = createResourceFile(file); - resFile.load(context); - - // add it to the folder - addFile(resFile); - } - } else { - if (kind == ResourceDeltaKind.REMOVED) { - removeFile(resFile, context); - } else { - resFile.update(context); - } - } - - return resFile; - } - - private ResourceFile createResourceFile(IAbstractFile file) { - // check if that's a single or multi resource type folder. We have a special case - // for ID generating resource types (layout/menu, and XML drawables, etc.). - // MultiResourceFile handles the case when several resource types come from a single file - // (values files). - - ResourceFile resFile; - if (mType != ResourceFolderType.VALUES) { - if (FolderTypeRelationship.isIdGeneratingFolderType(mType) && - SdkUtils.endsWithIgnoreCase(file.getName(), SdkConstants.DOT_XML)) { - List types = FolderTypeRelationship.getRelatedResourceTypes(mType); - ResourceType primaryType = types.get(0); - resFile = new IdGeneratingResourceFile(file, this, primaryType); - } else { - resFile = new SingleResourceFile(file, this); - } - } else { - resFile = new MultiResourceFile(file, this); - } - return resFile; - } - - /** - * Adds a {@link ResourceFile} to the folder. - * - * @param file The {@link ResourceFile}. - */ - @VisibleForTesting - public void addFile(ResourceFile file) { - if (mFiles == null) { - int initialSize = 16; - if (mRepository.isFrameworkRepository()) { - String name = mFolder.getName(); - // Pick some reasonable initial sizes for framework data structures - // since they are typically (a) large and (b) their sizes are roughly known - // in advance - switch (mType) { - case DRAWABLE: { - // See if it's one of the -mdpi, -hdpi etc folders which - // are large (~1250 items) - int index = name.indexOf('-'); - if (index == -1) { - initialSize = 230; // "drawable" folder - } else { - index = name.indexOf('-', index + 1); - if (index == -1) { - // One of the "drawable-" folders - initialSize = 1260; - } else { - // "drawable-sw600dp-hdpi" etc - initialSize = 30; - } - } - break; - } - case LAYOUT: { - // The main layout folder has about ~185 layouts in it; - // the others are small - if (name.indexOf('-') == -1) { - initialSize = 200; - } - break; - } - case VALUES: { - if (name.indexOf('-') == -1) { - initialSize = 32; - } else { - initialSize = 4; - } - break; - } - case ANIM: initialSize = 85; break; - case COLOR: initialSize = 32; break; - case RAW: initialSize = 4; break; - default: - // Stick with the 16 default - break; - } - } - - mFiles = new ArrayList(initialSize); - mNames = new HashMap(initialSize, 2.0f); - } - - mFiles.add(file); - mNames.put(file.getFile().getName(), file); - } - - protected void removeFile(ResourceFile file, ScanningContext context) { - file.dispose(context); - mFiles.remove(file); - mNames.remove(file.getFile().getName()); - } - - protected void dispose(ScanningContext context) { - if (mFiles != null) { - for (ResourceFile file : mFiles) { - file.dispose(context); - } - - mFiles.clear(); - mNames.clear(); - } - } - - /** - * Returns the {@link IAbstractFolder} associated with this object. - */ - public IAbstractFolder getFolder() { - return mFolder; - } - - /** - * Returns the {@link ResourceFolderType} of this object. - */ - public ResourceFolderType getType() { - return mType; - } - - public ResourceRepository getRepository() { - return mRepository; - } - - /** - * Returns the list of {@link ResourceType}s generated by the files inside this folder. - */ - public Collection getResourceTypes() { - ArrayList list = new ArrayList(); - - if (mFiles != null) { - for (ResourceFile file : mFiles) { - Collection types = file.getResourceTypes(); - - // loop through those and add them to the main list, - // if they are not already present - for (ResourceType resType : types) { - if (list.indexOf(resType) == -1) { - list.add(resType); - } - } - } - } - - return list; - } - - @Override - public FolderConfiguration getConfiguration() { - return mConfiguration; - } - - /** - * Returns whether the folder contains a file with the given name. - * @param name the name of the file. - */ - public boolean hasFile(String name) { - if (mNames != null && mNames.containsKey(name)) { - return true; - } - - // Note: mNames.containsKey(name) is faster, but doesn't give the same result; this - // method seems to be called on this ResourceFolder before it has been processed, - // so we need to use the file system check instead: - return mFolder.hasFile(name); - } - - /** - * Returns the {@link ResourceFile} matching a {@link IAbstractFile} object. - * - * @param file The {@link IAbstractFile} object. - * @param context a context object with state for the current update, such - * as a place to stash errors encountered - * @return the {@link ResourceFile} or null if no match was found. - */ - private ResourceFile getFile(IAbstractFile file, ScanningContext context) { - assert mFolder.equals(file.getParentFolder()); - - if (mNames != null) { - ResourceFile resFile = mNames.get(file.getName()); - if (resFile != null) { - return resFile; - } - } - - // If the file actually exists, the resource folder may not have been - // scanned yet; add it lazily - if (file.exists()) { - ResourceFile resFile = createResourceFile(file); - resFile.load(context); - addFile(resFile); - return resFile; - } - - return null; - } - - /** - * Returns the {@link ResourceFile} matching a given name. - * @param filename The name of the file to return. - * @return the {@link ResourceFile} or null if no match was found. - */ - public ResourceFile getFile(String filename) { - if (mNames != null) { - ResourceFile resFile = mNames.get(filename); - if (resFile != null) { - return resFile; - } - } - - // If the file actually exists, the resource folder may not have been - // scanned yet; add it lazily - IAbstractFile file = mFolder.getFile(filename); - if (file != null && file.exists()) { - ResourceFile resFile = createResourceFile(file); - resFile.load(new ScanningContext()); - addFile(resFile); - return resFile; - } - - return null; - } - - /** - * Returns whether a file in the folder is generating a resource of a specified type. - * @param type The {@link ResourceType} being looked up. - */ - public boolean hasResources(ResourceType type) { - // Check if the folder type is able to generate resource of the type that was asked. - // this is a first check to avoid going through the files. - List folderTypes = FolderTypeRelationship.getRelatedFolders(type); - - boolean valid = false; - for (ResourceFolderType rft : folderTypes) { - if (rft == mType) { - valid = true; - break; - } - } - - if (valid) { - if (mFiles != null) { - for (ResourceFile f : mFiles) { - if (f.hasResources(type)) { - return true; - } - } - } - } - return false; - } - - @Override - public String toString() { - return mFolder.toString(); - } -} diff --git a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceItem.java b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceItem.java deleted file mode 100644 index 549e3cb323..0000000000 --- a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceItem.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated; - -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.resources.ResourceType; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Comparator; -import java.util.List; - -/** - * @deprecated This class is part of an obsolete resource repository system that is no longer used - * in production code. The class is preserved temporarily for LayoutLib tests. - */ -@Deprecated -public class ResourceItem implements Comparable { - - private static final Comparator sComparator = new Comparator() { - @Override - public int compare(ResourceFile file1, ResourceFile file2) { - // get both FolderConfiguration and compare them - FolderConfiguration fc1 = file1.getFolder().getConfiguration(); - FolderConfiguration fc2 = file2.getFolder().getConfiguration(); - - return fc1.compareTo(fc2); - } - }; - - private final String mName; - - /** - * List of files generating this ResourceItem. - */ - private final List mFiles = new ArrayList(); - - /** - * Constructs a new ResourceItem. - * @param name the name of the resource as it appears in the XML and R.java files. - */ - public ResourceItem(String name) { - mName = name; - } - - /** - * Returns the name of the resource. - */ - public final String getName() { - return mName; - } - - /** - * Compares the {@link ResourceItem} to another. - * @param other the ResourceItem to be compared to. - */ - @Override - public int compareTo(ResourceItem other) { - return mName.compareTo(other.mName); - } - - /** - * Returns whether the resource is editable directly. - *

- * This is typically the case for resources that don't have alternate versions, or resources - * of type {@link ResourceType#ID} that aren't declared inline. - */ - public boolean isEditableDirectly() { - return hasAlternates() == false; - } - - /** - * Returns whether the ID resource has been declared inline inside another resource XML file. - * If the resource type is not {@link ResourceType#ID}, this will always return {@code false}. - */ - public boolean isDeclaredInline() { - return false; - } - - /** - * Returns a {@link ResourceValue} for this item based on the given configuration. - * If the ResourceItem has several source files, one will be selected based on the config. - * @param type the type of the resource. This is necessary because ResourceItem doesn't embed - * its type, but ResourceValue does. - * @param referenceConfig the config of the resource item. - * @param isFramework whether the resource is a framework value. Same as the type. - * @return a ResourceValue or null if none match the config. - */ - public ResourceValue getResourceValue(ResourceType type, FolderConfiguration referenceConfig, - boolean isFramework) { - // look for the best match for the given configuration - // the match has to be of type ResourceFile since that's what the input list contains - ResourceFile match = (ResourceFile) referenceConfig.findMatchingConfigurable(mFiles); - - if (match != null) { - // get the value of this configured resource. - return match.getValue(type, mName); - } - - return null; - } - - /** - * Adds a new source file. - * @param file the source file. - */ - protected void add(ResourceFile file) { - mFiles.add(file); - } - - /** - * Removes a file from the list of source files. - * @param file the file to remove - */ - protected void removeFile(ResourceFile file) { - mFiles.remove(file); - } - - /** - * Returns {@code true} if the item has no source file. - * @return true if the item has no source file. - */ - protected boolean hasNoSourceFile() { - return mFiles.isEmpty(); - } - - /** - * Reset the item by emptying its source file list. - */ - protected void reset() { - mFiles.clear(); - } - - /** - * Returns the sorted list of {@link ResourceItem} objects for this resource item. - */ - public ResourceFile[] getSourceFileArray() { - ArrayList list = new ArrayList(); - list.addAll(mFiles); - - Collections.sort(list, sComparator); - - return list.toArray(new ResourceFile[0]); - } - - /** - * Returns the list of source file for this resource. - */ - public List getSourceFileList() { - return Collections.unmodifiableList(mFiles); - } - - /** - * Returns if the resource has at least one non-default version. - * - * @see ResourceFile#getConfiguration() - * @see FolderConfiguration#isDefault() - */ - public boolean hasAlternates() { - for (ResourceFile file : mFiles) { - if (file.getFolder().getConfiguration().isDefault() == false) { - return true; - } - } - - return false; - } - - /** - * Returns whether the resource has a default version, with no qualifier. - * - * @see ResourceFile#getConfiguration() - * @see FolderConfiguration#isDefault() - */ - public boolean hasDefault() { - for (ResourceFile file : mFiles) { - if (file.getFolder().getConfiguration().isDefault()) { - return true; - } - } - - // We only want to return false if there's no default and more than 0 items. - return (mFiles.isEmpty()); - } - - /** - * Returns the number of alternate versions for this resource. - * - * @see ResourceFile#getConfiguration() - * @see FolderConfiguration#isDefault() - */ - public int getAlternateCount() { - int count = 0; - for (ResourceFile file : mFiles) { - if (file.getFolder().getConfiguration().isDefault() == false) { - count++; - } - } - - return count; - } - - /** - * Returns a formatted string usable in an XML to use for the {@link ResourceItem}. - * @param system Whether this is a system resource or a project resource. - * @return a string in the format @[type]/[name] - */ - public String getXmlString(ResourceType type, boolean system) { - if (type == ResourceType.ID && isDeclaredInline()) { - return (system ? "@android:" : "@+") + type.getName() + "/" + mName; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - } - - return (system ? "@android:" : "@") + type.getName() + "/" + mName; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - } - - @Override - public String toString() { - return "ResourceItem [mName=" + mName + ", mFiles=" + mFiles + "]"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ - } -} diff --git a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceRepository.java b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceRepository.java deleted file mode 100644 index f61f21298d..0000000000 --- a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ResourceRepository.java +++ /dev/null @@ -1,915 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated; - -import app.cash.paparazzi.deprecated.com.android.io.IAbstractFile; -import app.cash.paparazzi.deprecated.com.android.io.IAbstractFolder; -import app.cash.paparazzi.deprecated.com.android.io.IAbstractResource; -import com.android.SdkConstants; -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.resources.ResourceValueMap; -import com.android.ide.common.resources.configuration.FolderConfiguration; -import com.android.ide.common.resources.configuration.LocaleQualifier; -import com.android.resources.FolderTypeRelationship; -import com.android.resources.ResourceFolderType; -import com.android.resources.ResourceType; -import com.android.resources.ResourceUrl; -import java.io.File; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.EnumMap; -import java.util.HashMap; -import java.util.IdentityHashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.SortedSet; -import java.util.TreeSet; - -import static com.android.SdkConstants.ATTR_REF_PREFIX; -import static com.android.SdkConstants.PREFIX_RESOURCE_REF; -import static com.android.SdkConstants.PREFIX_THEME_REF; -import static com.android.SdkConstants.RESOURCE_CLZ_ATTR; - -/** - * @deprecated This class is part of an obsolete resource repository system that is no longer used - * in production code. The class is preserved temporarily for LayoutLib tests. - */ -@Deprecated -public abstract class ResourceRepository { - private final IAbstractFolder mResourceFolder; - - protected Map> mFolderMap = - new EnumMap<>(ResourceFolderType.class); - - protected Map> mResourceMap = - new EnumMap<>(ResourceType.class); - - private Map, Collection> mReadOnlyListMap = - new IdentityHashMap<>(); - - private final boolean mFrameworkRepository; - private boolean mCleared = true; - private boolean mInitializing; - - /** - * Makes a resource repository. - * - * @param resFolder the resource folder of the repository. - * @param isFrameworkRepository whether the repository is for framework resources. - */ - protected ResourceRepository(@NonNull IAbstractFolder resFolder, - boolean isFrameworkRepository) { - mResourceFolder = resFolder; - mFrameworkRepository = isFrameworkRepository; - } - - public IAbstractFolder getResFolder() { - return mResourceFolder; - } - - public boolean isFrameworkRepository() { - return mFrameworkRepository; - } - - public synchronized void clear() { - mCleared = true; - mFolderMap = new EnumMap>( - ResourceFolderType.class); - mResourceMap = new EnumMap>( - ResourceType.class); - - mReadOnlyListMap = - new IdentityHashMap, Collection>(); - } - - /** - * Ensures that the repository has been initialized again after a call to - * {@link ResourceRepository#clear()}. - * - * @return true if the repository was just re-initialized. - */ - public synchronized boolean ensureInitialized() { - if (mCleared && !mInitializing) { - ScanningContext context = new ScanningContext(); - mInitializing = true; - - IAbstractResource[] resources = mResourceFolder.listMembers(); - - for (IAbstractResource res : resources) { - if (res instanceof IAbstractFolder) { - IAbstractFolder folder = (IAbstractFolder)res; - ResourceFolder resFolder = processFolder(folder); - - if (resFolder != null) { - // now we process the content of the folder - IAbstractResource[] files = folder.listMembers(); - - for (IAbstractResource fileRes : files) { - if (fileRes instanceof IAbstractFile) { - IAbstractFile file = (IAbstractFile)fileRes; - - resFolder.processFile(file, ResourceDeltaKind.ADDED, context); - } - } - } - } - } - - mInitializing = false; - mCleared = false; - return true; - } - - return false; - } - - /** - * Adds a Folder Configuration to the project. - * - * @param type The resource type. - * @param config The resource configuration. - * @param folder The workspace folder object. - * @return the {@link ResourceFolder} object associated to this folder. - */ - private ResourceFolder add( - @NonNull ResourceFolderType type, - @NonNull FolderConfiguration config, - @NonNull IAbstractFolder folder) { - // get the list for the resource type - List list = mFolderMap.get(type); - - if (list == null) { - list = new ArrayList(); - - ResourceFolder cf = new ResourceFolder(type, config, folder, this); - list.add(cf); - - mFolderMap.put(type, list); - - return cf; - } - - // look for an already existing folder configuration. - for (ResourceFolder cFolder : list) { - if (cFolder.mConfiguration.equals(config)) { - // config already exist. Nothing to be done really, besides making sure - // the IAbstractFolder object is up to date. - cFolder.mFolder = folder; - return cFolder; - } - } - - // If we arrive here, this means we didn't find a matching configuration. - // So we add one. - ResourceFolder cf = new ResourceFolder(type, config, folder, this); - list.add(cf); - - return cf; - } - - /** - * Removes a {@link ResourceFolder} associated with the specified {@link IAbstractFolder}. - * - * @param type The type of the folder - * @param removedFolder the IAbstractFolder object. - * @param context the scanning context - * @return the {@link ResourceFolder} that was removed, or null if no matches were found. - */ - @Nullable - public ResourceFolder removeFolder( - @NonNull ResourceFolderType type, - @NonNull IAbstractFolder removedFolder, - @Nullable ScanningContext context) { - ensureInitialized(); - - // get the list of folders for the resource type. - List list = mFolderMap.get(type); - - if (list != null) { - int count = list.size(); - for (int i = 0 ; i < count ; i++) { - ResourceFolder resFolder = list.get(i); - IAbstractFolder folder = resFolder.getFolder(); - if (removedFolder.equals(folder)) { - // we found the matching ResourceFolder. we need to remove it. - list.remove(i); - - // remove its content - resFolder.dispose(context); - - return resFolder; - } - } - } - - return null; - } - - /** - * Returns true if this resource repository contains a resource of the given name. - * - * @param url the resource URL - * @return true if the resource is known - */ - public boolean hasResourceItem(@NonNull String url) { - // Handle theme references - if (url.startsWith(PREFIX_THEME_REF)) { - String remainder = url.substring(PREFIX_THEME_REF.length()); - if (url.startsWith(ATTR_REF_PREFIX)) { - url = PREFIX_RESOURCE_REF + url.substring(PREFIX_THEME_REF.length()); - return hasResourceItem(url); - } - int colon = url.indexOf(':'); - if (colon != -1) { - // Convert from ?android:progressBarStyleBig to ?android:attr/progressBarStyleBig - if (remainder.indexOf('/', colon) == -1) { - remainder = remainder.substring(0, colon) + RESOURCE_CLZ_ATTR + '/' - + remainder.substring(colon); - } - url = PREFIX_RESOURCE_REF + remainder; - return hasResourceItem(url); - } else { - int slash = url.indexOf('/'); - if (slash == -1) { - url = PREFIX_RESOURCE_REF + RESOURCE_CLZ_ATTR + '/' + remainder; - return hasResourceItem(url); - } - } - } - - if (!url.startsWith(PREFIX_RESOURCE_REF)) { - return false; - } - - assert url.startsWith("@") || url.startsWith("?") : url; - - ensureInitialized(); - - int typeEnd = url.indexOf('/', 1); - if (typeEnd != -1) { - int nameBegin = typeEnd + 1; - - // Skip @ and @+ - int typeBegin = url.startsWith("@+") ? 2 : 1; //$NON-NLS-1$ - - int colon = url.lastIndexOf(':', typeEnd); - if (colon != -1) { - typeBegin = colon + 1; - } - String typeName = url.substring(typeBegin, typeEnd); - ResourceType type = ResourceType.fromXmlValue(typeName); - if (type != null) { - String name = url.substring(nameBegin); - return hasResourceItem(type, name); - } - } - - return false; - } - - /** - * Returns true if this resource repository contains a resource of the given name. - * - * @param type the type of resource to look up - * @param name the name of the resource - * @return true if the resource is known - */ - public boolean hasResourceItem(@NonNull ResourceType type, @NonNull String name) { - ensureInitialized(); - - Map map = mResourceMap.get(type); - - if (map != null) { - - ResourceItem resourceItem = map.get(name); - if (resourceItem != null) { - return true; - } - } - - return false; - } - - /** - * Returns a {@link ResourceItem} matching the given {@link ResourceType} and name. If none - * exist, it creates one. - * - * @param type the resource type - * @param name the name of the resource. - * @return A resource item matching the type and name. - */ - @NonNull - public ResourceItem getResourceItem(@NonNull ResourceType type, @NonNull String name) { - ensureInitialized(); - - // looking for an existing ResourceItem with this type and name - ResourceItem item = findDeclaredResourceItem(type, name); - - // create one if there isn't one already, or if the existing one is inlined, since - // clearly we need a non inlined one (the inline one is removed too) - if (item == null || item.isDeclaredInline()) { - ResourceItem oldItem = item; - item = createResourceItem(name); - - Map map = mResourceMap.get(type); - - if (map == null) { - if (isFrameworkRepository()) { - // Pick initial size for the maps. Also change the load factor to 1.0 - // to avoid rehashing the whole table when we (as expected) get near - // the known rough size of each resource type map. - int size; - switch (type) { - // Based on counts in API 16. Going back to API 10, the counts - // are roughly 25-50% smaller (e.g. compared to the top 5 types below - // the fractions are 1107 vs 1734, 831 vs 1508, 895 vs 1255, - // 733 vs 1064 and 171 vs 783. - case PUBLIC: size = 1734; break; - case DRAWABLE: size = 1508; break; - case STRING: size = 1255; break; - case ATTR: size = 1064; break; - case STYLE: size = 783; break; - case ID: size = 347; break; - case STYLEABLE: - size = 210; - break; - case LAYOUT: size = 187; break; - case COLOR: size = 120; break; - case ANIM: size = 95; break; - case DIMEN: size = 81; break; - case BOOL: size = 54; break; - case INTEGER: size = 52; break; - case ARRAY: size = 51; break; - case PLURALS: size = 20; break; - case XML: size = 14; break; - case INTERPOLATOR : size = 13; break; - case ANIMATOR: size = 8; break; - case RAW: size = 4; break; - case MENU: size = 2; break; - case MIPMAP: size = 2; break; - case FRACTION: size = 1; break; - default: - size = 2; - } - map = new HashMap<>(size, 1.0f); - } else { - map = new HashMap<>(); - } - mResourceMap.put(type, map); - } - - map.put(item.getName(), item); - - if (oldItem != null) { - map.remove(oldItem.getName()); - } - } - - return item; - } - - /** - * Creates a resource item with the given name. - * @param name the name of the resource - * @return a new ResourceItem (or child class) instance. - */ - @NonNull - protected abstract ResourceItem createResourceItem(@NonNull String name); - - /** - * Processes a folder and adds it to the list of existing folders. - * @param folder the folder to process - * @return the ResourceFolder created from this folder, or null if the process failed. - */ - @Nullable - public ResourceFolder processFolder(@NonNull IAbstractFolder folder) { - ensureInitialized(); - - // split the name of the folder in segments. - String[] folderSegments = folder.getName().split(SdkConstants.RES_QUALIFIER_SEP); - - // get the enum for the resource type. - ResourceFolderType type = ResourceFolderType.getTypeByName(folderSegments[0]); - - if (type != null) { - // get the folder configuration. - FolderConfiguration config = FolderConfiguration.getConfig(folderSegments); - - if (config != null) { - return add(type, config, folder); - } - } - - return null; - } - - /** - * Returns a list of {@link ResourceFolder} for a specific {@link ResourceFolderType}. - * - * @param type The {@link ResourceFolderType} - */ - @Nullable - public List getFolders(@NonNull ResourceFolderType type) { - ensureInitialized(); - - return mFolderMap.get(type); - } - - @NonNull - public List getAvailableResourceTypes() { - ensureInitialized(); - - List list = new ArrayList(); - - // For each key, we check if there's a single ResourceType match. - // If not, we look for the actual content to give us the resource type. - - for (ResourceFolderType folderType : mFolderMap.keySet()) { - List types = FolderTypeRelationship.getRelatedResourceTypes(folderType); - if (types.size() == 1) { - // before we add it we check if it's not already present, since a ResourceType - // could be created from multiple folders, even for the folders that only create - // one type of resource (drawable for instance, can be created from drawable/ and - // values/) - if (!list.contains(types.get(0))) { - list.add(types.get(0)); - } - } else { - // there isn't a single resource type out of this folder, so we look for all - // content. - List folders = mFolderMap.get(folderType); - if (folders != null) { - for (ResourceFolder folder : folders) { - Collection folderContent = folder.getResourceTypes(); - - // then we add them, but only if they aren't already in the list. - for (ResourceType folderResType : folderContent) { - if (!list.contains(folderResType)) { - list.add(folderResType); - } - } - } - } - } - } - - return list; - } - - /** - * Returns a list of {@link ResourceItem} matching a given {@link ResourceType}. - * @param type the type of the resource items to return - * @return a non null collection of resource items - */ - @NonNull - public Collection getResourceItemsOfType(@NonNull ResourceType type) { - ensureInitialized(); - - Map map = mResourceMap.get(type); - - if (map == null) { - return Collections.emptyList(); - } - - Collection roList = mReadOnlyListMap.get(map); - if (roList == null) { - roList = Collections.unmodifiableCollection(map.values()); - mReadOnlyListMap.put(map, roList); - } - - return roList; - } - - /** - * Returns whether the repository has resources of a given {@link ResourceType}. - * @param type the type of resource to check. - * @return true if the repository contains resources of the given type, false otherwise. - */ - public boolean hasResourcesOfType(@NonNull ResourceType type) { - ensureInitialized(); - - Map items = mResourceMap.get(type); - return (items != null && !items.isEmpty()); - } - - /** - * Returns the {@link ResourceFolder} associated with a {@link IAbstractFolder}. - * @param folder The {@link IAbstractFolder} object. - * @return the {@link ResourceFolder} or null if it was not found. - */ - @Nullable - public ResourceFolder getResourceFolder(@NonNull IAbstractFolder folder) { - ensureInitialized(); - - Collection> values = mFolderMap.values(); - - for (List list : values) { - for (ResourceFolder resFolder : list) { - IAbstractFolder wrapper = resFolder.getFolder(); - if (wrapper.equals(folder)) { - return resFolder; - } - } - } - - return null; - } - - /** - * Returns the {@link ResourceFile} matching the given name, - * {@link ResourceFolderType} and configuration. - *

- * This only works with files generating one resource named after the file - * (for instance, layouts, bitmap based drawable, xml, anims). - * - * @param name the resource name or file name - * @param type the folder type search for - * @param config the folder configuration to match for - * @return the matching file or null if no match was found. - */ - @Nullable - public ResourceFile getMatchingFile( - @NonNull String name, - @NonNull ResourceFolderType type, - @NonNull FolderConfiguration config) { - List types = FolderTypeRelationship.getRelatedResourceTypes(type); - for (ResourceType t : types) { - if (t == ResourceType.ID) { - continue; - } - ResourceFile match = getMatchingFile(name, t, config); - if (match != null) { - return match; - } - } - - return null; - } - - /** - * Returns the {@link ResourceFile} matching the given name, - * {@link ResourceType} and configuration. - *

- * This only works with files generating one resource named after the file - * (for instance, layouts, bitmap based drawable, xml, anims). - * - * @param name the resource name or file name - * @param type the folder type search for - * @param config the folder configuration to match for - * @return the matching file or null if no match was found. - */ - @Nullable - public ResourceFile getMatchingFile( - @NonNull String name, - @NonNull ResourceType type, - @NonNull FolderConfiguration config) { - ensureInitialized(); - - String resourceName = name; - int dot = resourceName.indexOf('.'); - if (dot != -1) { - resourceName = resourceName.substring(0, dot); - } - - Map items = mResourceMap.get(type); - if (items != null) { - ResourceItem item = items.get(resourceName); - if (item != null) { - List files = item.getSourceFileList(); - if (files != null) { - if (files.size() > 1) { - ResourceValue value = item.getResourceValue(type, config, - isFrameworkRepository()); - if (value != null) { - String v = value.getValue(); - if (v != null) { - ResourceUrl url = ResourceUrl.parse(v); - if (url != null) { - return getMatchingFile(url.name, url.type, config); - } else { - // Looks like the resource value is pointing to a file - // It's most likely one of the source files for this - // resource item, so check those first - for (ResourceFile f : files) { - if (v.equals(f.getFile().getOsLocation())) { - // Found the file - return f; - } - } - - // No; look up the resource file from the full path - File file = new File(v); - if (file.exists()) { - ResourceFile f = findResourceFile(file); - if (f != null) { - return f; - } - } - } - } - } - } else if (files.size() == 1) { - // Single file: see if it matches - ResourceFile matchingFile = files.get(0); - if (matchingFile.getFolder().getConfiguration().isMatchFor(config)) { - return matchingFile; - } - } - } - } - } - - return null; - } - - /** - * Looks up the {@link ResourceFile} for the given {@link File}, if possible - * - * @param file the file - * @return the corresponding {@link ResourceFile}, or null if not a known {@link ResourceFile} - */ - @Nullable - protected ResourceFile findResourceFile(@NonNull File file) { - // Look up the right resource file for this path - String parentName = file.getParentFile().getName(); - IAbstractFolder folder = getResFolder().getFolder(parentName); - if (folder != null) { - ResourceFolder resourceFolder = getResourceFolder(folder); - if (resourceFolder == null) { - FolderConfiguration configForFolder = FolderConfiguration - .getConfigForFolder(parentName); - if (configForFolder != null) { - ResourceFolderType folderType = ResourceFolderType.getFolderType(parentName); - if (folderType != null) { - resourceFolder = add(folderType, configForFolder, folder); - } - } - } - if (resourceFolder != null) { - ResourceFile resourceFile = resourceFolder.getFile(file.getName()); - if (resourceFile != null) { - return resourceFile; - } - } - } - - return null; - } - - /** - * Returns the list of source files for a given resource. - * Optionally, if a {@link FolderConfiguration} is given, then only the best - * match for this config is returned. - * - * @param type the type of the resource. - * @param name the name of the resource. - * @param referenceConfig an optional config for which only the best match will be returned. - * - * @return a list of files generating this resource or null if it was not found. - */ - @Nullable - public List getSourceFiles(@NonNull ResourceType type, @NonNull String name, - @Nullable FolderConfiguration referenceConfig) { - ensureInitialized(); - - Collection items = getResourceItemsOfType(type); - - for (ResourceItem item : items) { - if (name.equals(item.getName())) { - if (referenceConfig != null) { - ResourceFile match = - referenceConfig.findMatchingConfigurable(item.getSourceFileList()); - if (match != null) { - return Collections.singletonList((ResourceFile) match); - } - - return null; - } - return item.getSourceFileList(); - } - } - - return null; - } - - /** - * Returns the resources values matching a given {@link FolderConfiguration}. - * - * @param referenceConfig the configuration that each value must match. - * @return a map with guaranteed to contain an entry for each {@link ResourceType} - */ - @NonNull - public Map getConfiguredResources( - @NonNull FolderConfiguration referenceConfig) { - ensureInitialized(); - - return doGetConfiguredResources(referenceConfig); - } - - /** - * Returns the resources values matching a given {@link FolderConfiguration} for the current - * project. - * - * @param referenceConfig the configuration that each value must match. - * @return a map with guaranteed to contain an entry for each {@link ResourceType} - */ - @NonNull - protected final Map doGetConfiguredResources( - @NonNull FolderConfiguration referenceConfig) { - ensureInitialized(); - - Map map = - new EnumMap(ResourceType.class); - - for (ResourceType key : ResourceType.values()) { - // get the local results and put them in the map - map.put(key, getConfiguredResource(key, referenceConfig)); - } - - return map; - } - - /** - * Returns the sorted list of languages used in the resources. - */ - @NonNull - public SortedSet getLanguages() { - ensureInitialized(); - - SortedSet set = new TreeSet(); - - Collection> folderList = mFolderMap.values(); - for (List folderSubList : folderList) { - for (ResourceFolder folder : folderSubList) { - FolderConfiguration config = folder.getConfiguration(); - LocaleQualifier locale = config.getLocaleQualifier(); - if (locale != null && locale.hasLanguage()) { - set.add(locale.getLanguage()); - } - } - } - - return set; - } - - /** - * Returns the sorted list of regions used in the resources with the given language. - * - * @param currentLanguage the current language the region must be associated with. - */ - @NonNull - public SortedSet getRegions(@NonNull String currentLanguage) { - ensureInitialized(); - - SortedSet set = new TreeSet(); - - Collection> folderList = mFolderMap.values(); - for (List folderSubList : folderList) { - for (ResourceFolder folder : folderSubList) { - FolderConfiguration config = folder.getConfiguration(); - - // get the language - LocaleQualifier locale = config.getLocaleQualifier(); - if (locale != null && currentLanguage.equals(locale.getLanguage()) - && locale.getRegion() != null) { - set.add(locale.getRegion()); - } - } - } - - return set; - } - - /** - * Loads the resources. - */ - public void loadResources() { - clear(); - ensureInitialized(); - } - - protected void removeFile(@NonNull Collection types, - @NonNull ResourceFile file) { - ensureInitialized(); - - for (ResourceType type : types) { - removeFile(type, file); - } - } - - protected void removeFile(@NonNull ResourceType type, @NonNull ResourceFile file) { - Map map = mResourceMap.get(type); - if (map != null) { - Collection values = map.values(); - List toDelete = null; - for (ResourceItem item : values) { - item.removeFile(file); - if (item.hasNoSourceFile()) { - if (toDelete == null) { - toDelete = new ArrayList(values.size()); - } - toDelete.add(item); - } - } - if (toDelete != null) { - for (ResourceItem item : toDelete) { - map.remove(item.getName()); - } - } - } - } - - /** - * Returns a map of (resource name, resource value) for the given {@link ResourceType}. - *

The values returned are taken from the resource files best matching a given - * {@link FolderConfiguration}. - * - * @param type the type of the resources. - * @param referenceConfig the configuration to best match. - */ - @NonNull - private ResourceValueMap getConfiguredResource(@NonNull ResourceType type, - @NonNull FolderConfiguration referenceConfig) { - // get the resource item for the given type - Map items = mResourceMap.get(type); - if (items == null) { - return ResourceValueMap.create(); - } - - // create the map - ResourceValueMap map = ResourceValueMap.createWithExpectedSize(items.size()); - - for (ResourceItem item : items.values()) { - ResourceValue value = item.getResourceValue(type, referenceConfig, - isFrameworkRepository()); - if (value != null) { - map.put(item.getName(), value); - } - } - - return map; - } - - /** - * Cleans up the repository of resource items that have no source file anymore. - */ - public void postUpdateCleanUp() { - // Since removed files/folders remove source files from existing ResourceItem, loop through - // all resource items and remove the ones that have no source files. - - Collection> maps = mResourceMap.values(); - for (Map map : maps) { - Set keySet = map.keySet(); - Iterator iterator = keySet.iterator(); - while (iterator.hasNext()) { - String name = iterator.next(); - ResourceItem resourceItem = map.get(name); - if (resourceItem.hasNoSourceFile()) { - iterator.remove(); - } - } - } - } - - /** - * Looks up an existing {@link ResourceItem} by {@link ResourceType} and name. - * Ignores inline resources. - * - * @param type the resource type. - * @param name the resource name. - * @return the existing ResourceItem or null if no match was found. - */ - @Nullable - private ResourceItem findDeclaredResourceItem(@NonNull ResourceType type, - @NonNull String name) { - Map map = mResourceMap.get(type); - - if (map != null) { - ResourceItem resourceItem = map.get(name); - if (resourceItem != null && !resourceItem.isDeclaredInline()) { - return resourceItem; - } - } - - return null; - } -} - diff --git a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ScanningContext.java b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ScanningContext.java deleted file mode 100644 index 153734ac2a..0000000000 --- a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ScanningContext.java +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; - -/** - * @deprecated This class is part of an obsolete resource repository system that is no longer used - * in production code. The class is preserved temporarily for LayoutLib tests. - */ -@Deprecated -public class ScanningContext { - private boolean mNeedsFullAapt; - private List mErrors; - - /** Constructs a new {@link ScanningContext} */ - public ScanningContext() { - super(); - } - - /** Returns a list of errors encountered during scanning, or null if there were no errors. */ - @Nullable - public List getErrors() { - return mErrors; - } - - /** - * Adds the given error to the scanning context. The error should use the - * same syntax as real aapt error messages such that the aapt parser can - * properly detect the filename, line number, etc. - * - * @param error the error message, including file name and line number at - * the beginning - */ - public void addError(@NonNull String error) { - if (mErrors == null) { - mErrors = new ArrayList<>(); - } - mErrors.add(error); - } - - /** - * Marks that a full aapt compilation of the resources is necessary because it has - * detected a change that cannot be incrementally handled. - */ - protected void requestFullAapt() { - mNeedsFullAapt = true; - } - - /** - * Returns whether this repository has been marked as "dirty"; if one or - * more of the constituent files have declared that the resource item names - * that they provide have changed. - * - * @return true if a full aapt compilation is required - */ - public boolean needsFullAapt() { - return mNeedsFullAapt; - } - - /** - * Asks the context to check whether the given attribute name and value is valid - * in this context. - * - * @param uri the XML namespace URI - * @param name the attribute local name - * @param value the attribute value - * @return true if the attribute is valid - */ - public boolean checkValue(@Nullable String uri, @NonNull String name, @NonNull String value) { - return true; - } -} diff --git a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/SingleResourceFile.java b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/SingleResourceFile.java deleted file mode 100644 index 8a7db55ea4..0000000000 --- a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/SingleResourceFile.java +++ /dev/null @@ -1,178 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated; - -import app.cash.paparazzi.deprecated.com.android.io.IAbstractFile; -import com.android.ide.common.rendering.api.DensityBasedResourceValueImpl; -import com.android.ide.common.rendering.api.ResourceNamespace; -import com.android.ide.common.rendering.api.ResourceReference; -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.rendering.api.ResourceValueImpl; -import com.android.ide.common.resources.configuration.DensityQualifier; -import com.android.ide.common.resources.configuration.ResourceQualifier; -import com.android.resources.FolderTypeRelationship; -import com.android.resources.ResourceType; -import com.android.utils.SdkUtils; -import java.util.Collection; -import java.util.List; -import javax.xml.parsers.SAXParserFactory; - -import static com.android.SdkConstants.DOT_XML; - -/** - * @deprecated This class is part of an obsolete resource repository system that is no longer used - * in production code. The class is preserved temporarily for LayoutLib tests. - */ -@Deprecated -public class SingleResourceFile extends ResourceFile { - - private static final SAXParserFactory sParserFactory = SAXParserFactory.newInstance(); - static { - sParserFactory.setNamespaceAware(true); - } - - private final String mResourceName; - private final ResourceType mType; - private ResourceValue mValue; - - public SingleResourceFile(IAbstractFile file, ResourceFolder folder) { - super(file, folder); - - // we need to infer the type of the resource from the folder type. - // This is easy since this is a single Resource file. - List types = FolderTypeRelationship.getRelatedResourceTypes(folder.getType()); - mType = types.get(0); - - // compute the resource name - mResourceName = getResourceName(mType); - - // test if there's a density qualifier associated with the resource - DensityQualifier qualifier = folder.getConfiguration().getDensityQualifier(); - - if (!ResourceQualifier.isValid(qualifier)) { - mValue = - new ResourceValueImpl( - new ResourceReference( - ResourceNamespace.fromBoolean(isFramework()), - mType, - getResourceName(mType)), - file.getOsLocation()); - } else { - mValue = - new DensityBasedResourceValueImpl( - new ResourceReference( - ResourceNamespace.fromBoolean(isFramework()), - mType, - getResourceName(mType)), - file.getOsLocation(), - qualifier.getValue()); - } - } - - @Override - protected void load(ScanningContext context) { - // get a resource item matching the given type and name - ResourceItem item = getRepository().getResourceItem(mType, mResourceName); - - // add this file to the list of files generating this resource item. - item.add(this); - - // Ask for an ID refresh since we're adding an item that will generate an ID - context.requestFullAapt(); - } - - @Override - protected void update(ScanningContext context) { - // when this happens, nothing needs to be done since the file only generates - // a single resources that doesn't actually change (its content is the file path) - - // However, we should check for newly introduced errors - // Parse the file and look for @+id/ entries - validateAttributes(context); - } - - @Override - protected void dispose(ScanningContext context) { - // only remove this file from the existing ResourceItem. - getFolder().getRepository().removeFile(mType, this); - - // Ask for an ID refresh since we're removing an item that previously generated an ID - context.requestFullAapt(); - - // don't need to touch the content, it'll get reclaimed as this objects disappear. - // In the mean time other objects may need to access it. - } - - @Override - public Collection getResourceTypes() { - return FolderTypeRelationship.getRelatedResourceTypes(getFolder().getType()); - } - - @Override - public boolean hasResources(ResourceType type) { - return FolderTypeRelationship.match(type, getFolder().getType()); - } - - /* - * (non-Javadoc) - * @see com.android.ide.eclipse.editors.resources.manager.ResourceFile#getValue(com.android.ide.eclipse.common.resources.ResourceType, java.lang.String) - * - * This particular implementation does not care about the type or name since a - * SingleResourceFile represents a file generating only one resource. - * The value returned is the full absolute path of the file in OS form. - */ - @Override - public ResourceValue getValue(ResourceType type, String name) { - return mValue; - } - - /** - * Returns the name of the resources. - */ - private String getResourceName(ResourceType type) { - // get the name from the filename. - String name = getFile().getName(); - - int pos = name.indexOf('.'); - if (pos != -1) { - name = name.substring(0, pos); - } - - return name; - } - - /** - * Validates the associated resource file to make sure the attribute references are valid - * - * @return true if parsing succeeds and false if it fails - */ - private boolean validateAttributes(ScanningContext context) { - // We only need to check if it's a non-framework file (and an XML file; skip .png's) - if (!isFramework() && SdkUtils.endsWith(getFile().getName(), DOT_XML)) { - ValidatingResourceParser parser = new ValidatingResourceParser(context, false); - try { - IAbstractFile file = getFile(); - return parser.parse(file.getOsLocation(), file.getContents()); - } catch (Exception e) { - context.needsFullAapt(); - } - - return false; - } - - return true; - } -} diff --git a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ValidatingResourceParser.java b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ValidatingResourceParser.java deleted file mode 100644 index e1183e97c4..0000000000 --- a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ValidatingResourceParser.java +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated; - -import com.android.SdkConstants; -import com.android.annotations.NonNull; -import com.google.common.io.Closeables; -import java.io.BufferedInputStream; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import org.kxml2.io.KXmlParser; -import org.xmlpull.v1.XmlPullParser; -import org.xmlpull.v1.XmlPullParserException; - -/** - * @deprecated This class is part of an obsolete resource repository system that is no longer used - * in production code. The class is preserved temporarily for LayoutLib tests. - */ -@Deprecated -public class ValidatingResourceParser { - private final boolean mIsFramework; - private ScanningContext mContext; - - /** - * Creates a new {@link ValidatingResourceParser} - * - * @param context a context object with state for the current update, such - * as a place to stash errors encountered - * @param isFramework true if scanning a framework resource - */ - public ValidatingResourceParser( - @NonNull ScanningContext context, - boolean isFramework) { - mContext = context; - mIsFramework = isFramework; - } - - /** - * Parse the given input and return false if it contains errors, or if - * the context is already tagged as needing a full aapt run. - * - * @param path the full OS path to the file being parsed - * @param input the input stream of the XML to be parsed (will be closed by this method) - * @return true if parsing succeeds and false if it fails - * @throws IOException if reading the contents fails - */ - public boolean parse(final String path, InputStream input) - throws IOException { - // No need to validate framework files - if (mIsFramework) { - try { - Closeables.close(input, true /* swallowIOException */); - } catch (IOException e) { - // cannot happen - } - return true; - } - if (mContext.needsFullAapt()) { - try { - Closeables.close(input, true /* swallowIOException */); - } catch (IOException e) { - // cannot happen - } - return false; - } - - KXmlParser parser = new KXmlParser(); - try { - parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); - - if (input instanceof FileInputStream) { - input = new BufferedInputStream(input); - } - parser.setInput(input, SdkConstants.UTF_8); - - return parse(path, parser); - } catch (XmlPullParserException e) { - String message = e.getMessage(); - - // Strip off position description - int index = message.indexOf("(position:"); //$NON-NLS-1$ (Hardcoded in KXml) - if (index != -1) { - message = message.substring(0, index); - } - - String error = String.format("%1$s:%2$d: Error: %3$s", //$NON-NLS-1$ - path, parser.getLineNumber(), message); - mContext.addError(error); - return false; - } catch (RuntimeException e) { - // Some exceptions are thrown by the KXmlParser that are not XmlPullParserExceptions, - // such as this one: - // java.lang.RuntimeException: Undefined Prefix: w in org.kxml2.io.KXmlParser@... - // at org.kxml2.io.KXmlParser.adjustNsp(Unknown Source) - // at org.kxml2.io.KXmlParser.parseStartTag(Unknown Source) - String message = e.getMessage(); - String error = String.format("%1$s:%2$d: Error: %3$s", //$NON-NLS-1$ - path, parser.getLineNumber(), message); - mContext.addError(error); - return false; - } finally { - try { - Closeables.close(input, true /* swallowIOException */); - } catch (IOException e) { - // cannot happen - } - } - } - - private boolean parse(String path, KXmlParser parser) - throws XmlPullParserException, IOException { - boolean checkForErrors = !mIsFramework && !mContext.needsFullAapt(); - - while (true) { - int event = parser.next(); - if (event == XmlPullParser.START_TAG) { - for (int i = 0, n = parser.getAttributeCount(); i < n; i++) { - String attribute = parser.getAttributeName(i); - String value = parser.getAttributeValue(i); - assert value != null : attribute; - - if (checkForErrors) { - String uri = parser.getAttributeNamespace(i); - if (!mContext.checkValue(uri, attribute, value)) { - mContext.requestFullAapt(); - return false; - } - } - } - } else if (event == XmlPullParser.END_DOCUMENT) { - break; - } - } - - return true; - } -} diff --git a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ValueResourceParser.java b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ValueResourceParser.java deleted file mode 100644 index 8b95b91458..0000000000 --- a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/ide/common/resources/deprecated/ValueResourceParser.java +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright (C) 2018 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated; - -import com.android.ide.common.rendering.api.ArrayResourceValueImpl; -import com.android.ide.common.rendering.api.AttrResourceValueImpl; -import com.android.ide.common.rendering.api.ResourceNamespace; -import com.android.ide.common.rendering.api.ResourceReference; -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.rendering.api.ResourceValueImpl; -import com.android.ide.common.rendering.api.StyleItemResourceValue; -import com.android.ide.common.rendering.api.StyleItemResourceValueImpl; -import com.android.ide.common.rendering.api.StyleResourceValueImpl; -import com.android.ide.common.rendering.api.StyleableResourceValueImpl; -import com.android.ide.common.resources.ValueXmlHelper; -import com.android.resources.ResourceType; -import com.google.common.base.Strings; -import org.xml.sax.Attributes; -import org.xml.sax.SAXException; -import org.xml.sax.helpers.DefaultHandler; - -import static com.android.SdkConstants.ANDROID_NS_NAME_PREFIX; -import static com.android.SdkConstants.ANDROID_NS_NAME_PREFIX_LEN; -import static com.android.SdkConstants.ATTR_NAME; -import static com.android.SdkConstants.ATTR_PARENT; -import static com.android.SdkConstants.ATTR_VALUE; -import static com.android.SdkConstants.TAG_RESOURCES; - -/** - * @deprecated This class is part of an obsolete resource repository system that is no longer used - * in production code. The class is preserved temporarily for LayoutLib tests. - */ -@Deprecated -public final class ValueResourceParser extends DefaultHandler { - - private static final ResourceReference TMP_REF = - new ResourceReference(ResourceNamespace.RES_AUTO, ResourceType.STRING, "_tmp"); - - public interface IValueResourceRepository { - void addResourceValue(ResourceValue value); - } - - private boolean inResources; - private int mDepth; - private ResourceValueImpl mCurrentValue; - private ArrayResourceValueImpl mArrayResourceValue; - private StyleResourceValueImpl mCurrentStyle; - private StyleableResourceValueImpl mCurrentDeclareStyleable; - private AttrResourceValueImpl mCurrentAttr; - private IValueResourceRepository mRepository; - private final boolean mIsFramework; - private final String mLibraryName; - - public ValueResourceParser(IValueResourceRepository repository, boolean isFramework, String libraryName) { - mRepository = repository; - mIsFramework = isFramework; - mLibraryName = libraryName; - } - - @Override - public void endElement(String uri, String localName, String qName) throws SAXException { - if (mCurrentValue != null) { - String value = mCurrentValue.getValue(); - value = value == null ? "" : ValueXmlHelper.unescapeResourceString(value, false, true); - mCurrentValue.setValue(value); - } - - if (inResources && qName.equals(TAG_RESOURCES)) { - inResources = false; - } else if (mDepth == 2) { - mCurrentValue = null; - mCurrentStyle = null; - mCurrentDeclareStyleable = null; - mCurrentAttr = null; - mArrayResourceValue = null; - } else if (mDepth == 3) { - if (mArrayResourceValue != null && mCurrentValue != null) { - mArrayResourceValue.addElement(mCurrentValue.getValue()); - } - mCurrentValue = null; - //noinspection VariableNotUsedInsideIf - if (mCurrentDeclareStyleable != null) { - mCurrentAttr = null; - } - } - - mDepth--; - super.endElement(uri, localName, qName); - } - - @Override - public void startElement(String uri, String localName, String qName, Attributes attributes) - throws SAXException { - try { - ResourceNamespace namespace = ResourceNamespace.fromBoolean(mIsFramework); - mDepth++; - if (!inResources && mDepth == 1) { - if (qName.equals(TAG_RESOURCES)) { - inResources = true; - } - } else if (mDepth == 2 && inResources) { - ResourceType type = - ResourceType.fromXmlTag( - new Object(), (t) -> qName, (t, name) -> attributes.getValue(name)); - - if (type != null) { - // get the resource name - String name = attributes.getValue(ATTR_NAME); - if (name != null) { - switch (type) { - case STYLE: - String parent = attributes.getValue(ATTR_PARENT); - mCurrentStyle = - new StyleResourceValueImpl( - namespace, name, parent, mLibraryName); - mRepository.addResourceValue(mCurrentStyle); - break; - case STYLEABLE: - mCurrentDeclareStyleable = - new StyleableResourceValueImpl( - namespace, name, null, mLibraryName); - mRepository.addResourceValue(mCurrentDeclareStyleable); - break; - case ATTR: - mCurrentAttr = - new AttrResourceValueImpl(namespace, name, mLibraryName); - mRepository.addResourceValue(mCurrentAttr); - break; - case ARRAY: - mArrayResourceValue = - new ArrayResourceValueImpl(namespace, name, mLibraryName); - mRepository.addResourceValue(mArrayResourceValue); - break; - default: - mCurrentValue = - new ResourceValueImpl( - namespace, type, name, null, mLibraryName); - mRepository.addResourceValue(mCurrentValue); - break; - } - } - } - } else if (mDepth == 3) { - // get the resource name - String name = attributes.getValue(ATTR_NAME); - if (!Strings.isNullOrEmpty(name)) { - if (mCurrentStyle != null) { - mCurrentValue = - new StyleItemResourceValueImpl( - mCurrentStyle.getNamespace(), name, null, mLibraryName); - mCurrentStyle.addItem((StyleItemResourceValue) mCurrentValue); - } else if (mCurrentDeclareStyleable != null) { - // is the attribute in the android namespace? - boolean isFramework = mIsFramework; - if (name.startsWith(ANDROID_NS_NAME_PREFIX)) { - name = name.substring(ANDROID_NS_NAME_PREFIX_LEN); - isFramework = true; - } - - mCurrentAttr = new AttrResourceValueImpl(namespace, name, mLibraryName); - mCurrentDeclareStyleable.addValue(mCurrentAttr); - - // also add it to the repository. - mRepository.addResourceValue(mCurrentAttr); - - } else if (mCurrentAttr != null) { - // get the enum/flag value - String value = attributes.getValue(ATTR_VALUE); - - try { - // Integer.decode/parseInt can't deal with hex value > 0x7FFFFFFF so we - // use Long.decode instead. - mCurrentAttr.addValue(name, Long.decode(value).intValue(), null); - } catch (NumberFormatException e) { - // pass, we'll just ignore this value - } - } - } else //noinspection VariableNotUsedInsideIf - if (mArrayResourceValue != null) { - // Create a temporary resource value to hold the item's value. The value is - // not added to the repository, since it's just a holder. The value will be set - // in the `characters` method and then added to mArrayResourceValue in `endElement`. - mCurrentValue = new ResourceValueImpl(TMP_REF, null); - } - } else if (mDepth == 4 && mCurrentAttr != null) { - // get the enum/flag name - String name = attributes.getValue(ATTR_NAME); - String value = attributes.getValue(ATTR_VALUE); - - try { - // Integer.decode/parseInt can't deal with hex value > 0x7FFFFFFF so we - // use Long.decode instead. - mCurrentAttr.addValue(name, Long.decode(value).intValue(), null); - } catch (NumberFormatException e) { - // pass, we'll just ignore this value - } - } - } finally { - super.startElement(uri, localName, qName, attributes); - } - } - - @Override - public void characters(char[] ch, int start, int length) { - if (mCurrentValue != null) { - String value = mCurrentValue.getValue(); - if (value == null) { - mCurrentValue.setValue(new String(ch, start, length)); - } else { - mCurrentValue.setValue(value + new String(ch, start, length)); - } - } - } -} diff --git a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/io/FileWrapper.java b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/io/FileWrapper.java deleted file mode 100644 index e471979292..0000000000 --- a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/io/FileWrapper.java +++ /dev/null @@ -1,158 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package app.cash.paparazzi.deprecated.com.android.io; - - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.net.URI; - -/** - * An implementation of {@link IAbstractFile} extending {@link File}. - */ -public class FileWrapper extends File implements IAbstractFile { - private static final long serialVersionUID = 1L; - - /** - * Creates a new File instance matching a given {@link File} object. - * @param file the file to match - */ - public FileWrapper(File file) { - super(file.getAbsolutePath()); - } - - /** - * Creates a new File instance from a parent abstract pathname and a child pathname string. - * @param parent the parent pathname - * @param child the child name - * - * @see File#File(File, String) - */ - public FileWrapper(File parent, String child) { - super(parent, child); - } - - /** - * Creates a new File instance by converting the given pathname string into an abstract - * pathname. - * @param osPathname the OS pathname - * - * @see File#File(String) - */ - public FileWrapper(String osPathname) { - super(osPathname); - } - - /** - * Creates a new File instance from a parent abstract pathname and a child pathname string. - * @param parent the parent pathname - * @param child the child name - * - * @see File#File(String, String) - */ - public FileWrapper(String parent, String child) { - super(parent, child); - } - - /** - * Creates a new File instance by converting the given file: URI into an - * abstract pathname. - * @param uri An absolute, hierarchical URI with a scheme equal to "file", a non-empty path - * component, and undefined authority, query, and fragment components - * - * @see File#File(URI) - */ - public FileWrapper(URI uri) { - super(uri); - } - - @Override - public InputStream getContents() throws StreamException { - try { - return new FileInputStream(this); - } catch (FileNotFoundException e) { - throw new StreamException(e, this, StreamException.Error.FILENOTFOUND); - } - } - - @Override - public void setContents(InputStream source) throws StreamException { - FileOutputStream fos = null; - try { - fos = new FileOutputStream(this); - - byte[] buffer = new byte[1024]; - int count = 0; - while ((count = source.read(buffer)) != -1) { - fos.write(buffer, 0, count); - } - } catch (IOException e) { - throw new StreamException(e, this); - } finally { - if (fos != null) { - try { - fos.close(); - } catch (IOException e) { - throw new StreamException(e, this); - } - } - } - } - - @Override - public OutputStream getOutputStream() throws StreamException { - try { - return new FileOutputStream(this); - } catch (FileNotFoundException e) { - throw new StreamException(e, this); - } - } - - @Override - public PreferredWriteMode getPreferredWriteMode() { - return PreferredWriteMode.OUTPUTSTREAM; - } - - @Override - public String getOsLocation() { - return getAbsolutePath(); - } - - @Override - public boolean exists() { - return isFile(); - } - - @Override - public long getModificationStamp() { - return lastModified(); - } - - @Override - public IAbstractFolder getParentFolder() { - String p = this.getParent(); - if (p == null) { - return null; - } - return new FolderWrapper(p); - } -} diff --git a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/io/FolderWrapper.java b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/io/FolderWrapper.java deleted file mode 100644 index b3a2f72a24..0000000000 --- a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/io/FolderWrapper.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package app.cash.paparazzi.deprecated.com.android.io; - - -import java.io.File; -import java.net.URI; -import java.util.ArrayList; - -/** - * An implementation of {@link IAbstractFolder} extending {@link File}. - */ -public class FolderWrapper extends File implements IAbstractFolder { - - private static final long serialVersionUID = 1L; - - /** - * Creates a new File instance from a parent abstract pathname and a child pathname string. - * @param parent the parent pathname - * @param child the child name - * - * @see File#File(File, String) - */ - public FolderWrapper(File parent, String child) { - super(parent, child); - } - - /** - * Creates a new File instance by converting the given pathname string into an abstract - * pathname. - * @param pathname the pathname - * - * @see File#File(String) - */ - public FolderWrapper(String pathname) { - super(pathname); - } - - /** - * Creates a new File instance from a parent abstract pathname and a child pathname string. - * @param parent the parent pathname - * @param child the child name - * - * @see File#File(String, String) - */ - public FolderWrapper(String parent, String child) { - super(parent, child); - } - - /** - * Creates a new File instance by converting the given file: URI into an - * abstract pathname. - * @param uri An absolute, hierarchical URI with a scheme equal to "file", a non-empty path - * component, and undefined authority, query, and fragment components - * - * @see File#File(URI) - */ - public FolderWrapper(URI uri) { - super(uri); - } - - /** - * Creates a new File instance matching a give {@link File} object. - * @param file the file to match - */ - public FolderWrapper(File file) { - super(file.getAbsolutePath()); - } - - @Override - public IAbstractResource[] listMembers() { - File[] files = listFiles(); - final int count = files == null ? 0 : files.length; - IAbstractResource[] afiles = new IAbstractResource[count]; - - if (files != null) { - for (int i = 0 ; i < count ; i++) { - File f = files[i]; - if (f.isFile()) { - afiles[i] = new FileWrapper(f); - } else if (f.isDirectory()) { - afiles[i] = new FolderWrapper(f); - } - } - } - - return afiles; - } - - @Override - public boolean hasFile(final String name) { - String[] match = list(new FilenameFilter() { - @Override - public boolean accept(IAbstractFolder dir, String filename) { - return name.equals(filename); - } - }); - - return match.length > 0; - } - - @Override - public IAbstractFile getFile(String name) { - return new FileWrapper(this, name); - } - - @Override - public IAbstractFolder getFolder(String name) { - return new FolderWrapper(this, name); - } - - @Override - public IAbstractFolder getParentFolder() { - String p = this.getParent(); - if (p == null) { - return null; - } - return new FolderWrapper(p); - } - - @Override - public String getOsLocation() { - return getAbsolutePath(); - } - - @Override - public boolean exists() { - return isDirectory(); - } - - @Override - public String[] list(FilenameFilter filter) { - File[] files = listFiles(); - if (files != null && files.length > 0) { - ArrayList list = new ArrayList(); - - for (File file : files) { - if (filter.accept(this, file.getName())) { - list.add(file.getName()); - } - } - - return list.toArray(new String[0]); - } - - return new String[0]; - } -} diff --git a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/io/IAbstractFile.java b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/io/IAbstractFile.java deleted file mode 100644 index 2a44295216..0000000000 --- a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/io/IAbstractFile.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package app.cash.paparazzi.deprecated.com.android.io; - -import java.io.InputStream; -import java.io.OutputStream; - -/** - * A file. - */ -public interface IAbstractFile extends IAbstractResource { - enum PreferredWriteMode { - INPUTSTREAM, OUTPUTSTREAM - } - - /** - * Returns an {@link InputStream} object on the file content. - * - * The stream must be closed by the caller. - * - * @throws StreamException - */ - InputStream getContents() throws StreamException; - - /** - * Sets the content of the file. - * @param source the content - * @throws StreamException - */ - void setContents(InputStream source) throws StreamException; - - /** - * Returns an {@link OutputStream} to write into the file. - * @throws StreamException - */ - OutputStream getOutputStream() throws StreamException; - - /** - * Returns the preferred mode to write into the file. - */ - PreferredWriteMode getPreferredWriteMode(); - - /** - * Returns the last modification timestamp - */ - long getModificationStamp(); -} diff --git a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/io/IAbstractFolder.java b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/io/IAbstractFolder.java deleted file mode 100644 index a5daf10798..0000000000 --- a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/io/IAbstractFolder.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package app.cash.paparazzi.deprecated.com.android.io; - -import java.io.File; - -/** - * A folder. - */ -public interface IAbstractFolder extends IAbstractResource { - /** - * Instances of classes that implement this interface are used to - * filter filenames. - */ - interface FilenameFilter { - /** - * Tests if a specified file should be included in a file list. - * - * @param dir the directory in which the file was found. - * @param name the name of the file. - * @return true if and only if the name should be - * included in the file list; false otherwise. - */ - boolean accept(IAbstractFolder dir, String name); - } - - /** - * Returns true if the receiver contains a file with a given name - * @param name the name of the file. This is the name without the path leading to the - * parent folder. - */ - boolean hasFile(String name); - - /** - * Returns an {@link IAbstractFile} representing a child of the current folder with the - * given name. The file may not actually exist. - * @param name the name of the file. - */ - IAbstractFile getFile(String name); - - /** - * Returns an {@link IAbstractFolder} representing a child of the current folder with the - * given name. The folder may not actually exist. - * @param name the name of the folder. - */ - IAbstractFolder getFolder(String name); - - /** - * Returns a list of all existing file and directory members in this folder. - * The returned array can be empty but is never null. - */ - IAbstractResource[] listMembers(); - - /** - * Returns a list of all existing file and directory members in this folder - * that satisfy the specified filter. - * - * @param filter A filename filter instance. Must not be null. - * @return An array of file names (generated using {@link File#getName()}). - * The array can be empty but is never null. - */ - String[] list(FilenameFilter filter); -} diff --git a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/io/IAbstractResource.java b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/io/IAbstractResource.java deleted file mode 100644 index 1004932f7d..0000000000 --- a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/io/IAbstractResource.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package app.cash.paparazzi.deprecated.com.android.io; - -/** - * Base representation of a file system resource. - *

- * This somewhat limited interface is designed to let classes use file-system resources, without - * having the manually handle either the standard Java file or the Eclipse file API.. - */ -public interface IAbstractResource { - - /** - * Returns the name of the resource. - */ - String getName(); - - /** - * Returns the OS path of the folder location (may be absolute). - */ - String getOsLocation(); - - /** - * Returns the path of the resource. - */ - String getPath(); - - /** - * Returns whether the resource actually exists. - */ - boolean exists(); - - /** - * Returns the parent folder or null if there is no parent. - */ - IAbstractFolder getParentFolder(); - - /** - * Deletes the resource. - */ - boolean delete(); -} diff --git a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/io/StreamException.java b/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/io/StreamException.java deleted file mode 100644 index a4401a20a3..0000000000 --- a/paparazzi/src/main/java/app/cash/paparazzi/deprecated/com/android/io/StreamException.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package app.cash.paparazzi.deprecated.com.android.io; - -/** - * Exception thrown when {@link IAbstractFile#getContents()} fails. - */ -public class StreamException extends Exception { - private static final long serialVersionUID = 1L; - - public enum Error { - DEFAULT, OUTOFSYNC, FILENOTFOUND - } - - private final Error mError; - private final IAbstractFile mFile; - - public StreamException(Exception e, IAbstractFile file) { - this(e, file, Error.DEFAULT); - } - - public StreamException(Exception e, IAbstractFile file, Error error) { - super(e); - mFile = file; - mError = error; - } - - public Error getError() { - return mError; - } - - public IAbstractFile getFile() { - return mFile; - } -} diff --git a/paparazzi/src/main/java/app/cash/paparazzi/internal/PaparazziAssetRepository.kt b/paparazzi/src/main/java/app/cash/paparazzi/internal/PaparazziAssetRepository.kt index d6a57d2170..6109ad2c2f 100644 --- a/paparazzi/src/main/java/app/cash/paparazzi/internal/PaparazziAssetRepository.kt +++ b/paparazzi/src/main/java/app/cash/paparazzi/internal/PaparazziAssetRepository.kt @@ -24,7 +24,6 @@ import java.io.IOException import java.io.InputStream internal class PaparazziAssetRepository( - private val assetPath: String, private val assetDirs: List = emptyList() ) : AssetRepository() { @Throws(FileNotFoundException::class) @@ -43,17 +42,13 @@ internal class PaparazziAssetRepository( path: String, mode: Int ): InputStream? { - if (assetDirs.isEmpty()) { - return open("$assetPath/$path") - } else { - for (assetDir in assetDirs) { - val assetFile = open("$assetDir/$path") - if (assetFile != null) { - return assetFile - } + for (assetDir in assetDirs) { + val assetFile = open("$assetDir/$path") + if (assetFile != null) { + return assetFile } - return null } + return null } @Throws(IOException::class) diff --git a/paparazzi/src/main/java/app/cash/paparazzi/internal/Renderer.kt b/paparazzi/src/main/java/app/cash/paparazzi/internal/Renderer.kt index f5153fed6d..aa2a08ed63 100644 --- a/paparazzi/src/main/java/app/cash/paparazzi/internal/Renderer.kt +++ b/paparazzi/src/main/java/app/cash/paparazzi/internal/Renderer.kt @@ -20,10 +20,6 @@ import app.cash.paparazzi.DeviceConfig import app.cash.paparazzi.Environment import app.cash.paparazzi.Flags import app.cash.paparazzi.Paparazzi -import app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated.FrameworkResources -import app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated.ResourceItem -import app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated.ResourceRepository -import app.cash.paparazzi.deprecated.com.android.io.FolderWrapper import app.cash.paparazzi.getFieldReflectively import app.cash.paparazzi.internal.resources.AarSourceResourceRepository import app.cash.paparazzi.internal.resources.AppResourceRepository @@ -51,59 +47,30 @@ internal class Renderer( fun prepare(): SessionParamsBuilder { val platformDataResDir = File("${environment.platformDir}/data/res") - val useLegacyResourceLoading = System.getProperty(Flags.LEGACY_RESOURCE_LOADING).toBoolean() - val (frameworkResources, projectResources) = - if (useLegacyResourceLoading) { - ResourceRepositoryBridge.Legacy( - FrameworkResources(FolderWrapper(platformDataResDir)) - .apply { - loadResources() - loadPublicResources(logger) - } - ) to - ResourceRepositoryBridge.Legacy( - object : ResourceRepository(FolderWrapper(environment.resDir), false) { - override fun createResourceItem(name: String): ResourceItem { - return ResourceItem(name) - } - }.apply { loadResources() } - ) - } else { - ResourceRepositoryBridge.New( - FrameworkResourceRepository.create( - resourceDirectoryOrFile = platformDataResDir.toPath(), - languagesToLoad = emptySet(), - useCompiled9Patches = false - ) - ) to - ResourceRepositoryBridge.New( - AppResourceRepository.create( - localResourceDirectories = environment.localResourceDirs.map { File(it) }, - moduleResourceDirectories = environment.moduleResourceDirs.map { File(it) }, - libraryRepositories = environment.libraryResourceDirs.map { dir -> - val resourceDirPath = Paths.get(dir) - AarSourceResourceRepository.create( - resourceDirectoryOrFile = resourceDirPath, - libraryName = resourceDirPath.parent.fileName.name // segment before /res - ) - } - ) - ) + val frameworkResources = FrameworkResourceRepository.create( + resourceDirectoryOrFile = platformDataResDir.toPath(), + languagesToLoad = emptySet(), + useCompiled9Patches = false + ) + val projectResources = AppResourceRepository.create( + localResourceDirectories = environment.localResourceDirs.map { File(it) }, + moduleResourceDirectories = environment.moduleResourceDirs.map { File(it) }, + libraryRepositories = environment.libraryResourceDirs.map { dir -> + val resourceDirPath = Paths.get(dir) + AarSourceResourceRepository.create( + resourceDirectoryOrFile = resourceDirPath, + libraryName = resourceDirPath.parent.fileName.name // segment before /res + ) } + ) - val useLegacyAssetLoading = System.getProperty(Flags.LEGACY_ASSET_LOADING).toBoolean() sessionParamsBuilder = SessionParamsBuilder( layoutlibCallback = layoutlibCallback, logger = logger, frameworkResources = frameworkResources, projectResources = projectResources, assetRepository = PaparazziAssetRepository( - assetPath = environment.assetsDir, - assetDirs = if (useLegacyAssetLoading) { - emptyList() - } else { - environment.allModuleAssetDirs + environment.libraryAssetDirs - } + assetDirs = environment.allModuleAssetDirs + environment.libraryAssetDirs ) ) .plusFlag(RenderParamsFlags.FLAG_DO_NOT_RENDER_ON_CREATE, true) diff --git a/paparazzi/src/main/java/app/cash/paparazzi/internal/ResourceRepositoryBridge.kt b/paparazzi/src/main/java/app/cash/paparazzi/internal/ResourceRepositoryBridge.kt deleted file mode 100644 index 4d4fcba75f..0000000000 --- a/paparazzi/src/main/java/app/cash/paparazzi/internal/ResourceRepositoryBridge.kt +++ /dev/null @@ -1,9 +0,0 @@ -package app.cash.paparazzi.internal - -import app.cash.paparazzi.deprecated.com.android.ide.common.resources.deprecated.ResourceRepository as LegacyResourceRepository -import com.android.ide.common.resources.ResourceRepository as NewResourceRepository - -sealed interface ResourceRepositoryBridge { - class Legacy(val repository: LegacyResourceRepository) : ResourceRepositoryBridge - class New(val repository: NewResourceRepository) : ResourceRepositoryBridge -} diff --git a/paparazzi/src/main/java/app/cash/paparazzi/internal/SessionParamsBuilder.kt b/paparazzi/src/main/java/app/cash/paparazzi/internal/SessionParamsBuilder.kt index fd2d3000cf..d2f1a4ec20 100644 --- a/paparazzi/src/main/java/app/cash/paparazzi/internal/SessionParamsBuilder.kt +++ b/paparazzi/src/main/java/app/cash/paparazzi/internal/SessionParamsBuilder.kt @@ -17,8 +17,6 @@ package app.cash.paparazzi.internal import app.cash.paparazzi.DeviceConfig -import app.cash.paparazzi.internal.ResourceRepositoryBridge.Legacy -import app.cash.paparazzi.internal.ResourceRepositoryBridge.New import app.cash.paparazzi.internal.parsers.LayoutPullParser import app.cash.paparazzi.internal.resources.pseudolocalizeIfNeeded import com.android.SdkConstants @@ -28,6 +26,7 @@ import com.android.ide.common.rendering.api.ResourceReference import com.android.ide.common.rendering.api.SessionParams import com.android.ide.common.rendering.api.SessionParams.Key import com.android.ide.common.rendering.api.SessionParams.RenderingMode +import com.android.ide.common.resources.ResourceRepository import com.android.ide.common.resources.ResourceResolver import com.android.ide.common.resources.ResourceValueMap import com.android.ide.common.resources.getConfiguredResources @@ -39,9 +38,9 @@ import com.android.resources.ResourceType internal data class SessionParamsBuilder( private val layoutlibCallback: PaparazziCallback, private val logger: PaparazziLogger, - private val frameworkResources: ResourceRepositoryBridge, + private val frameworkResources: ResourceRepository, private val assetRepository: AssetRepository, - private val projectResources: ResourceRepositoryBridge, + private val projectResources: ResourceRepository, private val deviceConfig: DeviceConfig = DeviceConfig.NEXUS_5, private val renderingMode: RenderingMode = RenderingMode.NORMAL, private val targetSdk: Int = 22, @@ -81,32 +80,15 @@ internal data class SessionParamsBuilder( val folderConfiguration = deviceConfig.folderConfiguration val resourceResolver = ResourceResolver.create( mapOf>( - when (frameworkResources) { - is Legacy -> - ResourceNamespace.ANDROID to - frameworkResources.repository.getConfiguredResources(folderConfiguration) - - is New -> - ResourceNamespace.ANDROID to - frameworkResources.repository.getConfiguredResources(folderConfiguration) - .pseudolocalizeIfNeeded(folderConfiguration.localeQualifier) - .row(ResourceNamespace.ANDROID) - }, - *when (projectResources) { - is Legacy -> { - arrayOf( - ResourceNamespace.TODO() to - projectResources.repository.getConfiguredResources(folderConfiguration) - ) - } - - is New -> - projectResources.repository.getConfiguredResources(folderConfiguration) - .pseudolocalizeIfNeeded(folderConfiguration.localeQualifier) - .rowMap() - .map { (key, value) -> key to value } - .toTypedArray() - } + ResourceNamespace.ANDROID to + frameworkResources.getConfiguredResources(folderConfiguration) + .pseudolocalizeIfNeeded(folderConfiguration.localeQualifier) + .row(ResourceNamespace.ANDROID), + *projectResources.getConfiguredResources(folderConfiguration) + .pseudolocalizeIfNeeded(folderConfiguration.localeQualifier) + .rowMap() + .map { (key, value) -> key to value } + .toTypedArray() ), ResourceReference( ResourceNamespace.fromBoolean(!isProjectTheme),