Skip to content

Commit ad9ad95

Browse files
committed
Merge branch 'stage' into fix/ADFA-3365-include-analysis-api-as-dependency
2 parents 1ca86bc + d97edf7 commit ad9ad95

File tree

26 files changed

+986
-222
lines changed

26 files changed

+986
-222
lines changed

app/src/main/java/com/itsaky/androidide/actions/EditorActivityAction.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ abstract class EditorActivityAction : ActionItem {
4444
super.prepare(data)
4545
if (!data.hasRequiredData(Context::class.java)) {
4646
markInvisible()
47+
return
4748
}
4849
}
4950

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
package com.itsaky.androidide.actions.build
2+
3+
import android.content.Context
4+
import android.graphics.ColorFilter
5+
import android.graphics.PorterDuff
6+
import android.graphics.PorterDuffColorFilter
7+
import android.graphics.drawable.Drawable
8+
import androidx.core.content.ContextCompat
9+
import com.itsaky.androidide.actions.ActionData
10+
import com.itsaky.androidide.actions.ActionItem
11+
import com.itsaky.androidide.actions.BaseBuildAction
12+
import com.itsaky.androidide.actions.getContext
13+
import com.itsaky.androidide.plugins.extensions.CommandOutput
14+
import com.itsaky.androidide.plugins.manager.build.PluginBuildActionManager
15+
import com.itsaky.androidide.plugins.manager.build.RegisteredBuildAction
16+
import com.itsaky.androidide.plugins.manager.core.PluginManager
17+
import com.itsaky.androidide.plugins.manager.ui.PluginDrawableResolver
18+
import com.itsaky.androidide.plugins.services.IdeCommandService
19+
import com.itsaky.androidide.resources.R
20+
import com.itsaky.androidide.utils.resolveAttr
21+
import com.itsaky.androidide.viewmodel.BottomSheetViewModel
22+
import com.google.android.material.bottomsheet.BottomSheetBehavior
23+
import androidx.lifecycle.lifecycleScope
24+
import com.itsaky.androidide.activities.editor.EditorHandlerActivity
25+
import kotlinx.coroutines.CancellationException
26+
import kotlinx.coroutines.Dispatchers
27+
import kotlinx.coroutines.launch
28+
import kotlinx.coroutines.withContext
29+
30+
class PluginBuildActionItem(
31+
context: Context,
32+
private val registered: RegisteredBuildAction,
33+
override val order: Int
34+
) : BaseBuildAction() {
35+
36+
override val id: String = "plugin.build.${registered.pluginId}.${registered.action.id}"
37+
38+
init {
39+
label = registered.action.name
40+
icon = resolvePluginIcon(context)
41+
location = ActionItem.Location.EDITOR_TOOLBAR
42+
requiresUIThread = true
43+
}
44+
45+
override fun prepare(data: ActionData) {
46+
val context = data.getActivity()
47+
if (context == null) {
48+
visible = false
49+
return
50+
}
51+
visible = true
52+
53+
val manager = PluginBuildActionManager.getInstance()
54+
val isRunning = manager.isActionRunning(registered.pluginId, registered.action.id)
55+
56+
if (isRunning) {
57+
label = "Cancel ${registered.action.name}"
58+
icon = ContextCompat.getDrawable(context, R.drawable.ic_stop)
59+
enabled = true
60+
} else {
61+
label = registered.action.name
62+
icon = resolvePluginIcon(context)
63+
enabled = true
64+
}
65+
}
66+
67+
override fun createColorFilter(data: ActionData): ColorFilter? {
68+
val context = data.getContext() ?: return null
69+
val isRunning = PluginBuildActionManager.getInstance()
70+
.isActionRunning(registered.pluginId, registered.action.id)
71+
val attr = if (isRunning) R.attr.colorError else R.attr.colorOnSurface
72+
return PorterDuffColorFilter(
73+
context.resolveAttr(attr),
74+
PorterDuff.Mode.SRC_ATOP
75+
)
76+
}
77+
78+
private fun resolvePluginIcon(fallbackContext: Context): Drawable? {
79+
val iconResId = registered.action.icon ?: return ContextCompat.getDrawable(fallbackContext, R.drawable.ic_run_outline)
80+
return PluginDrawableResolver.resolve(iconResId, registered.pluginId, fallbackContext)
81+
?: ContextCompat.getDrawable(fallbackContext, R.drawable.ic_run_outline)
82+
}
83+
84+
override suspend fun execAction(data: ActionData): Any {
85+
val manager = PluginBuildActionManager.getInstance()
86+
val pluginId = registered.pluginId
87+
val actionId = registered.action.id
88+
89+
if (manager.isActionRunning(pluginId, actionId)) {
90+
manager.cancelAction(pluginId, actionId)
91+
data.getActivity()?.let { resetProgressIfIdle(it) }
92+
return true
93+
}
94+
95+
val activity = data.getActivity() ?: return false
96+
97+
val pluginManager = PluginManager.getInstance() ?: return false
98+
val loadedPlugin = pluginManager.getLoadedPlugin(pluginId) ?: return false
99+
val commandService = loadedPlugin.context.services.get(IdeCommandService::class.java)
100+
?: return false
101+
102+
val execution = manager.executeAction(pluginId, actionId, commandService) ?: return false
103+
104+
activity.editorViewModel.isBuildInProgress = true
105+
val currentSheetState = activity.bottomSheetViewModel.sheetBehaviorState
106+
val targetState = if (currentSheetState == BottomSheetBehavior.STATE_HIDDEN)
107+
BottomSheetBehavior.STATE_COLLAPSED else currentSheetState
108+
activity.bottomSheetViewModel.setSheetState(
109+
sheetState = targetState,
110+
currentTab = BottomSheetViewModel.TAB_BUILD_OUTPUT
111+
)
112+
activity.appendBuildOutput("━━━ ${registered.action.name} ━━━")
113+
activity.invalidateOptionsMenu()
114+
115+
activity.lifecycleScope.launch(Dispatchers.Default) {
116+
runCatching {
117+
execution.output.collect { output ->
118+
val line = when (output) {
119+
is CommandOutput.StdOut -> output.line
120+
is CommandOutput.StdErr -> output.line
121+
is CommandOutput.ExitCode ->
122+
if (output.code != 0) "Process failed with code ${output.code}" else null
123+
}
124+
if (line != null) {
125+
withContext(Dispatchers.Main) {
126+
activity.appendBuildOutput(line)
127+
}
128+
}
129+
}
130+
131+
val result = execution.await()
132+
manager.notifyActionCompleted(pluginId, actionId, result)
133+
withContext(Dispatchers.Main) { resetProgressIfIdle(activity) }
134+
}.onFailure { e ->
135+
if (e is CancellationException) {
136+
manager.cancelAction(pluginId, actionId)
137+
throw e
138+
}
139+
withContext(Dispatchers.Main) { resetProgressIfIdle(activity) }
140+
}
141+
}
142+
143+
return true
144+
}
145+
146+
private fun resetProgressIfIdle(activity: EditorHandlerActivity) {
147+
val manager = PluginBuildActionManager.getInstance()
148+
if (buildService?.isBuildInProgress != true && !manager.hasActiveExecutions()) {
149+
activity.editorViewModel.isBuildInProgress = false
150+
}
151+
activity.invalidateOptionsMenu()
152+
}
153+
}

