From fddefdfc8f83975500a0fb6cc06deaff30bce2c5 Mon Sep 17 00:00:00 2001 From: Akash Yadav Date: Mon, 11 Sep 2023 23:43:57 +0530 Subject: [PATCH] fix: merge 'Quick run' and 'Cancel build' action into one This also adds color to those actions and closes #1264 --- .../itsaky/androidide/actions/ActionItem.kt | 15 ++++ .../itsaky/androidide/actions/actionUtils.kt | 8 +- .../internal/DefaultActionsRegistry.kt | 2 +- .../actions/build/CancelBuildAction.kt | 90 ------------------- ...n.kt => QuickRunWithCancellationAction.kt} | 89 +++++++++++++++++- .../androidide/utils/EditorActivityActions.kt | 6 +- .../res/layout/layout_run_task_dialog.xml | 2 +- resources/src/main/res/values/attrs.xml | 7 +- resources/src/main/res/values/styles.xml | 1 + 9 files changed, 118 insertions(+), 102 deletions(-) delete mode 100644 app/src/main/java/com/itsaky/androidide/actions/build/CancelBuildAction.kt rename app/src/main/java/com/itsaky/androidide/actions/build/{QuickRunAction.kt => QuickRunWithCancellationAction.kt} (64%) diff --git a/actions/src/main/java/com/itsaky/androidide/actions/ActionItem.kt b/actions/src/main/java/com/itsaky/androidide/actions/ActionItem.kt index cf86ac5b8..57e34467b 100644 --- a/actions/src/main/java/com/itsaky/androidide/actions/ActionItem.kt +++ b/actions/src/main/java/com/itsaky/androidide/actions/ActionItem.kt @@ -17,9 +17,13 @@ package com.itsaky.androidide.actions +import android.graphics.ColorFilter +import android.graphics.PorterDuff +import android.graphics.PorterDuffColorFilter import android.graphics.drawable.Drawable import android.view.Menu import android.view.View +import com.itsaky.androidide.utils.resolveAttr /** * An action that can be registered using the [ActionsRegistry] @@ -121,6 +125,17 @@ interface ActionItem { */ fun createActionView(data: ActionData): View? = null + /** + * Creates the color filter for this action's icon drawable. + * + * The default implementation returns a [PorterDuffColorFilter] instance with color [R.attr.colorOnSurface]. + */ + fun createColorFilter(data: ActionData): ColorFilter? { + return data.getContext()?.let { + PorterDuffColorFilter(it.resolveAttr(R.attr.colorOnSurface), PorterDuff.Mode.SRC_ATOP) + } + } + /** Location where an action item will be shown. */ enum class Location(val id: String) { diff --git a/actions/src/main/java/com/itsaky/androidide/actions/actionUtils.kt b/actions/src/main/java/com/itsaky/androidide/actions/actionUtils.kt index c29793cd4..cbe410ca5 100644 --- a/actions/src/main/java/com/itsaky/androidide/actions/actionUtils.kt +++ b/actions/src/main/java/com/itsaky/androidide/actions/actionUtils.kt @@ -24,8 +24,12 @@ import io.github.rosemoe.sora.widget.CodeEditor import java.io.File import java.nio.file.Path -fun ActionData.requireContext() : Context { - return get(Context::class.java) ?: throw IllegalArgumentException("No context instance provided") +fun ActionData.getContext(): Context? { + return get(Context::class.java) +} + +fun ActionData.requireContext(): Context { + return getContext() ?: throw IllegalArgumentException("No context instance provided") } fun ActionData.requireFile(): File { diff --git a/actions/src/main/java/com/itsaky/androidide/actions/internal/DefaultActionsRegistry.kt b/actions/src/main/java/com/itsaky/androidide/actions/internal/DefaultActionsRegistry.kt index 50cab146a..8c685249f 100644 --- a/actions/src/main/java/com/itsaky/androidide/actions/internal/DefaultActionsRegistry.kt +++ b/actions/src/main/java/com/itsaky/androidide/actions/internal/DefaultActionsRegistry.kt @@ -158,7 +158,7 @@ class DefaultActionsRegistry : ActionsRegistry() { item.isEnabled = action.enabled item.icon = action.icon?.apply { - colorFilter = PorterDuffColorFilter(context!!.resolveAttr(R.attr.colorOnSurface), SRC_ATOP) + colorFilter = action.createColorFilter(data) } if (item.icon != null) { diff --git a/app/src/main/java/com/itsaky/androidide/actions/build/CancelBuildAction.kt b/app/src/main/java/com/itsaky/androidide/actions/build/CancelBuildAction.kt deleted file mode 100644 index 833703a5d..000000000 --- a/app/src/main/java/com/itsaky/androidide/actions/build/CancelBuildAction.kt +++ /dev/null @@ -1,90 +0,0 @@ -/* - * 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.actions.build - -import android.content.Context -import androidx.core.content.ContextCompat -import com.itsaky.androidide.resources.R -import com.itsaky.androidide.actions.ActionData -import com.itsaky.androidide.actions.EditorActivityAction -import com.itsaky.androidide.actions.markInvisible -import com.itsaky.androidide.lookup.Lookup -import com.itsaky.androidide.projects.builder.BuildService -import com.itsaky.androidide.utils.ILogger -import com.itsaky.androidide.utils.flashError - -/** @author Akash Yadav */ -class CancelBuildAction(context: Context) : EditorActivityAction() { - - private val log = ILogger.newInstance(javaClass.simpleName) - - init { - label = context.getString(R.string.title_cancel_build) - icon = ContextCompat.getDrawable(context, R.drawable.ic_stop_daemons) - } - - override val id: String = "editor_stopGradleDaemons" - - override fun prepare(data: ActionData) { - super.prepare(data) - - if (!visible) { - return - } - - val context = data.getActivity() - val buildService = Lookup.getDefault().lookup(BuildService.KEY_BUILD_SERVICE) - if (context == null || buildService == null) { - markInvisible() - return - } - - visible = true - enabled = buildService.isBuildInProgress - } - - override fun execAction(data: ActionData): Boolean { - log.info("Sending build cancellation request...") - val builder = Lookup.getDefault().lookup(BuildService.KEY_BUILD_SERVICE) - if (builder?.isToolingServerStarted() != true) { - flashError(com.itsaky.androidide.projects.R.string.msg_tooling_server_unavailable) - return false - } - - builder.cancelCurrentBuild().whenComplete { - result, - error -> - if (error != null) { - log.error("Failed to send build cancellation request", error) - return@whenComplete - } - - if (!result.wasEnqueued) { - log.warn( - "Unable to enqueue cancellation request", - result.failureReason, - result.failureReason!!.message - ) - return@whenComplete - } - - log.info("Build cancellation request was successfully enqueued...") - } - return true - } -} diff --git a/app/src/main/java/com/itsaky/androidide/actions/build/QuickRunAction.kt b/app/src/main/java/com/itsaky/androidide/actions/build/QuickRunWithCancellationAction.kt similarity index 64% rename from app/src/main/java/com/itsaky/androidide/actions/build/QuickRunAction.kt rename to app/src/main/java/com/itsaky/androidide/actions/build/QuickRunWithCancellationAction.kt index 648e62d23..7f742dadf 100644 --- a/app/src/main/java/com/itsaky/androidide/actions/build/QuickRunAction.kt +++ b/app/src/main/java/com/itsaky/androidide/actions/build/QuickRunWithCancellationAction.kt @@ -18,13 +18,20 @@ package com.itsaky.androidide.actions.build import android.content.Context +import android.graphics.ColorFilter +import android.graphics.PorterDuff +import android.graphics.PorterDuffColorFilter import androidx.core.content.ContextCompat import com.itsaky.androidide.actions.ActionData import com.itsaky.androidide.actions.BaseBuildAction +import com.itsaky.androidide.actions.getContext +import com.itsaky.androidide.actions.markInvisible import com.itsaky.androidide.actions.requireContext +import com.itsaky.androidide.lookup.Lookup import com.itsaky.androidide.models.ApkMetadata import com.itsaky.androidide.projects.IProjectManager import com.itsaky.androidide.projects.api.AndroidModule +import com.itsaky.androidide.projects.builder.BuildService import com.itsaky.androidide.resources.R import com.itsaky.androidide.tasks.executeAsyncProvideError import com.itsaky.androidide.tooling.api.messages.result.TaskExecutionResult @@ -33,18 +40,23 @@ import com.itsaky.androidide.utils.ApkInstaller import com.itsaky.androidide.utils.DialogUtils import com.itsaky.androidide.utils.InstallationResultHandler import com.itsaky.androidide.utils.flashError +import com.itsaky.androidide.utils.resolveAttr import java.io.File /** - * The 'Quick Run' action in the editor activity. + * The 'Quick Run' and 'Cancel build' action in the editor activity. + * + * If a build is in progress, executing this action cancels the build. Otherwise, the selected + * build variant is built and installed to the device. * * @author Akash Yadav */ -class QuickRunAction(context: Context) : BaseBuildAction() { +class QuickRunWithCancellationAction(context: Context) : BaseBuildAction() { init { label = context.getString(R.string.quick_run_debug) icon = ContextCompat.getDrawable(context, R.drawable.ic_run_outline) + enabled = false } override val id: String = "ide.editor.build.quickRun" @@ -52,7 +64,44 @@ class QuickRunAction(context: Context) : BaseBuildAction() { // Execute on UI thread as this action might try to show dialogs to the user override var requiresUIThread: Boolean = true + override fun prepare(data: ActionData) { + + val context = data.getActivity() ?: run { + markInvisible() + return + } + + if (data.isBuildInProgress()) { + label = context.getString(R.string.title_cancel_build) + icon = ContextCompat.getDrawable(context, R.drawable.ic_stop_daemons) + + visible = true + enabled = true + } else { + label = context.getString(R.string.quick_run_debug) + icon = ContextCompat.getDrawable(context, R.drawable.ic_run_outline) + } + } + + override fun createColorFilter(data: ActionData): ColorFilter? { + return data.getContext()?.let { + PorterDuffColorFilter(it.resolveAttr( + if (data.isBuildInProgress()) + R.attr.colorError + else R.attr.colorSuccess + ), PorterDuff.Mode.SRC_ATOP) + } + } + override fun execAction(data: ActionData): Boolean { + if (data.isBuildInProgress()) { + return cancelBuild() + } + + return quickRun(data) + } + + private fun quickRun(data: ActionData): Boolean { chooseApplication(data) { module -> val activity = data.requireActivity() @@ -81,6 +130,36 @@ class QuickRunAction(context: Context) : BaseBuildAction() { return true } + private fun cancelBuild(): Boolean { + log.info("Sending build cancellation request...") + val builder = Lookup.getDefault().lookup(BuildService.KEY_BUILD_SERVICE) + if (builder?.isToolingServerStarted() != true) { + flashError(com.itsaky.androidide.projects.R.string.msg_tooling_server_unavailable) + return false + } + + builder.cancelCurrentBuild().whenComplete { result, + error -> + if (error != null) { + log.error("Failed to send build cancellation request", error) + return@whenComplete + } + + if (!result.wasEnqueued) { + log.warn( + "Unable to enqueue cancellation request", + result.failureReason, + result.failureReason!!.message + ) + return@whenComplete + } + + log.info("Build cancellation request was successfully enqueued...") + } + + return true + } + private fun handleResult( data: ActionData, result: TaskExecutionResult?, @@ -170,4 +249,10 @@ class QuickRunAction(context: Context) : BaseBuildAction() { ) } } + + private fun ActionData.isBuildInProgress(): Boolean { + val context = getActivity() + val buildService = Lookup.getDefault().lookup(BuildService.KEY_BUILD_SERVICE) + return context?.editorViewModel?.let { it.isInitializing || it.isBuildInProgress } == true || buildService?.isBuildInProgress == true + } } diff --git a/app/src/main/java/com/itsaky/androidide/utils/EditorActivityActions.kt b/app/src/main/java/com/itsaky/androidide/utils/EditorActivityActions.kt index b2d32029b..685416021 100644 --- a/app/src/main/java/com/itsaky/androidide/utils/EditorActivityActions.kt +++ b/app/src/main/java/com/itsaky/androidide/utils/EditorActivityActions.kt @@ -21,9 +21,8 @@ import com.itsaky.androidide.actions.ActionItem.Location.EDITOR_FILE_TABS import com.itsaky.androidide.actions.ActionItem.Location.EDITOR_FILE_TREE import com.itsaky.androidide.actions.ActionItem.Location.EDITOR_TOOLBAR import com.itsaky.androidide.actions.ActionsRegistry -import com.itsaky.androidide.actions.build.CancelBuildAction import com.itsaky.androidide.actions.build.ProjectSyncAction -import com.itsaky.androidide.actions.build.QuickRunAction +import com.itsaky.androidide.actions.build.QuickRunWithCancellationAction import com.itsaky.androidide.actions.build.RunTasksAction import com.itsaky.androidide.actions.editor.CopyAction import com.itsaky.androidide.actions.editor.CutAction @@ -66,13 +65,12 @@ class EditorActivityActions { // Toolbar actions registry.registerAction(UndoAction(context)) registry.registerAction(RedoAction(context)) - registry.registerAction(QuickRunAction(context)) + registry.registerAction(QuickRunWithCancellationAction(context)) registry.registerAction(RunTasksAction(context)) registry.registerAction(SaveFileAction(context)) registry.registerAction(PreviewLayoutAction(context)) registry.registerAction(FindActionMenu(context)) registry.registerAction(FileTreeAction(context)) - registry.registerAction(CancelBuildAction(context)) registry.registerAction(ProjectSyncAction(context)) registry.registerAction(ReloadColorSchemesAction(context)) registry.registerAction(DisconnectLogSendersAction(context)) diff --git a/app/src/main/res/layout/layout_run_task_dialog.xml b/app/src/main/res/layout/layout_run_task_dialog.xml index 14fe43a59..b1fa52f73 100644 --- a/app/src/main/res/layout/layout_run_task_dialog.xml +++ b/app/src/main/res/layout/layout_run_task_dialog.xml @@ -35,7 +35,7 @@ android:layout_marginEnd="16dp" android:tooltipText="@string/run" app:icon="@drawable/ic_run" - app:iconTint="@color/green_500" /> + app:iconTint="?attr/colorSuccess" /> + + + - - + + diff --git a/resources/src/main/res/values/styles.xml b/resources/src/main/res/values/styles.xml index 7c8877790..9950b026a 100755 --- a/resources/src/main/res/values/styles.xml +++ b/resources/src/main/res/values/styles.xml @@ -70,6 +70,7 @@ @style/AppTheme.SwitchPreference @style/AppTheme.ShapeOverlay.Large @style/TextAppearance.AndroidIDE.BodySmall + @color/green_500