diff --git a/gradle.properties b/gradle.properties index 8dc2f9fdd2..011d320a67 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,7 +6,7 @@ ideType=IC # In order to run Android Studion instead of Intellij Community, # specify the path to your Android Studio installation -//androidStudioPath=D:/AS2021 +//androidStudioPath=your_path_to_android_studio pythonCommunityPluginVersion=222.4167.37 #Version numbers: https://plugins.jetbrains.com/plugin/631-python/versions diff --git a/settings.gradle b/settings.gradle index 7e7cd525dc..7375c1636b 100644 --- a/settings.gradle +++ b/settings.gradle @@ -30,4 +30,5 @@ include 'utbot-maven' include 'utbot-summary-tests' include 'utbot-framework-test' include 'utbot-rd' +include 'utbot-android-studio' diff --git a/utbot-android-studio/build.gradle.kts b/utbot-android-studio/build.gradle.kts new file mode 100644 index 0000000000..13cefccbba --- /dev/null +++ b/utbot-android-studio/build.gradle.kts @@ -0,0 +1,46 @@ +plugins { + id("org.jetbrains.intellij") version "1.7.0" +} + +intellij { + /* + The list of Android Studio releases can be found here https://plugins.jetbrains.com/docs/intellij/android-studio-releases-list.html + For each release a compatible Intellij Idea version can be found in the right column. Specify it in "version.set("...") + + NOTE!!! + We use Android Studio Chipmunk (2021.2.1), although Android Studio Dolphin (2021.3.1) has been released. + The reason is that a version of Kotlin plugin compatible with Android Studio is required. + The list of Kotlin plugin releases can be found here https://plugins.jetbrains.com/plugin/6954-kotlin/versions/stable + The last compatible with AS plugin version on 19 Oct 2022 is Kotlin 212-1.7.10-release-333-AS5457.46, + it is not compatible with Dolphin release (https://plugins.jetbrains.com/plugin/6954-kotlin/versions/stable/193255). + */ + + val androidPlugins = listOf("org.jetbrains.android") + + val jvmPlugins = listOf( + "java", + "org.jetbrains.kotlin:212-1.7.10-release-333-AS5457.46" + ) + + plugins.set(jvmPlugins + androidPlugins) + + version.set("212.5712.43") + type.set("IC") +} + +project.tasks.asMap["runIde"]?.enabled = false + +tasks { + compileKotlin { + kotlinOptions { + jvmTarget = "11" + freeCompilerArgs = freeCompilerArgs + listOf("-Xallow-result-return-type", "-Xsam-conversions=class") + allWarningsAsErrors = false + } + } + + java { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } +} \ No newline at end of file diff --git a/utbot-android-studio/src/main/kotlin/org/androidstudio/plugin/util/UtAndroidGradleJavaProjectModelModifier.kt b/utbot-android-studio/src/main/kotlin/org/androidstudio/plugin/util/UtAndroidGradleJavaProjectModelModifier.kt new file mode 100644 index 0000000000..6ff259f3cb --- /dev/null +++ b/utbot-android-studio/src/main/kotlin/org/androidstudio/plugin/util/UtAndroidGradleJavaProjectModelModifier.kt @@ -0,0 +1,121 @@ +package org.androidstudio.plugin.util + +import com.android.tools.idea.gradle.AndroidGradleJavaProjectModelModifier +import com.android.tools.idea.gradle.dsl.api.GradleBuildModel +import com.android.tools.idea.gradle.dsl.api.dependencies.ArtifactDependencySpec +import com.android.tools.idea.gradle.dsl.api.dependencies.CommonConfigurationNames +import com.android.tools.idea.gradle.project.sync.GradleSyncInvoker +import com.android.tools.idea.gradle.project.sync.GradleSyncListener +import com.android.tools.idea.gradle.project.sync.idea.GradleSyncExecutor +import com.android.tools.idea.gradle.util.GradleUtil +import com.android.tools.idea.project.AndroidProjectInfo +import com.android.tools.idea.projectsystem.TestArtifactSearchScopes +import com.google.wireless.android.sdk.stats.GradleSyncStats +import com.intellij.openapi.command.WriteCommandAction +import com.intellij.openapi.command.undo.BasicUndoableAction +import com.intellij.openapi.command.undo.UndoManager +import com.intellij.openapi.externalSystem.ExternalSystemModulePropertyManager +import com.intellij.openapi.fileEditor.ex.FileEditorManagerEx +import com.intellij.openapi.module.Module +import com.intellij.openapi.project.Project +import com.intellij.openapi.roots.DependencyScope +import com.intellij.openapi.roots.ExternalLibraryDescriptor +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.util.containers.ContainerUtil +import org.jetbrains.concurrency.AsyncPromise +import org.jetbrains.concurrency.Promise + +class UtAndroidGradleJavaProjectModelModifier : AndroidGradleJavaProjectModelModifier() { + override fun addExternalLibraryDependency( + modules: Collection, + descriptor: ExternalLibraryDescriptor, + scope: DependencyScope + ): Promise? { + val module = ContainerUtil.getFirstItem(modules) ?: return null + + if (!isAndroidGradleProject(module.project)) { + return null + } + + val dependencySpec = ArtifactDependencySpec.create(descriptor.libraryArtifactId, descriptor.libraryGroupId, descriptor.preferredVersion) + return addExternalLibraryDependency(module, dependencySpec, scope) + + } + + private fun addExternalLibraryDependency( + module: Module, + dependencySpec: ArtifactDependencySpec, + scope: DependencyScope, + ): Promise? { + val project = module.project + val openedFile = FileEditorManagerEx.getInstanceEx(project).currentFile + val buildModelsToUpdate: MutableList = ArrayList() + + val buildModel = GradleBuildModel.get(module) ?: return null + val configurationName = getConfigurationName(module, scope, openedFile) + val dependencies = buildModel.dependencies() + dependencies.addArtifact(configurationName, dependencySpec) + buildModelsToUpdate.add(buildModel) + + WriteCommandAction.writeCommandAction(project).withName("Add Gradle Library Dependency").run { + buildModelsToUpdate.forEach { buildModel -> buildModel.applyChanges() } + registerUndoAction(project) + } + + return doAndroidGradleSync(project, GradleSyncStats.Trigger.TRIGGER_MODIFIER_ADD_LIBRARY_DEPENDENCY) + } + + private fun getConfigurationName(module: Module, scope: DependencyScope, openedFile: VirtualFile?): String = + GradleUtil.mapConfigurationName( + getLegacyConfigurationName(module, scope, openedFile), + GradleUtil.getAndroidGradleModelVersionInUse(module), + false + ) + + private fun getLegacyConfigurationName( + module: Module, + scope: DependencyScope, + openedFile: VirtualFile? + ): String { + if (!scope.isForProductionCompile) { + val testScopes = TestArtifactSearchScopes.getInstance(module) + if (testScopes != null && openedFile != null) { + return if (testScopes.isAndroidTestSource(openedFile)) CommonConfigurationNames.ANDROID_TEST_COMPILE else CommonConfigurationNames.TEST_COMPILE + } + } + return CommonConfigurationNames.COMPILE + } + + private fun registerUndoAction(project: Project) { + UndoManager.getInstance(project).undoableActionPerformed(object : BasicUndoableAction() { + + override fun undo() { + doAndroidGradleSync(project, GradleSyncStats.Trigger.TRIGGER_MODIFIER_ACTION_UNDONE) + + } + + override fun redo() { + doAndroidGradleSync(project, GradleSyncStats.Trigger.TRIGGER_MODIFIER_ACTION_REDONE) + } + }) + } + + private fun isAndroidGradleProject(project: Project): Boolean = AndroidProjectInfo.getInstance(project).requiresAndroidModel() + + private fun doAndroidGradleSync(project: Project, trigger: GradleSyncStats.Trigger): AsyncPromise { + val promise = AsyncPromise() + val request = GradleSyncInvoker.Request(trigger) + val listener = object : GradleSyncListener { + override fun syncSucceeded(project: Project) { + promise.setResult(null) + } + + override fun syncFailed(project: Project, errorMessage: String) { + promise.setError(errorMessage) + } + } + GradleSyncExecutor(project).sync(request, listener) + + return promise + } +} \ No newline at end of file diff --git a/utbot-intellij/build.gradle.kts b/utbot-intellij/build.gradle.kts index 16dd9156ef..b8388b9c74 100644 --- a/utbot-intellij/build.gradle.kts +++ b/utbot-intellij/build.gradle.kts @@ -96,4 +96,6 @@ dependencies { //api(project(":utbot-analytics")) testImplementation("org.mock-server:mockserver-netty:5.4.1") testApi(project(":utbot-framework")) + + implementation(project(":utbot-android-studio")) } \ No newline at end of file diff --git a/utbot-intellij/src/main/resources/META-INF/plugin.xml b/utbot-intellij/src/main/resources/META-INF/plugin.xml index a809967b5b..edf01fe9c0 100644 --- a/utbot-intellij/src/main/resources/META-INF/plugin.xml +++ b/utbot-intellij/src/main/resources/META-INF/plugin.xml @@ -32,6 +32,7 @@ +