mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
fix: use Kotlin coroutines for git clone (fixes #1452)
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
package com.itsaky.androidide.fragments
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.blankj.utilcode.util.ThreadUtils
|
||||
import com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
@@ -14,23 +16,29 @@ import com.itsaky.androidide.activities.PreferencesActivity
|
||||
import com.itsaky.androidide.activities.TerminalActivity
|
||||
import com.itsaky.androidide.adapters.MainActionsListAdapter
|
||||
import com.itsaky.androidide.app.BaseApplication
|
||||
import com.itsaky.androidide.app.BaseIDEActivity
|
||||
import com.itsaky.androidide.common.databinding.LayoutDialogProgressBinding
|
||||
import com.itsaky.androidide.databinding.FragmentMainBinding
|
||||
import com.itsaky.androidide.lsp.java.utils.CancelChecker
|
||||
import com.itsaky.androidide.models.MainScreenAction
|
||||
import com.itsaky.androidide.preferences.databinding.LayoutDialogTextInputBinding
|
||||
import com.itsaky.androidide.resources.R.string
|
||||
import com.itsaky.androidide.tasks.executeAsyncProvideError
|
||||
import com.itsaky.androidide.tasks.runOnUiThread
|
||||
import com.itsaky.androidide.utils.DialogUtils
|
||||
import com.itsaky.androidide.utils.Environment
|
||||
import com.itsaky.androidide.utils.ILogger
|
||||
import com.itsaky.androidide.utils.flashError
|
||||
import com.itsaky.androidide.utils.flashSuccess
|
||||
import com.itsaky.androidide.viewmodel.MainViewModel
|
||||
import com.termux.shared.termux.TermuxConstants
|
||||
import com.termux.shared.termux.TermuxConstants.TERMUX_APP.TERMUX_ACTIVITY
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.eclipse.jgit.api.Git
|
||||
import org.eclipse.jgit.lib.ProgressMonitor
|
||||
import java.io.File
|
||||
import java.util.concurrent.CancellationException
|
||||
|
||||
class MainFragment : BaseFragment() {
|
||||
|
||||
@@ -142,36 +150,47 @@ class MainFragment : BaseFragment() {
|
||||
val targetDir = File(Environment.PROJECTS_DIR, repoName)
|
||||
|
||||
val progress = GitCloneProgressMonitor(binding.progress, binding.message)
|
||||
var git: Git? = null
|
||||
val future = executeAsyncProvideError({
|
||||
return@executeAsyncProvideError Git.cloneRepository()
|
||||
.setURI(url)
|
||||
.setDirectory(targetDir)
|
||||
.setProgressMonitor(progress)
|
||||
.call()
|
||||
.also { git = it }
|
||||
}, { _, _ -> })
|
||||
val coroutineScope = (activity as? BaseIDEActivity?)?.activityScope ?: viewLifecycleScope
|
||||
|
||||
var getDialog: Function0<AlertDialog?>? = null
|
||||
|
||||
val cloneJob = coroutineScope.launch(Dispatchers.IO) {
|
||||
|
||||
val git = try {
|
||||
Git.cloneRepository()
|
||||
.setURI(url)
|
||||
.setDirectory(targetDir)
|
||||
.setProgressMonitor(progress)
|
||||
.call()
|
||||
} catch (err: Throwable) {
|
||||
if (!progress.isCancelled) {
|
||||
err.printStackTrace()
|
||||
withContext(Dispatchers.Main) {
|
||||
getDialog?.invoke()?.also { if (it.isShowing) it.dismiss() }
|
||||
showCloneError(err)
|
||||
}
|
||||
}
|
||||
null
|
||||
}
|
||||
|
||||
try {
|
||||
git?.close()
|
||||
} finally {
|
||||
val success = git != null
|
||||
getDialog?.invoke()?.also { dialog ->
|
||||
if (dialog.isShowing) dialog.dismiss()
|
||||
if (success) flashSuccess(string.git_clone_success)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
builder.setPositiveButton(android.R.string.cancel) { iface, _ ->
|
||||
iface.dismiss()
|
||||
progress.cancel()
|
||||
git?.close()
|
||||
future.cancel(true)
|
||||
cloneJob.cancel(CancellationException("Cancelled by user"))
|
||||
}
|
||||
|
||||
val dialog = builder.show()
|
||||
|
||||
future.whenComplete { result, error ->
|
||||
ThreadUtils.runOnUiThread {
|
||||
dialog?.dismiss()
|
||||
result?.close()
|
||||
if (result == null || error != null) {
|
||||
if (!future.isCancelled) {
|
||||
showCloneError(error)
|
||||
}
|
||||
} else flashSuccess(string.git_clone_success)
|
||||
}
|
||||
}
|
||||
getDialog = { builder.show() }
|
||||
}
|
||||
|
||||
private fun showCloneError(error: Throwable?) {
|
||||
@@ -203,15 +222,15 @@ class MainFragment : BaseFragment() {
|
||||
}
|
||||
|
||||
override fun start(totalTasks: Int) {
|
||||
ThreadUtils.runOnUiThread { progress.max = totalTasks }
|
||||
runOnUiThread { progress.max = totalTasks }
|
||||
}
|
||||
|
||||
override fun beginTask(title: String?, totalWork: Int) {
|
||||
ThreadUtils.runOnUiThread { message.text = title }
|
||||
runOnUiThread { message.text = title }
|
||||
}
|
||||
|
||||
override fun update(completed: Int) {
|
||||
ThreadUtils.runOnUiThread { progress.progress = completed }
|
||||
runOnUiThread { progress.progress = completed }
|
||||
}
|
||||
|
||||
override fun showDuration(enabled: Boolean) {
|
||||
|
||||
@@ -47,7 +47,7 @@ abstract class BaseIDEActivity : AppCompatActivity() {
|
||||
/**
|
||||
* [CoroutineScope] for executing tasks with the [Default][Dispatchers.Default] dispatcher.
|
||||
*/
|
||||
protected val activityScope = CoroutineScope(Dispatchers.Default)
|
||||
val activityScope = CoroutineScope(Dispatchers.Default)
|
||||
|
||||
val isStoragePermissionGranted: Boolean
|
||||
get() =
|
||||
|
||||
@@ -28,6 +28,10 @@ import androidx.fragment.app.Fragment
|
||||
import com.itsaky.androidide.common.R
|
||||
import com.itsaky.androidide.resources.R.string
|
||||
import com.itsaky.androidide.utils.flashError
|
||||
import kotlinx.coroutines.CoroutineName
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.cancel
|
||||
import java.io.File
|
||||
|
||||
open class BaseFragment @JvmOverloads constructor(contentLayoutId: Int = 0) :
|
||||
@@ -37,11 +41,18 @@ open class BaseFragment @JvmOverloads constructor(contentLayoutId: Int = 0) :
|
||||
private val allowedAuthorities =
|
||||
setOf(ANDROID_DOCS_AUTHORITY, ANDROIDIDE_DOCS_AUTHORITY)
|
||||
|
||||
protected val viewLifecycleScope = CoroutineScope(Dispatchers.Default + CoroutineName(javaClass.simpleName))
|
||||
|
||||
companion object {
|
||||
const val ANDROID_DOCS_AUTHORITY = "com.android.externalstorage.documents"
|
||||
const val ANDROIDIDE_DOCS_AUTHORITY = "com.itsaky.androidide.documents"
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
viewLifecycleScope.cancel("${javaClass.simpleName} is being destroyed")
|
||||
}
|
||||
|
||||
private val startForResult =
|
||||
registerForActivityResult(StartActivityForResult()) {
|
||||
val context = requireContext()
|
||||
|
||||
Reference in New Issue
Block a user