diff --git a/app/build.gradle b/app/build.gradle index 276ca88e9..47239dc96 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,6 +14,12 @@ android { multiDexEnabled true vectorDrawables.useSupportLibrary = true + + javaCompileOptions { + annotationProcessorOptions { + arguments = [ eventBusIndex : 'com.itsaky.androidide.events.AndroidIDEEventBusIndex' ] + } + } } compileOptions { @@ -87,6 +93,7 @@ dependencies { // Annotation processors annotationProcessor common.glide_ap + annotationProcessor common.eventbus_ap implementation common.editor implementation common.utilcode diff --git a/app/src/main/java/com/itsaky/androidide/EditorActivity.java b/app/src/main/java/com/itsaky/androidide/EditorActivity.java index 1053d7559..103aef579 100755 --- a/app/src/main/java/com/itsaky/androidide/EditorActivity.java +++ b/app/src/main/java/com/itsaky/androidide/EditorActivity.java @@ -87,7 +87,6 @@ import com.itsaky.androidide.fragments.SimpleOutputFragment; import com.itsaky.androidide.fragments.sheets.ProgressSheet; import com.itsaky.androidide.fragments.sheets.TextSheetFragment; import com.itsaky.androidide.handlers.EditorEventListener; -import com.itsaky.androidide.handlers.FileTreeActionHandler; import com.itsaky.androidide.interfaces.DiagnosticClickListener; import com.itsaky.androidide.interfaces.EditorActivityProvider; import com.itsaky.androidide.lsp.IDELanguageClientImpl; @@ -102,7 +101,6 @@ import com.itsaky.androidide.models.LogLine; import com.itsaky.androidide.models.Range; import com.itsaky.androidide.models.SaveResult; import com.itsaky.androidide.models.SearchResult; -import com.itsaky.androidide.projects.FileManager; import com.itsaky.androidide.projects.ProjectManager; import com.itsaky.androidide.projects.api.Project; import com.itsaky.androidide.services.GradleBuildService; @@ -110,12 +108,10 @@ import com.itsaky.androidide.services.LogReceiver; import com.itsaky.androidide.shell.ShellServer; import com.itsaky.androidide.tooling.api.messages.result.SimpleVariantData; import com.itsaky.androidide.tooling.api.messages.result.TaskExecutionResult; -import com.itsaky.androidide.utils.BootClasspathProvider; import com.itsaky.androidide.utils.CharSequenceInputStream; import com.itsaky.androidide.utils.DialogUtils; import com.itsaky.androidide.utils.EditorActivityActions; import com.itsaky.androidide.utils.EditorBottomSheetBehavior; -import com.itsaky.androidide.utils.Environment; import com.itsaky.androidide.utils.ILogger; import com.itsaky.androidide.utils.LSPUtils; import com.itsaky.androidide.utils.RecursiveFileSearcher; @@ -164,7 +160,6 @@ public class EditorActivity extends StudioActivity private static final int ACTION_ID_ALL = 102; private static final ILogger LOG = ILogger.newInstance("EditorActivity"); private final EditorEventListener mBuildEventListener = new EditorEventListener(); - private final FileTreeActionHandler mFileActionsHandler = new FileTreeActionHandler(); private ActivityEditorBinding mBinding; private LayoutDiagnosticInfoBinding mDiagnosticInfoBinding; private EditorBottomSheetTabAdapter bottomSheetTabAdapter; @@ -1127,27 +1122,6 @@ public class EditorActivity extends StudioActivity } } - @Override - protected void onStart() { - super.onStart(); - - // Make sure we list and store the bootstrap classes - CompletableFuture.runAsync( - () -> - BootClasspathProvider.update( - Collections.singleton(Environment.ANDROID_JAR.getAbsolutePath()))); - - mFileActionsHandler.register(); - FileManager.INSTANCE.register(); - } - - @Override - protected void onStop() { - super.onStop(); - mFileActionsHandler.unregister(); - FileManager.INSTANCE.unregister(); - } - @Override protected void onDestroy() { closeProject(false); diff --git a/app/src/main/java/com/itsaky/androidide/app/StudioApp.java b/app/src/main/java/com/itsaky/androidide/app/StudioApp.java index 5d4ef0ca8..3293802fa 100755 --- a/app/src/main/java/com/itsaky/androidide/app/StudioApp.java +++ b/app/src/main/java/com/itsaky/androidide/app/StudioApp.java @@ -24,6 +24,7 @@ import androidx.annotation.NonNull; import com.blankj.utilcode.util.ThrowableUtils; import com.itsaky.androidide.CrashHandlerActivity; import com.itsaky.androidide.R; +import com.itsaky.androidide.eventbus.events.Event; import com.itsaky.androidide.projects.ProjectResourceTable; import com.itsaky.androidide.utils.ILogger; import com.itsaky.apiinfo.ApiInfo; @@ -38,6 +39,8 @@ import com.itsaky.androidide.lsp.xml.XMLLanguageServer; import com.itsaky.sdk.SDKInfo; import com.itsaky.widgets.WidgetInfo; +import org.greenrobot.eventbus.EventBus; + import java.io.File; import java.util.Objects; import java.util.Set; @@ -62,6 +65,7 @@ public class StudioApp extends BaseApplication { this.uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(this::handleCrash); super.onCreate(); + getApiInformation(); } diff --git a/app/src/main/java/com/itsaky/androidide/handlers/EditorActivityLifecyclerObserver.kt b/app/src/main/java/com/itsaky/androidide/handlers/EditorActivityLifecyclerObserver.kt new file mode 100644 index 000000000..87035f88b --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/handlers/EditorActivityLifecyclerObserver.kt @@ -0,0 +1,83 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.handlers + +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.LifecycleOwner +import com.itsaky.androidide.EditorActivity +import com.itsaky.androidide.eventbus.events.Event +import com.itsaky.androidide.eventbus.events.EventReceiver +import com.itsaky.androidide.eventbus.events.editor.OnCreateEvent +import com.itsaky.androidide.eventbus.events.editor.OnDestroyEvent +import com.itsaky.androidide.eventbus.events.editor.OnPauseEvent +import com.itsaky.androidide.eventbus.events.editor.OnResumeEvent +import com.itsaky.androidide.eventbus.events.editor.OnStartEvent +import com.itsaky.androidide.eventbus.events.editor.OnStopEvent +import com.itsaky.androidide.projects.FileManager +import com.itsaky.androidide.projects.ProjectManager +import com.itsaky.androidide.utils.BootClasspathProvider +import java.util.concurrent.* + +/** + * Observes lifecycle events if [com.itsaky.androidide.EditorActivity]. + * + * @author Akash Yadav + */ +class EditorActivityLifecyclerObserver : DefaultLifecycleObserver { + + private val fileActionsHandler = FileTreeActionHandler() + + override fun onCreate(owner: LifecycleOwner) { + dispatchEvent(OnCreateEvent()) + } + + override fun onStart(owner: LifecycleOwner) { + CompletableFuture.runAsync(BootClasspathProvider::init) + register(fileActionsHandler, FileManager, ProjectManager) + + dispatchEvent(OnStartEvent()) + } + + override fun onResume(owner: LifecycleOwner) { + dispatchEvent(OnResumeEvent()) + } + + override fun onPause(owner: LifecycleOwner) { + dispatchEvent(OnPauseEvent()) + } + + override fun onStop(owner: LifecycleOwner) { + unregister(fileActionsHandler, FileManager, ProjectManager) + + dispatchEvent(OnStopEvent()) + } + + override fun onDestroy(owner: LifecycleOwner) { + dispatchEvent(OnDestroyEvent()) + } + + private fun register(vararg receivers: EventReceiver) { + receivers.forEach { it.register() } + } + + private fun unregister(vararg receivers: EventReceiver) { + receivers.forEach { it.unregister() } + } + + private fun dispatchEvent(event: Event) {} +} diff --git a/build-tools/eventbus-events/src/main/java/com/itsaky/androidide/eventbus/events/editor/ActivityLifecycleEvents.kt b/build-tools/eventbus-events/src/main/java/com/itsaky/androidide/eventbus/events/editor/ActivityLifecycleEvents.kt new file mode 100644 index 000000000..f1e57a8a8 --- /dev/null +++ b/build-tools/eventbus-events/src/main/java/com/itsaky/androidide/eventbus/events/editor/ActivityLifecycleEvents.kt @@ -0,0 +1,64 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.eventbus.events.editor + +import com.itsaky.androidide.eventbus.events.Event + +abstract class EditorActivityLifecycleEvent : Event() + +/** + * Dispatched just after `EditorActivity`'s `onCreate` method. + * + * @author Akash Yadav + */ +class OnCreateEvent : EditorActivityLifecycleEvent() + +/** + * Dispatched just after `EditorActivity`'s `onStart` method. + * + * @author Akash Yadav + */ +class OnStartEvent : EditorActivityLifecycleEvent() + +/** + * Dispatched just after `EditorActivity`'s `onResume` method. + * + * @author Akash Yadav + */ +class OnResumeEvent : EditorActivityLifecycleEvent() + +/** + * Dispatched just before `EditorActivity`'s `onPause` method. + * + * @author Akash Yadav + */ +class OnPauseEvent : EditorActivityLifecycleEvent() + +/** + * Dispatched just before `EditorActivity`'s `onStop` method. + * + * @author Akash Yadav + */ +class OnStopEvent : EditorActivityLifecycleEvent() + +/** + * Dispatched just before `EditorActivity`'s `onDestroy` method. + * + * @author Akash Yadav + */ +class OnDestroyEvent : EditorActivityLifecycleEvent() diff --git a/build-tools/eventbus/src/main/java/org/greenrobot/eventbus/meta/SubscriberInfoIndex.java b/build-tools/eventbus/src/main/java/org/greenrobot/eventbus/meta/SubscriberInfoIndex.java index 9fc65f6ff..02da55336 100644 --- a/build-tools/eventbus/src/main/java/org/greenrobot/eventbus/meta/SubscriberInfoIndex.java +++ b/build-tools/eventbus/src/main/java/org/greenrobot/eventbus/meta/SubscriberInfoIndex.java @@ -15,9 +15,7 @@ */ package org.greenrobot.eventbus.meta; -/** - * Interface for generated indexes. - */ +/** Interface for generated indexes. */ public interface SubscriberInfoIndex { - SubscriberInfo getSubscriberInfo(Class subscriberClass); + SubscriberInfo getSubscriberInfo(Class subscriberClass); } diff --git a/build.gradle b/build.gradle index ce715a3ee..c2a240406 100755 --- a/build.gradle +++ b/build.gradle @@ -47,60 +47,69 @@ subprojects { project.afterEvaluate { tasks.register ("checkTranslations", task -> { - def php = new File ("/usr/bin/php") - if (!php.exists ()) { - project.logger.lifecycle ("'${php.absolutePath}' not found. Skipping translation check.") - return - } - - def resDir = project.file ("src/main/res") - def strings = new File (resDir, "values/strings.xml") - def reportDir = new File (project.rootProject.buildDir, "translation-reports") - reportDir.delete () - - if (resDir.exists () && strings.exists ()) { - def translationDirs = resDir.listFiles ((FileFilter) (file -> { - return file.isDirectory () && file.getName ().startsWith ("values-") - })) - for (def dir : translationDirs) { - final var translation = new File (dir, "strings.xml") - if (translation.exists ()) { - def out = new File (reportDir, "${project.path.replace (':', '/')}/${dir.name}.txt") - if (!out.parentFile.exists ()) { - out.parentFile.mkdirs () - } - - if (out.exists ()) { - out.delete () - } - out.createNewFile () - - def result = exec { - ignoreExitValue true - standardOutput new FileOutputStream (out) - commandLine "${php.absolutePath}", - "${project.rootProject.file (".tools/strings-check.php")}", - "${strings.absolutePath}", - "${translation.absolutePath}" - } - - if (result.getExitValue () == 0) { - out.delete () - } else { - project.logger.lifecycle ("Translation report for '${project.path}/${dir.name}' is written to '${out.absolutePath}'") - } - - } else { - project.logger.info ("No translation file specifed for '${dir.name}'. Skipping..") - } - } - } else { - project.logger.info ("Default strings.xml file does not exist for project '${project.name}'") + doLast { + checkTranslations (project) } }) } } +private void checkTranslations (def project) { + def php = new File ("/usr/bin/php") + if (!php.exists ()) { + project.logger.lifecycle ("'${php.absolutePath}' not found. Skipping translation check.") + return + } + + def resDir = project.file ("src/main/res") + def strings = new File (resDir, "values/strings.xml") + def reportDir = new File (project.rootProject.buildDir, "translation-reports") + reportDir.delete () + + if (!resDir.exists () || !strings.exists ()) { + project.logger.info ("Default strings.xml file does not exist for project '${project.name}'") + return + } + + def translationDirs = resDir.listFiles ((FileFilter) (file -> { + return file.isDirectory () && file.getName ().startsWith ("values-") + })) + + for (def dir : translationDirs) { + final var translation = new File (dir, "strings.xml") + if (!translation.exists ()) { + project.logger.info ("No translation file specifed for '${dir.name}'. Skipping..") + return + } + + def out = new File (reportDir, "${project.path.replace (':', '/')}/${dir.name}.txt") + if (!out.parentFile.exists ()) { + out.parentFile.mkdirs () + } + + if (out.exists ()) { + out.delete () + } + + out.createNewFile () + + def result = exec { + ignoreExitValue true + standardOutput new FileOutputStream (out) + commandLine "${php.absolutePath}", + "${project.rootProject.file (".tools/strings-check.php")}", + "${strings.absolutePath}", + "${translation.absolutePath}" + } + + if (result.getExitValue () == 0) { + out.delete () + } else { + project.logger.lifecycle ("Translation report for '${project.path}/${dir.name}' is written to '${out.absolutePath}'") + } + } +} + allprojects { tasks.withType (org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { diff --git a/common/src/main/java/com/itsaky/androidide/utils/BootClasspathProvider.java b/common/src/main/java/com/itsaky/androidide/utils/BootClasspathProvider.java index 76a5b5487..9e0c57bd3 100644 --- a/common/src/main/java/com/itsaky/androidide/utils/BootClasspathProvider.java +++ b/common/src/main/java/com/itsaky/androidide/utils/BootClasspathProvider.java @@ -35,6 +35,11 @@ public class BootClasspathProvider { private static final Map bootClasspathClasses = new ConcurrentHashMap<>(); private static final ILogger LOG = ILogger.newInstance("BootClassProvider"); + /** Initializes the classpath provider with the default boot classpath. */ + public static synchronized void init() { + update(Collections.singleton(Environment.ANDROID_JAR.getAbsolutePath())); + } + /** * Updates the boot classpath cache. If a classpath is already indexed, it is skipped. * diff --git a/dependencies.gradle b/dependencies.gradle index dba289885..a0790b856 100755 --- a/dependencies.gradle +++ b/dependencies.gradle @@ -23,7 +23,8 @@ ext { lang3 : 'org.apache.commons:commons-lang3:3.12.0', io : 'commons-io:commons-io:2.11.0', kotlin : 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.7.0', - jkotlin : 'org.jetbrains.kotlin:kotlin-stdlib-jdk8' + jkotlin : 'org.jetbrains.kotlin:kotlin-stdlib-jdk8', + eventbus_ap: 'org.greenrobot:eventbus-annotation-processor:3.3.1' ] androidx = [ diff --git a/tests/test-home/.androidide/init/model.jar b/tests/test-home/.androidide/init/model.jar index 4dc5cb38b..03b2b7f3b 100644 Binary files a/tests/test-home/.androidide/init/model.jar and b/tests/test-home/.androidide/init/model.jar differ