app/src/main/java/com/itsaky/androidide/actions/file/ShowTooltipAction.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ class ShowTooltipAction(private val context: Context, override val order: Int) :
4242

4343
init {
4444
label = context.getString(R.string.title_show_tooltip)
45-
val drawable = ContextCompat.getDrawable(context, R.drawable.ic_action_help)
45+
val drawable = ContextCompat.getDrawable(context, R.drawable.ic_action_help_outlined)
4646
icon = drawable?.let { tintDrawable(context, it) }
4747
}
4848

app/src/main/java/com/itsaky/androidide/actions/filetree/HelpAction.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class HelpAction(context: Context, override val order: Int) :
1111
BaseFileTreeAction(
1212
context = context,
1313
labelRes = R.string.help,
14-
iconRes = R.drawable.ic_action_help
14+
iconRes = R.drawable.ic_action_help_outlined
1515
) {
1616
override val id: String = "ide.editor.fileTree.help"
1717
override fun retrieveTooltipTag(isReadOnlyContext: Boolean): String {

app/src/main/java/com/itsaky/androidide/actions/main/OpenTerminalAction.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import com.itsaky.androidide.actions.ActionItem
1010
import com.itsaky.androidide.actions.markInvisible
1111
import com.itsaky.androidide.activities.TerminalActivity
1212
import com.itsaky.androidide.idetooltips.TooltipTag
13+
import com.itsaky.androidide.utils.applyMultiWindowFlags
1314

1415
class OpenTerminalAction(context: Context) : ActionItem {
1516

@@ -38,7 +39,9 @@ class OpenTerminalAction(context: Context) : ActionItem {
3839

3940
override suspend fun execAction(data: ActionData): Any {
4041
val context = data.get(Context::class.java) ?: return false
41-
context.startActivity(Intent(context, TerminalActivity::class.java))
42+
val intent = Intent(context, TerminalActivity::class.java)
43+
.applyMultiWindowFlags(context)
44+
context.startActivity(intent)
4245
return true
4346
}
4447
}

app/src/main/java/com/itsaky/androidide/actions/sidebar/TerminalSidebarAction.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import com.itsaky.androidide.actions.requireContext
2727
import com.itsaky.androidide.activities.TerminalActivity
2828
import com.itsaky.androidide.idetooltips.TooltipTag
2929
import com.itsaky.androidide.projects.IProjectManager
30+
import com.itsaky.androidide.utils.applyMultiWindowFlags
3031
import com.termux.shared.termux.TermuxConstants.TERMUX_APP.TERMUX_ACTIVITY
3132
import java.util.Objects
3233
import kotlin.reflect.KClass
@@ -71,7 +72,7 @@ class TerminalSidebarAction(
7172
?.name,
7273
)
7374
putExtra(TERMUX_ACTIVITY.EXTRA_FAILSAFE_SESSION, isFailsafe)
74-
}
75+
}.applyMultiWindowFlags(context)
7576
context.startActivity(intent)
7677
}
7778
}

