diff --git a/.github/workflows/build-plugin.yml b/.github/workflows/build-plugin.yml index f80c0a8..166fd08 100644 --- a/.github/workflows/build-plugin.yml +++ b/.github/workflows/build-plugin.yml @@ -25,7 +25,11 @@ jobs: run: chmod +x ./gradlew - name: Build plugin - run: ./gradlew buildPlugin + run: | + ./gradlew :plugin:buildPlugin \ + -x :app:test \ + -x :app:compileTestKotlin \ + --no-daemon - name: Get version from gradle.properties id: get_version @@ -39,7 +43,7 @@ jobs: - name: Create GitHub Release id: create_release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -47,9 +51,10 @@ jobs: name: Release v${{ steps.get_version.outputs.version }} draft: false prerelease: false + generate_release_notes: true - name: Upload plugin artifact to release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: files: plugin/build/distributions/yamp_plugin_${{ steps.get_version.outputs.version }}.zip env: diff --git a/.github/workflows/checks.yaml b/.github/workflows/checks.yaml index fc689a2..fa9a937 100644 --- a/.github/workflows/checks.yaml +++ b/.github/workflows/checks.yaml @@ -11,10 +11,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v4 with: - java-version: "17" + java-version: "21" distribution: "temurin" - name: Validate Gradle wrapper diff --git a/.github/workflows/pr_master.yaml b/.github/workflows/pr_master.yaml index 227c1dd..52cdcf3 100644 --- a/.github/workflows/pr_master.yaml +++ b/.github/workflows/pr_master.yaml @@ -8,34 +8,35 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Set up JDK 17 - uses: actions/setup-java@v3 + - uses: actions/checkout@v4 + + - name: Set up JDK 21 + uses: actions/setup-java@v4 with: - java-version: '17' - distribution: 'temurin' + java-version: "21" + distribution: "temurin" - name: Validate Gradle wrapper - uses: gradle/wrapper-validation-action@e6e38bacfdf1a337459f332974bb2327a31aaf4b + uses: gradle/actions/wrapper-validation@v4 - name: Build with Gradle - uses: gradle/gradle-build-action@67421db6bd0bf253fb4bd25b31ebb98943c375e1 + uses: gradle/actions/setup-gradle@v4 - with: - arguments: fatJar + - name: Run fatJar + run: ./gradlew fatJar - - name : Retrieve Version - run: | - echo "::set-output name=VERSION_NAME::$(${{github.workspace}}/gradlew -q printVersionName)" + - name: Retrieve Version id: yamp_version + run: | + VERSION=$(./gradlew -q printVersionName) + echo "VERSION_NAME=$VERSION" >> "$GITHUB_OUTPUT" - name: Get version - run: - echo "version_name=${{steps.yamp_version.outputs.VERSION_NAME}}" >> $GITHUB_ENV + run: echo "version_name=${{ steps.yamp_version.outputs.VERSION_NAME }}" >> $GITHUB_ENV - - run: echo ${{env.version_name}} + - run: echo ${{ env.version_name }} - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: - name: yamp-${{env.version_name}} + name: yamp-${{ env.version_name }} path: app/build/libs diff --git a/app/build.gradle b/app/build.gradle index ab0e2f9..8069cba 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -5,6 +5,12 @@ plugins { id 'org.jetbrains.kotlin.jvm' } +kotlin { + jvmToolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} + def properties(String key) { return project.findProperty(key).toString() } @@ -12,7 +18,14 @@ def properties(String key) { group = properties("pluginGroup") version = properties("yampVersion") -sourceCompatibility = 11 +sourceCompatibility = 17 +targetCompatibility = 17 + +java { + toolchain { + languageVersion.set(JavaLanguageVersion.of(17)) + } +} java.sourceSets.main.java { if (OperatingSystem.current().isMacOsX()) { @@ -46,12 +59,12 @@ dependencies { testImplementation "junit:junit:4.12" testImplementation 'org.mockito:mockito-core:2.23.0' testImplementation "com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0" - testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.2" + testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3" + testImplementation "org.jetbrains.kotlin:kotlin-test-junit:1.9.22" } compileJava { options.compilerArgs << "-XDignore.symbol.file=true" options.fork = true - options.forkOptions.executable = 'javac' } task fatJar(type: Jar) { @@ -86,7 +99,10 @@ if (OperatingSystem.current().isLinux()) { task buildInstaller(type: Exec) { dependsOn fatJar workingDir '.' - println("Current WD: " + workingDir) commandLine "./${installerScript}" args version + + doFirst { + println("Current WD: " + workingDir) + } } diff --git a/gradle.properties b/gradle.properties index 81c9716..b228c33 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,7 @@ kotlin.code.style=official +org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError + studioCompilePath=/Applications/Android Studio.app/Contents #studioCompilePath=/Applications/Android Studio Preview.app/Contents @@ -8,7 +10,7 @@ studioCompilePath=/Applications/Android Studio.app/Contents pluginGroup = com.github.grishberg pluginName = android-methods-profiler -yampVersion = 25.08.29 +yampVersion = 26.04.05 # See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html # for insight into build numbers and IntelliJ Platform versions. @@ -17,6 +19,7 @@ sinceBuild=243.22562.145 # IntelliJ Platform Properties -> https://github.com/JetBrains/gradle-intellij-plugin#intellij-platform-properties platformType = AI +#localASVersion=/Applications/Android Studio.app/Contents #TODO: set to 2025.1.1.2 platformVersion=2024.3.1.7 androidPluginVersion=243.22562.218 diff --git a/plugin/src/main/kotlin/com/github/grishberg/profiler/androidstudio/PluginProjectInfo.kt b/plugin/src/main/kotlin/com/github/grishberg/profiler/androidstudio/PluginProjectInfo.kt index 5f1fd9a..4376c7c 100644 --- a/plugin/src/main/kotlin/com/github/grishberg/profiler/androidstudio/PluginProjectInfo.kt +++ b/plugin/src/main/kotlin/com/github/grishberg/profiler/androidstudio/PluginProjectInfo.kt @@ -1,18 +1,18 @@ package com.github.grishberg.profiler.androidstudio -import com.android.ddmlib.IDevice import com.android.tools.idea.model.AndroidModel -import com.android.tools.idea.run.activity.ActivityLocator -import com.android.tools.idea.run.activity.DefaultActivityLocator import com.github.grishberg.profiler.common.AppLogger -import com.intellij.facet.FacetManager -import com.intellij.openapi.module.ModuleManager import com.github.grishberg.profiler.ui.dialogs.recorder.ProjectInfo +import com.intellij.facet.FacetManager import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.module.ModuleManager import com.intellij.openapi.project.Project -import com.intellij.openapi.util.ThrowableComputable +import java.io.File +import javax.xml.parsers.DocumentBuilderFactory import org.jetbrains.android.facet.AndroidFacet -import org.jetbrains.android.sdk.AndroidSdkUtils +import org.w3c.dom.Document +import org.w3c.dom.Element +import org.w3c.dom.NodeList class PluginProjectInfo( project: Project, private val logger: AppLogger @@ -22,16 +22,15 @@ class PluginProjectInfo( override val activityName: String? init { - val facets = try{ + val facets = try { getAndroidFacets(project) } catch (e: Exception) { logger.e("getAndroidFacets error", e) emptyList() } packageName = createPackageName(facets) - val devices = AndroidSdkUtils.getDebugBridge(project)?.devices ?: emptyArray() activityName = try { - getDefaultActivityName(facets, devices) + getDefaultActivityName(facets) } catch (e: Exception) { logger.e("PluginProjectInfo: Error while fetching getDefaultActivityName", e) null @@ -62,15 +61,98 @@ class PluginProjectInfo( return null } - @Throws(ActivityLocator.ActivityLocatorException::class) - private fun getDefaultActivityName(facets: List, devices: Array): String? { + @Throws(Exception::class) + private fun getDefaultActivityName(facets: List): String? { val facet = facets.firstOrNull() ?: return null - val device = devices.firstOrNull() ?: return null - return ApplicationManager.getApplication() - .runReadAction(ThrowableComputable { - DefaultActivityLocator( - facet - ).getQualifiedActivityName(device) - }) + return ApplicationManager.getApplication().runReadAction { + try { + val manifestFile = findManifestFileReflection(facet) + if (manifestFile == null || !manifestFile.exists()) { + logger.d("Manifest file not found") + return@runReadAction null + } + val factory = DocumentBuilderFactory.newInstance() + val builder = factory.newDocumentBuilder() + val document: Document = builder.parse(manifestFile) + val activities: NodeList = document.getElementsByTagName("activity") + for (i in 0 until activities.length) { + val activity = activities.item(i) as Element + val intentFilters = activity.getElementsByTagName("intent-filter") + for (j in 0 until intentFilters.length) { + val intentFilter = intentFilters.item(j) as Element + val actions = intentFilter.getElementsByTagName("action") + val categories = intentFilter.getElementsByTagName("category") + var hasMainAction = false + var hasLauncherCategory = false + for (k in 0 until actions.length) { + val actionNode = actions.item(k) as Element + val actionName = actionNode.getAttribute("android:name") + if (actionName == "android.intent.action.MAIN") { + hasMainAction = true + } + } + for (k in 0 until categories.length) { + val categoryNode = categories.item(k) as Element + val categoryName = categoryNode.getAttribute("android:name") + if (categoryName == "android.intent.category.LAUNCHER") { + hasLauncherCategory = true + } + } + if (hasMainAction && hasLauncherCategory) { + val activityName = activity.getAttribute("android:name") + if (activityName.isNotEmpty()) { + logger.d("Found default activity: $activityName") + return@runReadAction activityName + } + } + } + } + logger.d("No default activity found in manifest") + null + } catch (e: Throwable) { + logger.e("Error while parsing manifest", e) + null + } + } + } + + private fun findManifestFileReflection(facet: AndroidFacet): File? { + try { + val sourceProviderClass = Class.forName("org.jetbrains.android.facet.AndroidFacet\$SourceProvider") + val sourceProviderGetter = sourceProviderClass.getMethod("getSourceProvider") + val sourceProvider = sourceProviderGetter.invoke(facet) + if (sourceProvider != null) { + val allSourceFilesMethod = sourceProvider.javaClass.getMethod("getAllSourceFiles") + + @Suppress("UNCHECKED_CAST") val allSourceFiles = + allSourceFilesMethod.invoke(sourceProvider) as List + for (file in allSourceFiles) { + if (file.name == "AndroidManifest.xml") { + logger.d("Found manifest via sourceProvider reflection") + return file + } + } + } + } catch (e: Throwable) { + logger.d("sourceProvider reflection failed: ${e.message}") + } + + try { + val manifestClass = Class.forName("org.jetbrains.android.dom.manifest.Manifest") + val manifestGetter = AndroidFacet::class.java.getMethod("getManifest") + val manifest = manifestGetter.invoke(facet) + if (manifest != null) { + val xmlFileGetter = manifestClass.getMethod("getXmlFile") + val xmlFile = xmlFileGetter.invoke(manifest) as? File + if (xmlFile != null) { + logger.d("Found manifest via manifest.xmlFile reflection") + return xmlFile + } + } + } catch (e: Throwable) { + logger.d("manifest.xmlFile reflection failed: ${e.message}") + } + + return null } }