1717
1818package com.itsaky.androidide.tooling.impl.internal
1919
20+ import com.android.builder.model.v2.dsl.BuildType
2021import com.android.builder.model.v2.ide.AndroidArtifact
2122import com.android.builder.model.v2.ide.GraphItem
2223import com.android.builder.model.v2.ide.Library
24+ import com.android.builder.model.v2.ide.ProjectType
2325import com.android.builder.model.v2.ide.Variant
26+ import com.android.builder.model.v2.models.AndroidDsl
2427import com.android.builder.model.v2.models.AndroidProject
2528import com.android.builder.model.v2.models.BasicAndroidProject
2629import com.android.builder.model.v2.models.VariantDependencies
@@ -40,9 +43,9 @@ import com.itsaky.androidide.tooling.api.models.ProjectMetadata
4043import com.itsaky.androidide.tooling.api.models.params.StringParameter
4144import com.itsaky.androidide.tooling.api.util.AndroidModulePropertyCopier
4245import com.itsaky.androidide.tooling.api.util.AndroidModulePropertyCopier.copy
43- import com.itsaky.androidide.tooling.api.util.compareSemanticVersions
4446import com.itsaky.androidide.tooling.api.util.extractPackageName
4547import com.itsaky.androidide.utils.AndroidPluginVersion
48+ import com.itsaky.androidide.utils.capitalizeString
4649import org.gradle.tooling.model.GradleProject
4750import java.io.File
4851import java.io.Serializable
@@ -57,7 +60,8 @@ internal class AndroidProjectImpl(
5760 private val basicAndroidProject : BasicAndroidProject ,
5861 private val androidProject : AndroidProject ,
5962 private val variantDependencies : VariantDependencies ,
60- private val versions : Versions
63+ private val versions : Versions ,
64+ private val androidDsl : AndroidDsl ,
6165) : GradleProjectImpl(gradleProject), IAndroidProject, Serializable {
6266
6367 private val serialVersionUID = 1L
@@ -84,19 +88,17 @@ internal class AndroidProjectImpl(
8488 }
8589 }
8690
87- private fun AndroidArtifact.toMetadata (name : String ): AndroidArtifactMetadata {
88- return AndroidArtifactMetadata (
89- name = name,
90- resGenTaskName = resGenTaskName,
91+ private fun AndroidArtifact.toMetadata (variantName : String ): AndroidArtifactMetadata {
92+ return AndroidArtifactMetadata (name = variantName,
93+ applicationId = computeApplicationId(variantName), resGenTaskName = resGenTaskName,
9194 assembleTaskOutputListingFile = assembleTaskOutputListingFile,
9295 generatedResourceFolders = generatedResourceFolders,
9396 generatedSourceFolders = generatedSourceFolders, maxSdkVersion = maxSdkVersion,
9497 minSdkVersion = minSdkVersion.apiLevel, signingConfigName = signingConfigName,
9598 sourceGenTaskName = sourceGenTaskName, assembleTaskName = assembleTaskName,
9699 classJars = classesFolders.filter { it.name.endsWith(" .jar" ) },
97100 compileTaskName = compileTaskName,
98- targetSdkVersionOverride = targetSdkVersionOverride?.apiLevel ? : - 1
99- )
101+ targetSdkVersionOverride = targetSdkVersionOverride?.apiLevel ? : - 1 )
100102 }
101103
102104 override fun getVariant (param : StringParameter ): CompletableFuture <AndroidVariantMetadata ?> {
@@ -106,11 +108,8 @@ internal class AndroidProjectImpl(
106108 }
107109
108110 private fun Variant.toMetadata (): AndroidVariantMetadata {
109- return AndroidVariantMetadata (
110- name = name,
111- mainArtifact = mainArtifact.toMetadata(name),
112- otherArtifacts = mutableMapOf ()
113- )
111+ return AndroidVariantMetadata (name = name, mainArtifact = mainArtifact.toMetadata(name),
112+ otherArtifacts = mutableMapOf ())
114113 }
115114
116115 override fun getBootClasspaths (): CompletableFuture <Collection <File >> {
@@ -131,11 +130,8 @@ internal class AndroidProjectImpl(
131130 }
132131 }
133132
134- private fun fillLibrary (
135- item : GraphItem ,
136- libraries : Map <String , Library >,
137- seen : HashMap <String , DefaultLibrary >
138- ): DefaultLibrary ? {
133+ private fun fillLibrary (item : GraphItem , libraries : Map <String , Library >,
134+ seen : HashMap <String , DefaultLibrary >): DefaultLibrary ? {
139135
140136 val lib = libraries[item.key] ? : return null
141137 val library = copy(lib)
@@ -164,8 +160,9 @@ internal class AndroidProjectImpl(
164160 return CompletableFuture .supplyAsync {
165161
166162 // model sync files available only in v7.3.0 and later
167- return @supplyAsync if (AndroidPluginVersion .parse(versions.agp) >= AndroidPluginVersion (7 , 3 , 0 ))
168- copy(androidProject.modelSyncFiles)
163+ return @supplyAsync if (AndroidPluginVersion .parse(versions.agp) >= AndroidPluginVersion (7 , 3 ,
164+ 0 )
165+ ) copy(androidProject.modelSyncFiles)
169166 else emptyList()
170167 }
171168 }
@@ -191,22 +188,13 @@ internal class AndroidProjectImpl(
191188 val gradleMetadata = super .getMetadata().get()
192189
193190 val viewBindingOptions = androidProject.viewBindingOptions?.let (
194- AndroidModulePropertyCopier ::copy)
195- ? : DefaultViewBindingOptions ()
191+ AndroidModulePropertyCopier ::copy) ? : DefaultViewBindingOptions ()
196192
197- return @supplyAsync AndroidProjectMetadata (
198- gradleMetadata,
199- packageName,
200- basicAndroidProject.projectType,
201- copy(androidProject.flags),
202- copy(androidProject.javaCompileOptions),
203- viewBindingOptions,
204- androidProject.resourcePrefix,
205- androidProject.namespace,
206- androidProject.androidTestNamespace,
207- androidProject.testFixturesNamespace,
208- getClassesJar()
209- )
193+ return @supplyAsync AndroidProjectMetadata (gradleMetadata, packageName,
194+ basicAndroidProject.projectType, copy(androidProject.flags),
195+ copy(androidProject.javaCompileOptions), viewBindingOptions, androidProject.resourcePrefix,
196+ androidProject.namespace, androidProject.androidTestNamespace,
197+ androidProject.testFixturesNamespace, getClassesJar())
210198 }
211199 }
212200
@@ -234,4 +222,77 @@ internal class AndroidProjectImpl(
234222 this .packageName = extractPackageName(manifestFile) ? : UNKNOWN_PACKAGE
235223 this .shouldLookupPackage = false
236224 }
237- }
225+
226+ private fun AndroidArtifact.computeApplicationId (variantName : String ): String? {
227+ val minAgpForAppId = AndroidPluginVersion (7 , 4 , 0 )
228+ return if (minAgpForAppId <= AndroidPluginVersion .parse(versions.agp)) {
229+ applicationId
230+ } else {
231+ computeApplicationIdLegacy(variantName)
232+ }
233+ }
234+
235+ // Adapted from the following :
236+ // https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:build-system/gradle-core/src/main/java/com/android/build/gradle/internal/core/dsl/impl/ComponentDslInfoImpl.kt;drc=6a5551bdea55c0c991f1ccf1e3f8f6f3d2cd2cb7;l=107
237+ // https://cs.android.com/android-studio/platform/tools/base/+/mirror-goog-studio-main:build-system/gradle-core/src/main/java/com/android/build/gradle/internal/core/dsl/impl/VariantDslInfoImpl.kt;drc=d44f5b98cd5530eceb230e0d151ad96c4277f78d;l=109
238+
239+ protected fun computeApplicationIdLegacy (variantName : String ): String {
240+ val basicVariant = basicAndroidProject.variants.firstOrNull { it.name == variantName }
241+ val buildType = basicVariant?.buildType?.let { buildTypeName ->
242+ androidDsl.buildTypes.find { buildType -> buildType.name == buildTypeName }
243+ }!!
244+
245+ val appIdFromFlavor = if (basicAndroidProject.projectType == ProjectType .APPLICATION ) {
246+ androidDsl.productFlavors.find { flavor ->
247+ " ${flavor.name}${buildType.name.capitalizeString()} " == variantName
248+ }?.applicationId
249+ } else {
250+ androidDsl.defaultConfig.applicationId
251+ }
252+
253+ return if (appIdFromFlavor == null ) {
254+ // No appId value set from DSL; use the namespace value from the DSL.
255+ " ${androidProject.namespace}${computeApplicationIdSuffix(variantName, buildType)} "
256+ } else {
257+ // use value from flavors/defaultConfig
258+ // needed to make nullability work in kotlinc
259+ val finalAppIdFromFlavors: String = appIdFromFlavor
260+ " $finalAppIdFromFlavors${computeApplicationIdSuffix(variantName, buildType)} "
261+ }
262+ }
263+
264+ /* *
265+ * Combines all the appId suffixes into a single one.
266+ *
267+ * The suffixes are separated by '.' whether their first char is a '.' or not.
268+ */
269+ protected fun computeApplicationIdSuffix (variantName : String , buildType : BuildType ): String {
270+ // for the suffix we combine the suffix from all the flavors. However, we're going to
271+ // want the higher priority one to be last.
272+ val suffixes = mutableListOf<String >()
273+ androidDsl.defaultConfig.applicationIdSuffix?.let {
274+ suffixes.add(it)
275+ }
276+
277+ if (basicAndroidProject.projectType == ProjectType .APPLICATION ) {
278+
279+ val flavorSuffix = androidDsl.productFlavors.find { flavor ->
280+ " ${flavor.name}${buildType.name.capitalizeString()} " == variantName
281+ }?.applicationIdSuffix
282+
283+ flavorSuffix?.also { suffixes.add(flavorSuffix) }
284+
285+ // then we add the build type after.
286+ buildType.applicationIdSuffix?.also {
287+ suffixes.add(it)
288+ }
289+ }
290+
291+ val nonEmptySuffixes = suffixes.filter { it.isNotEmpty() }
292+ return if (nonEmptySuffixes.isNotEmpty()) {
293+ " .${nonEmptySuffixes.joinToString(separator = " ." , transform = { it.removePrefix(" ." ) })} "
294+ } else {
295+ " "
296+ }
297+ }
298+ }
0 commit comments