Search code examples
androidkotlinandroid-gradle-pluginandroid-studio-plugin

How to add dynamically dependency in project from custom plugin's action in android studio?


I need to create a custom plugin for Android studio and with action, I need to add the dependency in build.gradle file.

With AnAction, I have overridden the actionPerformed method.

Now I need to add the dependency dynamically, after performing an action of that then how can I add that?


Solution

  • I have got the solution and applied the same,

    class GradleManager(private val project: Project) {
    
        companion object {
            const val DEFAULT_MODULE_NAME = "app"
            const val IMPLEMENTATION = "implementation"
            const val COMPILE = "compile"
            const val DEPENDENCIES = "dependencies"
        }
    
        private var buildGradle: Document? = null
        private var modules = arrayOf<Any>()
        private var projectBaseDir: VirtualFile? = null
    
        @Throws(FileNotFoundException::class)
        fun initBuildGradle(): Boolean {
            getModulesExist()
            val gradleVirtualFile: VirtualFile?
            gradleVirtualFile = if (modules.size > 1) {
                val isHaveAppModule: String? = modules.find { it == DEFAULT_MODULE_NAME } as String
                if (isHaveAppModule != null && isHaveAppModule != "") {
                    projectBaseDir!!
                            .findChild(isHaveAppModule)!!
                            .findChild("build.gradle")
                } else {
                    return false
                }
            } else {
                projectBaseDir!!
                        .findChild(modules[0] as String)!!
                        .findChild("build.gradle")
            }
            if (gradleVirtualFile != null) {
                buildGradle = FileDocumentManager.getInstance().getDocument(gradleVirtualFile)
            }
            return true
        }
    
        @Throws(FileNotFoundException::class)
        private fun getModulesExist() {
            val basePath = project.basePath
            if (StringUtils.isEmpty(basePath)) {
                throw FileNotFoundException("Project base path not found.")
            }
            projectBaseDir = LocalFileSystem.getInstance().findFileByPath(basePath!!)
            if (projectBaseDir == null) {
                throw FileNotFoundException("Project base directory not found.")
            }
            val virtualSettingsGradle = projectBaseDir!!.findChild("settings.gradle")
            if (virtualSettingsGradle != null) {
                val settingsGradle = FileDocumentManager.getInstance().getDocument(virtualSettingsGradle)
                if (settingsGradle != null) {
                modules = readSettingsGradle(settingsGradle)
            }
            } else if (projectBaseDir!!.findChild("build.gradle") == null) {
                throw FileNotFoundException("Project doesn't contain any gradle file.")
            }
        }
    
        fun addDependency(repository: String, actionEvent: AnActionEvent) {
            val documentText = buildGradle!!.text.split("\n".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
            val sb = StringBuilder()
            var counter = 0
            var canSearch = false
            for (i in documentText.indices) {
                val line = documentText[i]
                if (canSearch) {
                    if (line.contains("{")) {
                        counter += 1
                    }
                    if (line.contains("}")) {
                        if (counter > 0) {
                            counter -= 1
                        } else {
                            canSearch = false
                            sb.append("\t$repository").append("\n")
                        }
                    }
                }
                if (line.contains(DEPENDENCIES)) {
                    val tempLine = line.replace(DEPENDENCIES, "")
                    if (tempLine.trim().equals("{", true)) {
                        canSearch = true
                    } else {
                        if (!tempLine.trim().isRequiredField()) {
                            counter = -1
                            canSearch = true
                        } else {
                            if (tempLine.trim().contains("{")
                                    && !tempLine.trim().contains("//")
                                    && (tempLine.trim().contains(IMPLEMENTATION) || tempLine.trim().contains(COMPILE))
                            ) {
                                canSearch = true
                            }
                        }
                    }
                }
                sb.append(line).append("\n")
            }
            writeToGradle(sb, actionEvent)
        }
    
        private fun writeToGradle(stringBuilder: StringBuilder, actionEvent: AnActionEvent) {
            val application = ApplicationManager.getApplication()
            application.invokeLater {
                application.runWriteAction { buildGradle!!.setText(stringBuilder) }
                syncProject(actionEvent)
            }
        }
    
        private fun syncProject(actionEvent: AnActionEvent) {
            val androidSyncAction = getAction("Android.SyncProject")
            val refreshAllProjectAction = getAction("ExternalSystem.RefreshAllProjects")
            if (androidSyncAction != null && androidSyncAction !is EmptyAction) {
                androidSyncAction.actionPerformed(actionEvent)
            } else if (refreshAllProjectAction != null && refreshAllProjectAction !is EmptyAction) {
                refreshAllProjectAction.actionPerformed(actionEvent)
            } else {
                SwingUtilities.invokeLater {
                    Messages.showInfoMessage(
                            "Project sync failed.",
                            "SYNC FAILED"
                    )
                }
            }
        }
    
        private fun getAction(actionId: String): AnAction? {
            return ActionManager.getInstance().getAction(actionId)
        }
    
        private fun readSettingsGradle(settingsGradle: Document): Array<Any> {
            return Stream.of(*settingsGradle.text.split("'".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray())
                    .filter { s -> s.startsWith(":") }
                    .map { s -> s.replace(":", "") }
                    .toArray()
        }
    
        private fun String?.isRequiredField(): Boolean {
            return this != null && isNotEmpty() && isNotBlank()
        }
    }
    

    Please refer here for the reference.