mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
feat: add basic support for handling configuration changes
This commit is contained in:
+3
-2
@@ -31,6 +31,7 @@ import com.itsaky.androidide.actions.locations.CodeActionsMenu
|
||||
import com.itsaky.androidide.utils.ILogger
|
||||
import com.itsaky.androidide.utils.resolveAttr
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
/**
|
||||
* Default implementation for the [ActionsRegistry]
|
||||
@@ -40,7 +41,7 @@ import java.util.concurrent.CompletableFuture
|
||||
class DefaultActionsRegistry : ActionsRegistry() {
|
||||
|
||||
private val log = ILogger.newInstance("DefaultActionsRegistry")
|
||||
private val actions: MutableMap<String, MutableMap<String, ActionItem>> = mutableMapOf()
|
||||
private val actions = ConcurrentHashMap<String, HashMap<String, ActionItem>>()
|
||||
private val listeners = mutableSetOf<ActionExecListener>()
|
||||
|
||||
init {
|
||||
@@ -49,7 +50,7 @@ class DefaultActionsRegistry : ActionsRegistry() {
|
||||
|
||||
override fun getActions(location: ActionItem.Location): MutableMap<String, ActionItem> {
|
||||
if (actions[location.id] == null) {
|
||||
actions[location.id] = mutableMapOf()
|
||||
actions[location.id] = hashMapOf()
|
||||
}
|
||||
|
||||
return actions[location.id]!!
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activities.MainActivity"
|
||||
android:configChanges="orientation|screenSize|uiMode"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:exported="true"
|
||||
android:theme="@style/AppTheme.Splash">
|
||||
<intent-filter>
|
||||
@@ -47,17 +47,17 @@
|
||||
<activity
|
||||
android:name=".activities.editor.EditorActivityKt"
|
||||
android:launchMode="singleTop"
|
||||
android:configChanges="orientation|screenSize|uiMode"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
<activity
|
||||
android:name=".activities.PreferencesActivity"
|
||||
android:configChanges="orientation|screenSize" />
|
||||
<activity
|
||||
android:name=".activities.AboutActivity"
|
||||
android:configChanges="orientation|screenSize|uiMode" />
|
||||
android:configChanges="orientation|screenSize" />
|
||||
<activity
|
||||
android:name=".activities.TerminalActivity"
|
||||
android:configChanges="orientation|screenSize|uiMode"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
|
||||
<service
|
||||
|
||||
@@ -42,7 +42,7 @@ class FileTreeAction() : EditorActivityAction() {
|
||||
override fun execAction(data: ActionData): Boolean {
|
||||
val context = getActivity(data) ?: return false
|
||||
|
||||
context.binding?.root?.apply {
|
||||
context.binding.root.apply {
|
||||
if (!isDrawerOpen(GravityCompat.END)) {
|
||||
openDrawer(GravityCompat.END)
|
||||
return true
|
||||
|
||||
@@ -38,7 +38,7 @@ class CloseFileAction(context: Context) : FileTabAction() {
|
||||
}
|
||||
|
||||
override fun EditorHandlerActivity.doAction(data: ActionData): Boolean {
|
||||
binding?.tabs?.selectedTabPosition?.let { closeFile(it) }
|
||||
binding.tabs.selectedTabPosition.let { closeFile(it) }
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,6 @@ import com.itsaky.androidide.ui.editor.CodeEditorView
|
||||
import com.itsaky.androidide.uidesigner.UIDesignerActivity
|
||||
import com.itsaky.androidide.utils.ActionMenuUtils.createMenu
|
||||
import com.itsaky.androidide.utils.DialogUtils.newMaterialDialogBuilder
|
||||
import com.itsaky.androidide.utils.EditorActivityActions.Companion.register as registerActivityActions
|
||||
import com.itsaky.androidide.utils.ILogger
|
||||
import com.itsaky.androidide.utils.InstallationResultHandler.onResult
|
||||
import com.itsaky.androidide.utils.SingleSessionCallback
|
||||
@@ -95,11 +94,12 @@ import com.itsaky.androidide.xml.versions.ApiVersionsRegistry
|
||||
import com.itsaky.androidide.xml.widgets.WidgetTableRegistry
|
||||
import com.itsaky.toaster.Toaster.Type.ERROR
|
||||
import com.itsaky.toaster.toast
|
||||
import java.io.File
|
||||
import java.util.Objects
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode.MAIN
|
||||
import java.io.File
|
||||
import java.util.Objects
|
||||
import com.itsaky.androidide.utils.EditorActivityActions.Companion.register as registerActivityActions
|
||||
|
||||
/**
|
||||
* Base class for EditorActivity which handles most of the view related things.
|
||||
@@ -119,18 +119,20 @@ abstract class BaseEditorActivity :
|
||||
protected var filesTreeFragment: FileTreeFragment? = null
|
||||
protected var editorBottomSheet: BottomSheetBehavior<out View?>? = null
|
||||
protected var isDestroying = false
|
||||
protected var isConfigChange = false
|
||||
protected var wasInitializing = false
|
||||
|
||||
protected val log: ILogger = ILogger.newInstance("EditorActivity")
|
||||
protected val logReceiver: LogReceiver = LogReceiver().setLogListener(::appendApkLog)
|
||||
|
||||
var uiDesignerResultLauncher: ActivityResultLauncher<Intent>? = null
|
||||
val viewModel by viewModels<EditorViewModel>()
|
||||
var binding: ActivityEditorBinding? = null
|
||||
lateinit var binding: ActivityEditorBinding
|
||||
protected set
|
||||
|
||||
private val onBackPressedCallback: OnBackPressedCallback =
|
||||
object : OnBackPressedCallback(true) {
|
||||
override fun handleOnBackPressed() {
|
||||
val binding = this@BaseEditorActivity.binding ?: return
|
||||
if (binding.root.isDrawerOpen(GravityCompat.END)) {
|
||||
binding.root.closeDrawer(GravityCompat.END)
|
||||
} else if (binding.root.isDrawerOpen(GravityCompat.START)) {
|
||||
@@ -159,6 +161,7 @@ abstract class BaseEditorActivity :
|
||||
protected abstract fun doConfirmProjectClose()
|
||||
|
||||
protected open fun preDestroy() {
|
||||
viewModel.isConfigChange = !isDestroying
|
||||
try {
|
||||
unregisterReceiver(logReceiver)
|
||||
} catch (th: Throwable) {
|
||||
@@ -167,28 +170,27 @@ abstract class BaseEditorActivity :
|
||||
}
|
||||
|
||||
protected open fun postDestroy() {
|
||||
Lookup.DEFAULT.unregisterAll()
|
||||
ApiVersionsRegistry.getInstance().clear()
|
||||
ResourceTableRegistry.getInstance().clear()
|
||||
WidgetTableRegistry.getInstance().clear()
|
||||
|
||||
binding = null
|
||||
binding = null
|
||||
if (isDestroying) {
|
||||
Lookup.DEFAULT.unregisterAll()
|
||||
ApiVersionsRegistry.getInstance().clear()
|
||||
ResourceTableRegistry.getInstance().clear()
|
||||
WidgetTableRegistry.getInstance().clear()
|
||||
}
|
||||
}
|
||||
|
||||
override fun bindLayout(): View {
|
||||
this.binding = ActivityEditorBinding.inflate(layoutInflater)
|
||||
this.diagnosticInfoBinding = this.binding!!.diagnosticInfo
|
||||
return this.binding!!.root
|
||||
this.diagnosticInfoBinding = this.binding.diagnosticInfo
|
||||
return this.binding.root
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = MAIN)
|
||||
open fun onInstallationResult(event: InstallationResultEvent) {
|
||||
val intent = event.intent
|
||||
if (binding == null || isDestroying) {
|
||||
if (isDestroying) {
|
||||
return
|
||||
}
|
||||
val binding = this.binding ?: return
|
||||
|
||||
val packageName = onResult(this, intent)
|
||||
if (packageName != null) {
|
||||
Snackbar.make(binding.realContainer, string.msg_action_open_application, Snackbar.LENGTH_LONG)
|
||||
@@ -209,15 +211,20 @@ abstract class BaseEditorActivity :
|
||||
projectPath = savedInstanceState.getString(KEY_PROJECT_PATH)!!
|
||||
}
|
||||
|
||||
this.wasInitializing = viewModel.isInitializing
|
||||
this.isConfigChange = viewModel.isConfigChange
|
||||
viewModel.isConfigChange = false
|
||||
viewModel.isInitializing = false
|
||||
|
||||
onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
|
||||
lifecycle.addObserver(mLifecycleObserver)
|
||||
|
||||
setSupportActionBar(binding?.editorToolbar)
|
||||
setSupportActionBar(binding.editorToolbar)
|
||||
|
||||
dmonStatusFragment = getDaemonStatusFragment()
|
||||
|
||||
setupDrawerToggle()
|
||||
binding?.tabs?.addOnTabSelectedListener(this)
|
||||
binding.tabs.addOnTabSelectedListener(this)
|
||||
|
||||
setupViews()
|
||||
|
||||
@@ -289,7 +296,7 @@ abstract class BaseEditorActivity :
|
||||
id.editornav_terminal -> openTerminal()
|
||||
}
|
||||
|
||||
binding?.root?.closeDrawer(GravityCompat.START)
|
||||
binding.root.closeDrawer(GravityCompat.START)
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -324,7 +331,7 @@ abstract class BaseEditorActivity :
|
||||
}
|
||||
|
||||
open fun appendApkLog(line: LogLine) {
|
||||
binding?.bottomSheet?.appendApkLog(line)
|
||||
binding.bottomSheet.appendApkLog(line)
|
||||
}
|
||||
|
||||
open fun handleSearchResults(map: Map<File, List<SearchResult>>?) {
|
||||
@@ -347,11 +354,11 @@ abstract class BaseEditorActivity :
|
||||
}
|
||||
|
||||
open fun setSearchResultAdapter(adapter: SearchListAdapter) {
|
||||
binding?.bottomSheet?.setSearchResultAdapter(adapter)
|
||||
binding.bottomSheet.setSearchResultAdapter(adapter)
|
||||
}
|
||||
|
||||
open fun setDiagnosticsAdapter(adapter: DiagnosticsAdapter) {
|
||||
binding?.bottomSheet?.setDiagnosticsAdapter(adapter)
|
||||
binding.bottomSheet.setDiagnosticsAdapter(adapter)
|
||||
}
|
||||
|
||||
open fun showDaemonStatus() {
|
||||
@@ -387,23 +394,19 @@ abstract class BaseEditorActivity :
|
||||
}
|
||||
|
||||
val index =
|
||||
binding
|
||||
?.bottomSheet
|
||||
?.pagerAdapter
|
||||
?.findIndexOfFragmentByClass(SearchResultFragment::class.java)
|
||||
?: -1
|
||||
binding.bottomSheet.pagerAdapter.findIndexOfFragmentByClass(SearchResultFragment::class.java)
|
||||
|
||||
if (index >= 0 && index < (binding?.bottomSheet?.binding?.tabs?.tabCount ?: 0)) {
|
||||
binding?.bottomSheet?.binding?.tabs?.getTabAt(index)?.select()
|
||||
if (index >= 0 && index < binding.bottomSheet.binding.tabs.tabCount) {
|
||||
binding.bottomSheet.binding.tabs.getTabAt(index)?.select()
|
||||
}
|
||||
}
|
||||
|
||||
open fun handleDiagnosticsResultVisibility(errorVisible: Boolean) {
|
||||
binding?.bottomSheet?.handleDiagnosticsResultVisibility(errorVisible)
|
||||
binding.bottomSheet.handleDiagnosticsResultVisibility(errorVisible)
|
||||
}
|
||||
|
||||
open fun handleSearchResultVisibility(errorVisible: Boolean) {
|
||||
binding?.bottomSheet?.handleSearchResultVisibility(errorVisible)
|
||||
binding.bottomSheet.handleSearchResultVisibility(errorVisible)
|
||||
}
|
||||
|
||||
open fun showFirstBuildNotice() {
|
||||
@@ -461,7 +464,6 @@ abstract class BaseEditorActivity :
|
||||
}
|
||||
|
||||
private fun setupDrawerToggle() {
|
||||
val binding = this.binding ?: return
|
||||
val toggle =
|
||||
ActionBarDrawerToggle(
|
||||
this,
|
||||
@@ -479,23 +481,17 @@ abstract class BaseEditorActivity :
|
||||
|
||||
private fun onBuildStatusChanged() {
|
||||
val visible = viewModel.isBuildInProgress || viewModel.isInitializing
|
||||
binding?.buildProgressIndicator?.visibility = if (visible) View.VISIBLE else View.GONE
|
||||
binding.buildProgressIndicator.visibility = if (visible) View.VISIBLE else View.GONE
|
||||
invalidateOptionsMenu()
|
||||
}
|
||||
|
||||
private fun setupViews() {
|
||||
viewModel._isBuildInProgress.observe(this) { onBuildStatusChanged() }
|
||||
viewModel._isInitializing.observe(this) { onBuildStatusChanged() }
|
||||
viewModel._statusText.observe(this) {
|
||||
binding?.bottomSheet?.setStatus(it.first, it.second)
|
||||
}
|
||||
viewModel._statusText.observe(this) { binding.bottomSheet.setStatus(it.first, it.second) }
|
||||
|
||||
viewModel.observeFiles(this) { files ->
|
||||
if (binding == null) {
|
||||
return@observeFiles
|
||||
}
|
||||
|
||||
binding?.apply {
|
||||
binding.apply {
|
||||
if (files == null || files.isEmpty()) {
|
||||
tabs.visibility = View.GONE
|
||||
viewContainer.displayedChild = 1
|
||||
@@ -525,11 +521,11 @@ abstract class BaseEditorActivity :
|
||||
}
|
||||
|
||||
private fun setupNoEditorView() {
|
||||
binding?.noEditorSummary?.movementMethod = LinkMovementMethod()
|
||||
binding.noEditorSummary.movementMethod = LinkMovementMethod()
|
||||
val filesSpan: ClickableSpan =
|
||||
object : ClickableSpan() {
|
||||
override fun onClick(widget: View) {
|
||||
binding?.root?.openDrawer(GravityCompat.END)
|
||||
binding.root.openDrawer(GravityCompat.END)
|
||||
}
|
||||
}
|
||||
val bottomSheetSpan: ClickableSpan =
|
||||
@@ -541,7 +537,7 @@ abstract class BaseEditorActivity :
|
||||
val sb = SpannableStringBuilder()
|
||||
appendClickableSpan(sb, string.msg_swipe_for_files, filesSpan)
|
||||
appendClickableSpan(sb, string.msg_swipe_for_output, bottomSheetSpan)
|
||||
binding?.noEditorSummary?.text = sb
|
||||
binding.noEditorSummary.text = sb
|
||||
}
|
||||
|
||||
private fun appendClickableSpan(
|
||||
@@ -564,7 +560,7 @@ abstract class BaseEditorActivity :
|
||||
}
|
||||
|
||||
private fun setupBottomSheet() {
|
||||
editorBottomSheet = BottomSheetBehavior.from<View>(binding!!.bottomSheet)
|
||||
editorBottomSheet = BottomSheetBehavior.from<View>(binding.bottomSheet)
|
||||
editorBottomSheet?.addBottomSheetCallback(
|
||||
object : BottomSheetCallback() {
|
||||
override fun onStateChanged(bottomSheet: View, newState: Int) {
|
||||
@@ -577,7 +573,7 @@ abstract class BaseEditorActivity :
|
||||
}
|
||||
|
||||
override fun onSlide(bottomSheet: View, slideOffset: Float) {
|
||||
binding?.apply {
|
||||
binding.apply {
|
||||
val editorScale = 1 - slideOffset * (1 - EDITOR_CONTAINER_SCALE_FACTOR)
|
||||
this.bottomSheet.onSlide(slideOffset)
|
||||
this.viewContainer.scaleX = editorScale
|
||||
@@ -590,7 +586,7 @@ abstract class BaseEditorActivity :
|
||||
val observer: OnGlobalLayoutListener =
|
||||
object : OnGlobalLayoutListener {
|
||||
override fun onGlobalLayout() {
|
||||
binding?.let {
|
||||
binding.let {
|
||||
it.viewContainer.pivotY = 0f
|
||||
it.viewContainer.pivotX = it.viewContainer.width / 2f
|
||||
it.viewContainer.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
@@ -598,14 +594,14 @@ abstract class BaseEditorActivity :
|
||||
}
|
||||
}
|
||||
|
||||
binding?.apply {
|
||||
binding.apply {
|
||||
viewContainer.viewTreeObserver.addOnGlobalLayoutListener(observer)
|
||||
bottomSheet.setOffsetAnchor(editorToolbar)
|
||||
}
|
||||
}
|
||||
|
||||
private fun refreshSymbolInput(editor: CodeEditorView) {
|
||||
binding?.bottomSheet?.refreshSymbolInput(editor)
|
||||
binding.bottomSheet.refreshSymbolInput(editor)
|
||||
}
|
||||
|
||||
private fun setupDiagnosticInfo() {
|
||||
@@ -625,7 +621,7 @@ abstract class BaseEditorActivity :
|
||||
|
||||
private fun onSoftInputChanged() {
|
||||
invalidateOptionsMenu()
|
||||
binding?.bottomSheet?.onSoftInputChanged()
|
||||
binding.bottomSheet.onSoftInputChanged()
|
||||
}
|
||||
|
||||
private fun openTerminal() {
|
||||
@@ -652,7 +648,7 @@ abstract class BaseEditorActivity :
|
||||
}
|
||||
|
||||
private fun getSyncBanner(): MaterialBanner? {
|
||||
return binding?.run {
|
||||
return binding.run {
|
||||
return@run syncBanner
|
||||
.setContentTextColor(resolveAttr(attr.colorOnPrimaryContainer))
|
||||
.setBannerBackgroundColor(resolveAttr(attr.colorPrimaryContainer))
|
||||
@@ -666,7 +662,7 @@ abstract class BaseEditorActivity :
|
||||
return object : SingleSessionCallback() {
|
||||
override fun onCreated(sessionId: Int) {
|
||||
log.debug("on session created:", sessionId)
|
||||
binding?.apply {
|
||||
binding.apply {
|
||||
bottomSheet.setActionText(getString(string.msg_installing_apk))
|
||||
bottomSheet.setActionProgress(0)
|
||||
bottomSheet.showChild(EditorBottomSheet.CHILD_ACTION)
|
||||
@@ -674,11 +670,11 @@ abstract class BaseEditorActivity :
|
||||
}
|
||||
|
||||
override fun onProgressChanged(sessionId: Int, progress: Float) {
|
||||
binding?.bottomSheet?.setActionProgress((progress * 100f).toInt())
|
||||
binding.bottomSheet.setActionProgress((progress * 100f).toInt())
|
||||
}
|
||||
|
||||
override fun onFinished(sessionId: Int, success: Boolean) {
|
||||
binding?.apply {
|
||||
binding.apply {
|
||||
bottomSheet.showChild(EditorBottomSheet.CHILD_HEADER)
|
||||
bottomSheet.setActionProgress(0)
|
||||
if (!success) {
|
||||
|
||||
+23
-22
@@ -51,43 +51,47 @@ import java.io.File
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
|
||||
|
||||
|
||||
override fun doOpenFile(file: File, selection: Range?) {
|
||||
openFileAndSelect(file, selection)
|
||||
}
|
||||
|
||||
|
||||
override fun doCloseAll(runAfter: () -> Unit) {
|
||||
closeAll(runAfter)
|
||||
}
|
||||
|
||||
|
||||
override fun doSaveAll(): Boolean {
|
||||
return saveAll()
|
||||
}
|
||||
|
||||
|
||||
override fun provideCurrentEditor(): CodeEditorView? {
|
||||
return getCurrentEditor()
|
||||
}
|
||||
|
||||
|
||||
override fun provideEditorAt(index: Int): CodeEditorView? {
|
||||
return getEditorAtIndex(index)
|
||||
}
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
viewModel._displayedFile.observe(this) { this.binding?.editorContainer?.displayedChild = it }
|
||||
viewModel._displayedFile.observe(this) { this.binding.editorContainer.displayedChild = it }
|
||||
viewModel._fileTreeDrawerOpened.observe(this) { opened ->
|
||||
this.binding?.editorDrawerLayout?.apply {
|
||||
this.binding.editorDrawerLayout.apply {
|
||||
if (opened) openDrawer(GravityCompat.END) else closeDrawer(GravityCompat.END)
|
||||
}
|
||||
}
|
||||
|
||||
if (viewModel.getOpenedFileCount() != binding.tabs.tabCount) {
|
||||
// The activity proabably has been restarted after a configuration change
|
||||
// restore the opened files
|
||||
|
||||
val files = viewModel.getOpenedFiles()
|
||||
viewModel.removeAllFiles()
|
||||
files.forEach(this::openFile)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
binding = null
|
||||
}
|
||||
|
||||
@SuppressLint("RestrictedApi")
|
||||
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
|
||||
if (menu is MenuBuilder) {
|
||||
@@ -95,7 +99,7 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
override fun onPrepareOptionsMenu(menu: Menu): Boolean {
|
||||
ensureToolbarMenu(menu)
|
||||
return true
|
||||
@@ -108,7 +112,7 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
|
||||
}
|
||||
|
||||
override fun getEditorAtIndex(index: Int): CodeEditorView? {
|
||||
return binding?.editorContainer?.getChildAt(index) as CodeEditorView?
|
||||
return binding.editorContainer.getChildAt(index) as CodeEditorView?
|
||||
}
|
||||
|
||||
override fun openFileAndSelect(file: File, selection: Range?) {
|
||||
@@ -134,7 +138,6 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
|
||||
return null
|
||||
}
|
||||
|
||||
val binding = this.binding ?: return null
|
||||
val index = openFileAndGetIndex(file, range)
|
||||
val tab = binding.tabs.getTabAt(index)
|
||||
if (tab != null && index >= 0 && !tab.isSelected) {
|
||||
@@ -154,7 +157,6 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
|
||||
}
|
||||
|
||||
override fun openFileAndGetIndex(file: File, selection: Range?): Int {
|
||||
val binding = this.binding ?: return -1
|
||||
val openedFileIndex = findIndexOfEditorByFile(file)
|
||||
if (openedFileIndex != -1) {
|
||||
log.error("File is already opened. File: $file")
|
||||
@@ -181,7 +183,6 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
|
||||
}
|
||||
|
||||
override fun getEditorForFile(file: File): CodeEditorView? {
|
||||
val binding = this.binding ?: return null
|
||||
for (i in 0 until viewModel.getOpenedFileCount()) {
|
||||
val editor = binding.editorContainer.getChildAt(i) as CodeEditorView
|
||||
if (file == editor.file) {
|
||||
@@ -302,7 +303,7 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
|
||||
}
|
||||
|
||||
viewModel.removeFile(index)
|
||||
binding?.apply {
|
||||
binding.apply {
|
||||
tabs.removeTabAt(index)
|
||||
editorContainer.removeViewAt(index)
|
||||
}
|
||||
@@ -311,7 +312,7 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
|
||||
return
|
||||
}
|
||||
|
||||
binding?.tabs?.requestLayout()
|
||||
binding.tabs.requestLayout()
|
||||
}
|
||||
|
||||
override fun closeAll() {
|
||||
@@ -341,7 +342,7 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
|
||||
notifyFilesUnsaved(unsavedFiles) { closeOthers() }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
open fun ensureToolbarMenu(menu: Menu) {
|
||||
menu.clear()
|
||||
val data = ActionData()
|
||||
@@ -377,7 +378,7 @@ open class EditorHandlerActivity : ProjectHandlerActivity(), IEditorHandler {
|
||||
}
|
||||
|
||||
viewModel.removeAllFiles()
|
||||
binding?.apply {
|
||||
binding.apply {
|
||||
tabs.removeAllTabs()
|
||||
tabs.requestLayout()
|
||||
editorContainer.removeAllViews()
|
||||
|
||||
+139
-80
@@ -33,14 +33,16 @@ import com.itsaky.androidide.databinding.LayoutSearchProjectBinding
|
||||
import com.itsaky.androidide.fragments.sheets.ProgressSheet
|
||||
import com.itsaky.androidide.handlers.EditorBuildEventListener
|
||||
import com.itsaky.androidide.handlers.LspHandler.connectClient
|
||||
import com.itsaky.androidide.handlers.LspHandler.destroy
|
||||
import com.itsaky.androidide.handlers.LspHandler.destroyLanguageServers
|
||||
import com.itsaky.androidide.interfaces.IProjectHandler
|
||||
import com.itsaky.androidide.lookup.Lookup
|
||||
import com.itsaky.androidide.lsp.IDELanguageClientImpl
|
||||
import com.itsaky.androidide.preferences.internal.NO_OPENED_PROJECT
|
||||
import com.itsaky.androidide.preferences.internal.lastOpenedProject
|
||||
import com.itsaky.androidide.projects.ProjectManager.cachedInitResult
|
||||
import com.itsaky.androidide.projects.ProjectManager.getProjectDirPath
|
||||
import com.itsaky.androidide.projects.ProjectManager.notifyProjectUpdate
|
||||
import com.itsaky.androidide.projects.ProjectManager.projectInitialized
|
||||
import com.itsaky.androidide.projects.ProjectManager.projectPath
|
||||
import com.itsaky.androidide.projects.ProjectManager.rootProject
|
||||
import com.itsaky.androidide.projects.ProjectManager.setupProject
|
||||
@@ -54,16 +56,17 @@ import com.itsaky.androidide.utils.RecursiveFileSearcher
|
||||
import com.itsaky.toaster.Toaster.Type.ERROR
|
||||
import com.itsaky.toaster.toast
|
||||
import java.io.File
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import java.util.regex.Pattern
|
||||
import java.util.stream.Collectors
|
||||
|
||||
/** @author Akash Yadav */
|
||||
@Suppress("MemberVisibilityCanBePrivate")
|
||||
abstract class ProjectHandlerActivity : BaseEditorActivity(), IProjectHandler {
|
||||
|
||||
|
||||
protected var mSearchingProgress: ProgressSheet? = null
|
||||
protected var mFindInProjectDialog: AlertDialog? = null
|
||||
|
||||
|
||||
val findInProjectDialog: AlertDialog
|
||||
get() {
|
||||
if (mFindInProjectDialog == null) {
|
||||
@@ -71,47 +74,57 @@ abstract class ProjectHandlerActivity : BaseEditorActivity(), IProjectHandler {
|
||||
}
|
||||
return mFindInProjectDialog!!
|
||||
}
|
||||
|
||||
|
||||
protected val mBuildEventListener = EditorBuildEventListener()
|
||||
private val mGradleServiceConnection: ServiceConnection =
|
||||
object : ServiceConnection {
|
||||
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
||||
val buildService = (service as GradleServiceBinder).service
|
||||
log.info("Gradle build service has been started...")
|
||||
Lookup.DEFAULT.update(BuildService.KEY_BUILD_SERVICE, buildService)
|
||||
buildService.setEventListener(mBuildEventListener).startToolingServer {
|
||||
initializeProject()
|
||||
}
|
||||
onGradleBuildServiceConnected(buildService)
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(name: ComponentName?) {
|
||||
log.info("Disconnected from Gradle build service...")
|
||||
log.info("Disconnected from Gradle build service")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
abstract fun doCloseAll(runAfter: () -> Unit)
|
||||
|
||||
|
||||
override fun doDismissSearchProgress() {
|
||||
if (mSearchingProgress?.isShowing == true) {
|
||||
mSearchingProgress!!.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun doConfirmProjectClose() {
|
||||
confirmProjectClose()
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
|
||||
mBuildEventListener.setActivity(this as EditorHandlerActivity)
|
||||
startServices()
|
||||
}
|
||||
|
||||
|
||||
override fun preDestroy() {
|
||||
closeProject(false)
|
||||
if (isDestroying) {
|
||||
closeProject(false)
|
||||
projectInitialized = false
|
||||
cachedInitResult = null
|
||||
}
|
||||
|
||||
super.preDestroy()
|
||||
unbindService(mGradleServiceConnection)
|
||||
|
||||
if (isDestroying) {
|
||||
try {
|
||||
app.unbindService(mGradleServiceConnection)
|
||||
} catch (err: Throwable) {
|
||||
log.error("Unable to unbind service", err)
|
||||
} finally {
|
||||
Lookup.DEFAULT.unregister(BuildService.KEY_BUILD_SERVICE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setStatus(status: CharSequence, @GravityInt gravity: Int) {
|
||||
@@ -119,7 +132,7 @@ abstract class ProjectHandlerActivity : BaseEditorActivity(), IProjectHandler {
|
||||
}
|
||||
|
||||
override fun appendBuildOutput(str: String) {
|
||||
binding?.bottomSheet?.appendBuildOut(str)
|
||||
binding.bottomSheet.appendBuildOut(str)
|
||||
}
|
||||
|
||||
override fun notifySyncNeeded() {
|
||||
@@ -127,11 +140,19 @@ abstract class ProjectHandlerActivity : BaseEditorActivity(), IProjectHandler {
|
||||
}
|
||||
|
||||
override fun startServices() {
|
||||
|
||||
val service = Lookup.DEFAULT.lookup(BuildService.KEY_BUILD_SERVICE) as GradleBuildService?
|
||||
if (service != null) {
|
||||
log.info("Reusing already started Gradle build service")
|
||||
onGradleBuildServiceConnected(service)
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
bindService(
|
||||
app.bindService(
|
||||
Intent(this, GradleBuildService::class.java),
|
||||
mGradleServiceConnection,
|
||||
BIND_AUTO_CREATE
|
||||
BIND_AUTO_CREATE or BIND_IMPORTANT
|
||||
)
|
||||
) {
|
||||
log.info("Bind request for Gradle build service was successful...")
|
||||
@@ -143,13 +164,22 @@ abstract class ProjectHandlerActivity : BaseEditorActivity(), IProjectHandler {
|
||||
}
|
||||
|
||||
override fun initializeProject() {
|
||||
val projectPath = projectPath
|
||||
val projectDir = File(projectPath)
|
||||
if (!projectDir.exists()) {
|
||||
log.error("Project directory does not exist. Cannot initialize project")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
val initialized = projectInitialized && cachedInitResult != null
|
||||
|
||||
log.debug(projectInitialized, cachedInitResult, wasInitializing)
|
||||
|
||||
// When returning after a configuration change between the initialization process,
|
||||
// we do not want to start another project initialization
|
||||
if (initialized && !wasInitializing) {
|
||||
return
|
||||
}
|
||||
|
||||
//noinspection ConstantConditions
|
||||
ThreadUtils.runOnUiThread { preProjectInit() }
|
||||
|
||||
@@ -158,16 +188,27 @@ abstract class ProjectHandlerActivity : BaseEditorActivity(), IProjectHandler {
|
||||
log.error("No build service found. Cannot initialize project.")
|
||||
return
|
||||
}
|
||||
|
||||
val future =
|
||||
if (!(isConfigChange && wasInitializing) && !initialized) {
|
||||
buildService.initializeProject(projectDir.absolutePath)
|
||||
} else {
|
||||
// The project initialization was in progress before the configuration change
|
||||
// In this case, we should not start another project initialization
|
||||
CompletableFuture.supplyAsync {
|
||||
log.warn("Project has already been initialized. Skipping initialization process.")
|
||||
cachedInitResult
|
||||
}
|
||||
}
|
||||
|
||||
val future = buildService.initializeProject(projectDir.absolutePath)
|
||||
future.whenCompleteAsync { result: InitializeResult?, error: Throwable? ->
|
||||
future.whenCompleteAsync { result, error ->
|
||||
if (result == null || error != null) {
|
||||
log.error("An error occurred initializing the project with Tooling API", error)
|
||||
setStatus(getString(string.msg_project_initialization_failed))
|
||||
return@whenCompleteAsync
|
||||
}
|
||||
|
||||
onProjectInitialized()
|
||||
|
||||
onProjectInitialized(result)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,86 +217,100 @@ abstract class ProjectHandlerActivity : BaseEditorActivity(), IProjectHandler {
|
||||
if (IDELanguageClientImpl.isInitialized()) {
|
||||
IDELanguageClientImpl.shutdown()
|
||||
}
|
||||
destroy(isChangingConfigurations)
|
||||
destroyLanguageServers(isChangingConfigurations)
|
||||
} catch (err: Throwable) {
|
||||
log.error("Unable to stop editor services. Please report this issue.", err)
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun onProjectInitialized() {
|
||||
protected fun onGradleBuildServiceConnected(service: GradleBuildService) {
|
||||
log.info("Connected to Gradle build service")
|
||||
|
||||
service.setEventListener(mBuildEventListener)
|
||||
|
||||
if (!service.isToolingServerStarted) {
|
||||
service.startToolingServer { initializeProject() }
|
||||
} else {
|
||||
initializeProject()
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun onProjectInitialized(result: InitializeResult) {
|
||||
if (isConfigChange && projectInitialized && result == cachedInitResult) {
|
||||
return
|
||||
}
|
||||
|
||||
cachedInitResult = result
|
||||
setupProject()
|
||||
notifyProjectUpdate()
|
||||
ThreadUtils.runOnUiThread { postProjectInit() }
|
||||
}
|
||||
|
||||
|
||||
protected open fun postProjectInit() {
|
||||
this.binding ?: return
|
||||
initialSetup()
|
||||
setStatus(getString(string.msg_project_initialized))
|
||||
viewModel.isInitializing = false
|
||||
|
||||
projectInitialized = true
|
||||
|
||||
if (mFindInProjectDialog?.isShowing == true) {
|
||||
mFindInProjectDialog!!.dismiss()
|
||||
}
|
||||
|
||||
|
||||
mFindInProjectDialog = null // Create the dialog again if needed
|
||||
}
|
||||
|
||||
|
||||
protected open fun createFindInProjectDialog(): AlertDialog? {
|
||||
if (rootProject == null) {
|
||||
log.warn("No root project model found. Is the project initialized?")
|
||||
toast(getString(string.msg_project_not_initialized), ERROR)
|
||||
return null
|
||||
}
|
||||
|
||||
val moduleDirs = try {
|
||||
rootProject!!.subModules.stream()
|
||||
.map(Project::projectDir)
|
||||
.collect(Collectors.toList())
|
||||
} catch (e: Throwable) {
|
||||
toast(getString(string.msg_no_modules), ERROR)
|
||||
emptyList()
|
||||
}
|
||||
|
||||
|
||||
val moduleDirs =
|
||||
try {
|
||||
rootProject!!.subModules.stream().map(Project::projectDir).collect(Collectors.toList())
|
||||
} catch (e: Throwable) {
|
||||
toast(getString(string.msg_no_modules), ERROR)
|
||||
emptyList()
|
||||
}
|
||||
|
||||
return createFindInProjectDialog(moduleDirs)
|
||||
}
|
||||
|
||||
|
||||
protected open fun createFindInProjectDialog(moduleDirs: List<File>): AlertDialog? {
|
||||
val srcDirs = mutableListOf<File>()
|
||||
val binding = LayoutSearchProjectBinding.inflate(layoutInflater)
|
||||
binding.modulesContainer.removeAllViews()
|
||||
|
||||
|
||||
for (i in moduleDirs.indices) {
|
||||
val module = moduleDirs[i]
|
||||
val src = File(module, "src")
|
||||
|
||||
|
||||
if (!module.exists() || !module.isDirectory || !src.exists() || !src.isDirectory) {
|
||||
continue
|
||||
}
|
||||
|
||||
|
||||
val check = CheckBox(this)
|
||||
check.text = module.name
|
||||
check.isChecked = true
|
||||
|
||||
|
||||
val params = MarginLayoutParams(-2, -2)
|
||||
params.bottomMargin = SizeUtils.dp2px(4f)
|
||||
binding.modulesContainer.addView(check, params)
|
||||
srcDirs.add(src)
|
||||
}
|
||||
|
||||
|
||||
val builder = newMaterialDialogBuilder(this)
|
||||
builder.setTitle(string.menu_find_project)
|
||||
builder.setView(binding.root)
|
||||
builder.setCancelable(false)
|
||||
builder.setPositiveButton(
|
||||
string.menu_find
|
||||
) { dialog, _ ->
|
||||
builder.setPositiveButton(string.menu_find) { dialog, _ ->
|
||||
val text = binding.input.editText!!.text.toString().trim()
|
||||
if (text.isEmpty()) {
|
||||
toast(string.msg_empty_search_query, ERROR)
|
||||
return@setPositiveButton
|
||||
}
|
||||
|
||||
|
||||
val searchDirs = mutableListOf<File>()
|
||||
for (i in 0 until binding.modulesContainer.childCount) {
|
||||
val check = binding.modulesContainer.getChildAt(i) as CheckBox
|
||||
@@ -263,12 +318,16 @@ abstract class ProjectHandlerActivity : BaseEditorActivity(), IProjectHandler {
|
||||
searchDirs.add(srcDirs[i])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val extensions = binding.filter.editText!!.text.toString().trim()
|
||||
val extensionList = mutableListOf<String>()
|
||||
if (extensions.isNotEmpty()) {
|
||||
if (extensions.contains("|")) {
|
||||
for (str in extensions.split(Pattern.quote("|").toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) {
|
||||
for (str in
|
||||
extensions
|
||||
.split(Pattern.quote("|").toRegex())
|
||||
.dropLastWhile { it.isEmpty() }
|
||||
.toTypedArray()) {
|
||||
if (str.trim().isEmpty()) {
|
||||
continue
|
||||
}
|
||||
@@ -278,26 +337,27 @@ abstract class ProjectHandlerActivity : BaseEditorActivity(), IProjectHandler {
|
||||
extensionList.add(extensions)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (searchDirs.isEmpty()) {
|
||||
toast(string.msg_select_search_modules, ERROR)
|
||||
} else {
|
||||
dialog.dismiss()
|
||||
|
||||
|
||||
getProgressSheet(string.msg_searching_project)?.apply {
|
||||
show(supportFragmentManager, "search_in_project_progress")
|
||||
}
|
||||
|
||||
RecursiveFileSearcher.searchRecursiveAsync(
|
||||
text, extensionList, searchDirs) { results -> handleSearchResults(results) }
|
||||
|
||||
RecursiveFileSearcher.searchRecursiveAsync(text, extensionList, searchDirs) { results ->
|
||||
handleSearchResults(results)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
builder.setNegativeButton(android.R.string.cancel) { dialog, _ -> dialog.dismiss() }
|
||||
mFindInProjectDialog = builder.create()
|
||||
return mFindInProjectDialog
|
||||
}
|
||||
|
||||
|
||||
private fun initialSetup() {
|
||||
lastOpenedProject = getProjectDirPath()
|
||||
try {
|
||||
@@ -306,21 +366,21 @@ abstract class ProjectHandlerActivity : BaseEditorActivity(), IProjectHandler {
|
||||
log.warn("Project not initialized. Skipping initial setup...")
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
var projectName = rootProject.name
|
||||
if (projectName.isEmpty()) {
|
||||
projectName = File(getProjectDirPath()).name
|
||||
}
|
||||
|
||||
|
||||
supportActionBar!!.subtitle = projectName
|
||||
} catch (th: Throwable) {
|
||||
// ignored
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun closeProject(manualFinish: Boolean) {
|
||||
stopServices()
|
||||
|
||||
|
||||
// Make sure we close files
|
||||
// This will make sure that file contents are not erased.
|
||||
doCloseAll {
|
||||
@@ -330,42 +390,41 @@ abstract class ProjectHandlerActivity : BaseEditorActivity(), IProjectHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun confirmProjectClose() {
|
||||
val builder = newMaterialDialogBuilder(this)
|
||||
builder.setTitle(string.title_confirm_project_close)
|
||||
builder.setMessage(string.msg_confirm_project_close)
|
||||
builder.setNegativeButton(string.no, null)
|
||||
builder.setPositiveButton(
|
||||
string.yes
|
||||
) { dialog, _ ->
|
||||
builder.setPositiveButton(string.yes) { dialog, _ ->
|
||||
dialog.dismiss()
|
||||
closeProject(true)
|
||||
}
|
||||
builder.show()
|
||||
}
|
||||
|
||||
|
||||
private fun initLspClient() {
|
||||
if (!IDELanguageClientImpl.isInitialized()) {
|
||||
IDELanguageClientImpl.initialize(this as EditorHandlerActivity)
|
||||
}
|
||||
connectClient(IDELanguageClientImpl.getInstance())
|
||||
}
|
||||
|
||||
|
||||
private fun preProjectInit() {
|
||||
setStatus(getString(string.msg_initializing_project))
|
||||
viewModel.isInitializing = true
|
||||
}
|
||||
|
||||
|
||||
open fun getProgressSheet(msg: Int): ProgressSheet? {
|
||||
doDismissSearchProgress()
|
||||
|
||||
mSearchingProgress = ProgressSheet().apply {
|
||||
isCancelable = false
|
||||
setMessage(getString(msg))
|
||||
setSubMessageEnabled(false)
|
||||
}
|
||||
|
||||
|
||||
mSearchingProgress =
|
||||
ProgressSheet().apply {
|
||||
isCancelable = false
|
||||
setMessage(getString(msg))
|
||||
setSubMessageEnabled(false)
|
||||
}
|
||||
|
||||
return mSearchingProgress
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ public class IDEApplication extends BaseApplication {
|
||||
private static final ILogger LOG = ILogger.newInstance("IDEApplication");
|
||||
private static IDEApplication instance;
|
||||
private Thread.UncaughtExceptionHandler uncaughtExceptionHandler;
|
||||
|
||||
|
||||
public static IDEApplication getInstance() {
|
||||
return instance;
|
||||
}
|
||||
@@ -49,7 +49,7 @@ public class IDEApplication extends BaseApplication {
|
||||
this.uncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
|
||||
Thread.setDefaultUncaughtExceptionHandler(this::handleCrash);
|
||||
super.onCreate();
|
||||
|
||||
|
||||
EventBus.builder()
|
||||
.addIndex(new AppEventsIndex())
|
||||
.addIndex(new ProjectsApiEventsIndex())
|
||||
|
||||
@@ -37,6 +37,9 @@ class EditorBuildEventListener : GradleBuildService.EventListener {
|
||||
fun setActivity(activity: EditorHandlerActivity) {
|
||||
this.activityReference = WeakReference(activity)
|
||||
}
|
||||
|
||||
fun releaes() {
|
||||
}
|
||||
|
||||
override fun prepareBuild() {
|
||||
val isFirstBuild = isFirstBuild
|
||||
|
||||
@@ -103,7 +103,7 @@ class FileTreeActionHandler : BaseEventHandler() {
|
||||
}
|
||||
|
||||
val context = event[Context::class.java]!! as EditorHandlerActivity
|
||||
context.binding?.root?.closeDrawer(GravityCompat.END)
|
||||
context.binding.root.closeDrawer(GravityCompat.END)
|
||||
if (event.file.name.endsWith(".apk")) {
|
||||
ApkInstaller.installApk(
|
||||
context,
|
||||
|
||||
@@ -39,7 +39,7 @@ object LspHandler {
|
||||
ILanguageServerRegistry.getDefault().connectClient(client)
|
||||
}
|
||||
|
||||
fun destroy(isConfigurationChange: Boolean) {
|
||||
fun destroyLanguageServers(isConfigurationChange: Boolean) {
|
||||
if (isConfigurationChange) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ import android.content.Intent;
|
||||
import android.os.Binder;
|
||||
import android.os.IBinder;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@@ -96,7 +97,7 @@ public class GradleBuildService extends Service implements BuildService, IToolin
|
||||
private final IBinder mBinder = new GradleServiceBinder();
|
||||
private boolean isToolingServerStarted = false;
|
||||
private boolean isBuildInProgress = false;
|
||||
private Thread toolingServerThread;
|
||||
private ToolingServerRunner toolingServerThread;
|
||||
private NotificationManager notificationManager;
|
||||
private IToolingApiServer server;
|
||||
private EventListener eventListener;
|
||||
@@ -105,8 +106,14 @@ public class GradleBuildService extends Service implements BuildService, IToolin
|
||||
public void onCreate() {
|
||||
notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
showNotification(getString(R.string.build_status_idle), false);
|
||||
|
||||
Lookup.DEFAULT.update(BuildService.KEY_BUILD_SERVICE, this);
|
||||
}
|
||||
|
||||
|
||||
public boolean isToolingServerStarted() {
|
||||
return isToolingServerStarted;
|
||||
}
|
||||
|
||||
private void showNotification(final String message, final boolean isProgress) {
|
||||
LOG.info("Showing notification to user...");
|
||||
startForeground(NOTIFICATION_ID, buildNotification(message, isProgress));
|
||||
@@ -163,6 +170,7 @@ public class GradleBuildService extends Service implements BuildService, IToolin
|
||||
@Nullable
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
LOG.debug("onBind() called with: intent = [" + intent + "]");
|
||||
return mBinder;
|
||||
}
|
||||
|
||||
@@ -397,8 +405,15 @@ public class GradleBuildService extends Service implements BuildService, IToolin
|
||||
|
||||
public void startToolingServer(@Nullable OnServerStartListener listener) {
|
||||
if (toolingServerThread == null || !toolingServerThread.isAlive()) {
|
||||
toolingServerThread = new Thread(new ToolingServerRunner(listener));
|
||||
toolingServerThread = new ToolingServerRunner(listener);
|
||||
toolingServerThread.start();
|
||||
return;
|
||||
}
|
||||
|
||||
if (toolingServerThread.isStarted && listener != null) {
|
||||
listener.onServerStarted();
|
||||
} else {
|
||||
toolingServerThread.setListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,14 +480,19 @@ public class GradleBuildService extends Service implements BuildService, IToolin
|
||||
}
|
||||
}
|
||||
|
||||
private class ToolingServerRunner implements Runnable {
|
||||
private class ToolingServerRunner extends Thread {
|
||||
|
||||
@Nullable private final OnServerStartListener listener;
|
||||
@Nullable private OnServerStartListener listener;
|
||||
private boolean isStarted = false;
|
||||
|
||||
public ToolingServerRunner(@Nullable OnServerStartListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
|
||||
public void setListener(@Nullable final OnServerStartListener listener) {
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
@@ -525,6 +545,7 @@ public class GradleBuildService extends Service implements BuildService, IToolin
|
||||
|
||||
ProjectManager.INSTANCE.setupProject();
|
||||
|
||||
isStarted = true;
|
||||
if (listener != null) {
|
||||
listener.onServerStarted();
|
||||
}
|
||||
|
||||
@@ -18,6 +18,9 @@ package com.itsaky.androidide.utils
|
||||
|
||||
import android.content.Context
|
||||
import com.itsaky.androidide.actions.ActionItem
|
||||
import com.itsaky.androidide.actions.ActionItem.Location.EDITOR_FILE_TABS
|
||||
import com.itsaky.androidide.actions.ActionItem.Location.EDITOR_TEXT_ACTIONS
|
||||
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
|
||||
@@ -79,6 +82,10 @@ class EditorActivityActions {
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun clear() = ActionsRegistry.getInstance().clearActions(ActionItem.Location.EDITOR_TOOLBAR)
|
||||
fun clear() {
|
||||
val locations = arrayOf(EDITOR_TOOLBAR, EDITOR_FILE_TABS, EDITOR_TEXT_ACTIONS)
|
||||
val registry = ActionsRegistry.getInstance()
|
||||
locations.forEach(registry::clearActions)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ class EditorViewModel : ViewModel() {
|
||||
|
||||
internal val _isBuildInProgress = MutableLiveData(false)
|
||||
internal val _isInitializing = MutableLiveData(false)
|
||||
internal val _isConfigurationChange = MutableLiveData(false)
|
||||
internal val _statusText = MutableLiveData<Pair<CharSequence, @GravityInt Int>>("" to CENTER)
|
||||
internal val _displayedFile = MutableLiveData(-1)
|
||||
internal val _fileTreeDrawerOpened = MutableLiveData(false)
|
||||
@@ -42,6 +43,12 @@ class EditorViewModel : ViewModel() {
|
||||
*/
|
||||
private val mCurrentFile = MutableLiveData<Pair<Int, File?>?>(null)
|
||||
|
||||
var isConfigChange: Boolean
|
||||
get() = _isConfigurationChange.value ?: false
|
||||
set(value) {
|
||||
_isConfigurationChange.value = value
|
||||
}
|
||||
|
||||
var isBuildInProgress: Boolean
|
||||
get() = _isBuildInProgress.value ?: false
|
||||
set(value) {
|
||||
|
||||
@@ -30,6 +30,7 @@ import com.itsaky.androidide.projects.api.Project
|
||||
import com.itsaky.androidide.projects.builder.BuildService
|
||||
import com.itsaky.androidide.projects.util.ProjectTransformer
|
||||
import com.itsaky.androidide.tooling.api.IProject
|
||||
import com.itsaky.androidide.tooling.api.messages.result.InitializeResult
|
||||
import com.itsaky.androidide.utils.ILogger
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
@@ -52,6 +53,9 @@ object ProjectManager : EventReceiver {
|
||||
|
||||
var rootProject: Project? = null
|
||||
var app: AndroidModule? = null
|
||||
|
||||
var projectInitialized: Boolean = false
|
||||
var cachedInitResult: InitializeResult? = null
|
||||
|
||||
@JvmOverloads
|
||||
fun setupProject(project: IProject = Lookup.DEFAULT.lookup(BuildService.KEY_PROJECT_PROXY)!!) {
|
||||
@@ -61,7 +65,6 @@ object ProjectManager : EventReceiver {
|
||||
this.app = this.rootProject!!.findFirstAndroidAppModule()
|
||||
this.rootProject!!.subModules.filterIsInstance(ModuleProject::class.java).forEach {
|
||||
it.indexSourcesAndClasspaths()
|
||||
|
||||
if (it is AndroidModule) {
|
||||
it.readResources()
|
||||
}
|
||||
|
||||
Binary file not shown.
@@ -20,6 +20,7 @@
|
||||
<application android:theme="@style/Theme.AndroidIDE">
|
||||
<activity
|
||||
android:name=".UIDesignerActivity"
|
||||
android:configChanges="orientation|screenSize"
|
||||
android:windowSoftInputMode="adjustResize" />
|
||||
<activity
|
||||
android:name=".ShowXmlActivity"
|
||||
|
||||
Reference in New Issue
Block a user