fix: merge 'Quick run' and 'Cancel build' action into one

This also adds color to those actions and closes #1264
This commit is contained in:
Akash Yadav
2023-09-11 23:43:57 +05:30
parent 42ac697856
commit fddefdfc8f
9 changed files with 118 additions and 102 deletions
@@ -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) {
@@ -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 {
@@ -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) {
@@ -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 <https://www.gnu.org/licenses/>.
*/
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
}
}
@@ -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
}
}
@@ -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))
@@ -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" />
<TextView
android:layout_width="wrap_content"
+5 -2
View File
@@ -18,8 +18,11 @@
-->
<resources>
<!-- Color used for success drawables-->
<attr format="color" name="colorSuccess" />
<declare-styleable name="VectorMasterView">
<attr name="vector_src" format="reference" />
<attr name="use_legacy_parser" format="boolean" />
<attr format="reference" name="vector_src" />
<attr format="boolean" name="use_legacy_parser" />
</declare-styleable>
</resources>
+1
View File
@@ -70,6 +70,7 @@
<item name="switchPreferenceStyle">@style/AppTheme.SwitchPreference</item>
<item name="shapeAppearanceLargeComponent">@style/AppTheme.ShapeOverlay.Large</item>
<item name="textAppearanceBodySmall">@style/TextAppearance.AndroidIDE.BodySmall</item>
<item name="colorSuccess">@color/green_500</item>
</style>
<style name="Theme.AndroidIDE" parent="Theme.AndroidIDE.Styled">