app/src/main/java/com/itsaky/androidide/activities/editor/EditorHandlerActivity.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ import com.itsaky.androidide.models.OpenedFile
6363
import com.itsaky.androidide.models.OpenedFilesCache
6464
import com.itsaky.androidide.models.Range
6565
import com.itsaky.androidide.models.SaveResult
66+
import com.itsaky.androidide.plugins.manager.build.PluginBuildActionManager
6667
import com.itsaky.androidide.plugins.manager.fragment.PluginFragmentFactory
6768
import com.itsaky.androidide.plugins.manager.ui.PluginDrawableResolver
6869
import com.itsaky.androidide.plugins.manager.ui.PluginEditorTabManager
@@ -423,12 +424,15 @@ open class EditorHandlerActivity :
423424
content.projectActionsToolbar.clearMenu()
424425

425426
val actions = getInstance().getActions(EDITOR_TOOLBAR)
427+
val hiddenIds = PluginBuildActionManager.getInstance().getHiddenActionIds()
426428
actions.onEachIndexed { index, entry ->
427429
val action = entry.value
428430
val isLast = index == actions.size - 1
429431

430432
action.prepare(data)
431433

434+
if (action.id in hiddenIds || !action.visible) return@onEachIndexed
435+
432436
action.icon?.apply {
433437
colorFilter = action.createColorFilter(data)
434438
alpha = if (action.enabled) 255 else 76

app/src/main/java/com/itsaky/androidide/activities/editor/ProjectHandlerActivity.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,7 @@ abstract class ProjectHandlerActivity : BaseEditorActivity() {
759759
initialSetup()
760760
setStatus(getString(string.msg_project_initialized))
761761
editorViewModel.isInitializing = false
762+
invalidateOptionsMenu()
762763

763764
if (mFindInProjectDialog?.isShowing == true) {
764765
mFindInProjectDialog!!.dismiss()

app/src/main/java/com/itsaky/androidide/assets/BaseAssetsInstaller.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ abstract class BaseAssetsInstaller : AssetsInstaller {
2222
"aapt",
2323
"aapt2",
2424
"aidl",
25+
"d8",
2526
"dexdump",
2627
"split-select",
2728
"zipalign",

app/src/main/java/com/itsaky/androidide/utils/EditorActivityActions.kt

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -57,10 +57,13 @@ import com.itsaky.androidide.actions.filetree.RenameAction
5757
import com.itsaky.androidide.actions.text.RedoAction
5858
import com.itsaky.androidide.actions.text.UndoAction
5959
import com.itsaky.androidide.actions.PluginActionItem
60+
import com.itsaky.androidide.actions.build.PluginBuildActionItem
6061
import com.itsaky.androidide.actions.etc.GenerateXMLAction
6162
import com.itsaky.androidide.plugins.extensions.UIExtension
63+
import com.itsaky.androidide.plugins.manager.build.PluginBuildActionManager
6264
import com.itsaky.androidide.plugins.manager.core.PluginManager
6365

66+
6467
/**
6568
* Takes care of registering actions to the actions registry for the editor activity.
6669
*
@@ -104,6 +107,7 @@ class EditorActivityActions {
104107

105108
// Plugin contributions
106109
order = registerPluginActions(context, registry, order)
110+
order = registerPluginBuildActions(context, registry, order)
107111

108112
// editor text actions
109113
registry.registerAction(ExpandSelectionAction(context, order++))
@@ -157,7 +161,8 @@ class EditorActivityActions {
157161
registry.clearActionsExceptWhere(EDITOR_TOOLBAR) { action ->
158162
action.id == QuickRunAction.ID ||
159163
action.id == RunTasksAction.ID ||
160-
action.id == ProjectSyncAction.ID
164+
action.id == ProjectSyncAction.ID ||
165+
action.id.startsWith("plugin.build.")
161166
}
162167
}
163168

@@ -185,13 +190,28 @@ class EditorActivityActions {
185190
registry.registerAction(action)
186191
}
187192
} catch (e: Exception) {
188-
// Continue with other plugins if one fails
189-
System.err.println("")
190-
Log.d("plugin_debug", "Failed to register menu items for plugin: ${plugin.javaClass.simpleName} - ${e.message}")
193+
Log.w("plugin_debug", "Failed to register menu items for plugin: ${plugin.javaClass.simpleName}", e)
191194
}
192195
}
193196

194197
return order
195198
}
199+
200+
@JvmStatic
201+
private fun registerPluginBuildActions(context: Context, registry: ActionsRegistry, startOrder: Int): Int {
202+
var order = startOrder
203+
204+
PluginBuildActionManager.getInstance().getAllBuildActions().forEach { registered ->
205+
runCatching {
206+
registry.registerAction(PluginBuildActionItem(context, registered, order++))
207+
Log.d("plugin_debug", "Registered build action: ${registered.action.id} from plugin: ${registered.pluginId}")
208+
}.onFailure { e ->
209+
Log.w("plugin_debug", "Failed to register build action: ${registered.action.id}", e)
210+
}
211+
}
212+
213+
return order
214+
}
215+
196216
}
197217
}

0 commit comments

Comments
 (0)