1 /*
<lambda>null2  * Copyright 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package androidx.glance.appwidget.layoutgenerator.gradle
18 
19 import androidx.glance.appwidget.layoutgenerator.GeneratedFiles
20 import androidx.glance.appwidget.layoutgenerator.LayoutGenerator
21 import androidx.glance.appwidget.layoutgenerator.cleanResources
22 import androidx.glance.appwidget.layoutgenerator.generateRegistry
23 import com.android.build.api.variant.AndroidComponentsExtension
24 import java.io.File
25 import org.gradle.api.DefaultTask
26 import org.gradle.api.Project
27 import org.gradle.api.file.DirectoryProperty
28 import org.gradle.api.tasks.CacheableTask
29 import org.gradle.api.tasks.InputDirectory
30 import org.gradle.api.tasks.OutputDirectory
31 import org.gradle.api.tasks.PathSensitive
32 import org.gradle.api.tasks.PathSensitivity
33 import org.gradle.api.tasks.TaskAction
34 
35 /**
36  * Task generating the layouts from a set of Layout templates.
37  *
38  * See [LayoutGenerator] for details on the template format, and [generateRegistry] for details on
39  * the Kotlin code generated to access those layouts from code.
40  */
41 @CacheableTask
42 abstract class LayoutGeneratorTask : DefaultTask() {
43 
44     @get:PathSensitive(PathSensitivity.NAME_ONLY)
45     @get:InputDirectory
46     abstract val containerLayoutDirectory: DirectoryProperty
47 
48     @get:PathSensitive(PathSensitivity.NAME_ONLY)
49     @get:InputDirectory
50     abstract val childLayoutDirectory: DirectoryProperty
51 
52     @get:OutputDirectory abstract val outputSourceDir: DirectoryProperty
53 
54     @get:OutputDirectory abstract val outputResourcesDir: DirectoryProperty
55 
56     @TaskAction
57     fun execute() {
58 
59         outputSourceDir.asFile.get().mkdirs()
60         outputResourcesDir.asFile.get().mkdirs()
61 
62         val generatedLayouts =
63             LayoutGenerator()
64                 .generateAllFiles(
65                     checkNotNull(containerLayoutDirectory.get().asFile.listFiles()).asList(),
66                     checkNotNull(childLayoutDirectory.get().asFile.listFiles()).asList(),
67                     outputResourcesDir.get().asFile
68                 )
69         generateRegistry(
70             packageName = outputModule,
71             layouts = generatedLayouts.generatedContainers,
72             boxChildLayouts = generatedLayouts.generatedBoxChildren,
73             rowColumnChildLayouts = generatedLayouts.generatedRowColumnChildren,
74             outputSourceDir = outputSourceDir.get().asFile
75         )
76         cleanResources(outputResourcesDir.get().asFile, generatedLayouts.extractGeneratedFiles())
77     }
78 
79     private fun GeneratedFiles.extractGeneratedFiles(): Set<File> =
80         generatedContainers.values
81             .flatMap { container -> container.map { it.generatedFile } }
82             .toSet() +
83             generatedBoxChildren.values
84                 .flatMap { child -> child.map { it.generatedFile } }
85                 .toSet() +
86             generatedRowColumnChildren.values
87                 .flatMap { child -> child.map { it.generatedFile } }
88                 .toSet() +
89             extraFiles
90 
91     companion object {
92         /** Registers [LayoutGeneratorTask] in [project] for all variants. */
93         @JvmStatic
94         fun registerLayoutGenerator(
95             project: Project,
96             containerLayoutDirectory: File,
97             childLayoutDirectory: File,
98         ) {
99 
100             val outputDirectory = "generatedLayouts"
101             val buildDirectory = project.layout.buildDirectory
102 
103             val taskName = "generateLayouts"
104 
105             val task =
106                 project.tasks.register(taskName, LayoutGeneratorTask::class.java) {
107                     it.containerLayoutDirectory.set(containerLayoutDirectory)
108                     it.childLayoutDirectory.set(childLayoutDirectory)
109                     it.outputSourceDir.set(buildDirectory.dir("$outputDirectory/kotlin"))
110                     it.outputResourcesDir.set(buildDirectory.dir("$outputDirectory/res/layouts"))
111                 }
112 
113             project.extensions.getByType(AndroidComponentsExtension::class.java).onVariants {
114                 variant ->
115                 variant.sources.java?.addGeneratedSourceDirectory(
116                     task,
117                     LayoutGeneratorTask::outputSourceDir
118                 )
119                 variant.sources.res?.addGeneratedSourceDirectory(
120                     task,
121                     LayoutGeneratorTask::outputResourcesDir
122                 )
123             }
124         }
125     }
126 }
127 
128 private const val outputModule: String = "androidx.glance.appwidget"
129