diff --git a/app/src/main/java/com/itsaky/androidide/fragments/CloneRepositoryFragment.kt b/app/src/main/java/com/itsaky/androidide/fragments/CloneRepositoryFragment.kt index 6cdf18215f..b462f08995 100644 --- a/app/src/main/java/com/itsaky/androidide/fragments/CloneRepositoryFragment.kt +++ b/app/src/main/java/com/itsaky/androidide/fragments/CloneRepositoryFragment.kt @@ -82,12 +82,11 @@ class CloneRepositoryFragment : BaseFragment() { } cloneButton.setOnClickListener { - val url = repoUrl.text.toString() - val path = localPath.text.toString() - val username = if (authCheckbox.isChecked) username.text.toString() else null - val password = if (authCheckbox.isChecked) password.text.toString() else null - - viewModel.cloneRepository(url, path, username, password) + cloneRepo() + } + + retryButton.setOnClickListener { + cloneRepo() } exitButton.setOnClickListener { @@ -110,6 +109,15 @@ class CloneRepositoryFragment : BaseFragment() { } } + private fun FragmentCloneRepositoryBinding.cloneRepo() { + val url = repoUrl.text.toString() + val path = localPath.text.toString() + val mUsername = if (authCheckbox.isChecked) username.text.toString() else null + val mPassword = if (authCheckbox.isChecked) password.text.toString() else null + + viewModel.cloneRepository(url, path, mUsername, mPassword) + } + private fun observeViewModel() { viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { @@ -140,14 +148,17 @@ class CloneRepositoryFragment : BaseFragment() { when (state) { is CloneRepoUiState.Idle -> { cloneButton.isEnabled = state.isCloneButtonEnabled + retryButton.visibility = View.GONE statusText.text = "" } is CloneRepoUiState.Cloning -> { cloneButton.isEnabled = false + retryButton.visibility = View.GONE statusText.text = getString(R.string.cloning_repo) } is CloneRepoUiState.Error -> { cloneButton.isEnabled = true + retryButton.visibility = View.VISIBLE val statusMessage = state.errorResId?.let { getString(it) } ?: state.errorMessage statusText.text = statusMessage } diff --git a/app/src/main/java/com/itsaky/androidide/viewmodel/CloneRepositoryViewModel.kt b/app/src/main/java/com/itsaky/androidide/viewmodel/CloneRepositoryViewModel.kt index f6379ddcb7..650652921a 100644 --- a/app/src/main/java/com/itsaky/androidide/viewmodel/CloneRepositoryViewModel.kt +++ b/app/src/main/java/com/itsaky/androidide/viewmodel/CloneRepositoryViewModel.kt @@ -4,7 +4,6 @@ import android.app.Application import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.application import androidx.lifecycle.viewModelScope -import com.itsaky.androidide.R import com.itsaky.androidide.git.core.GitRepositoryManager import com.itsaky.androidide.git.core.models.CloneRepoUiState import kotlinx.coroutines.Dispatchers @@ -16,7 +15,12 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.eclipse.jgit.lib.ProgressMonitor import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider +import org.eclipse.jgit.api.errors.TransportException +import java.net.UnknownHostException +import java.io.EOFException import java.io.File +import com.blankj.utilcode.util.NetworkUtils +import com.itsaky.androidide.resources.R class CloneRepositoryViewModel(application: Application) : AndroidViewModel(application) { @@ -67,6 +71,17 @@ class CloneRepositoryViewModel(application: Application) : AndroidViewModel(appl return } + if (!NetworkUtils.isConnected()) { + _uiState.update { + CloneRepoUiState.Error( + url = url, + localPath = localPath, + errorResId = R.string.no_internet_connection + ) + } + return + } + viewModelScope.launch { var hasCloned = false _uiState.update { @@ -143,12 +158,28 @@ class CloneRepositoryViewModel(application: Application) : AndroidViewModel(appl CloneRepoUiState.Success(localPath = localPath) } } catch (e: Exception) { - val errorMessage = e.message ?: application.getString(R.string.unknown_error) + // Error handling + val isNetworkError = e is TransportException && e.cause is UnknownHostException + val isConnectionDrop = e.cause is EOFException || + e.message?.contains("Unexpected end of stream") == true || + e.message?.contains("Software caused connection abort") == true + + val errorResId = when { + isNetworkError -> R.string.no_internet_connection + isConnectionDrop -> R.string.connection_lost + else -> null + } + + val errorMessage = if (errorResId == null) { + e.message ?: application.getString(R.string.unknown_error) + } else null + _uiState.update { CloneRepoUiState.Error( url = url, localPath = localPath, - errorMessage = application.getString(R.string.clone_failed, errorMessage) + errorResId = errorResId, + errorMessage = errorMessage?.let { application.getString(R.string.clone_failed, it) } ) } } finally { diff --git a/app/src/main/res/layout/fragment_clone_repository.xml b/app/src/main/res/layout/fragment_clone_repository.xml index 8b6b90b732..7b2e9e863a 100644 --- a/app/src/main/res/layout/fragment_clone_repository.xml +++ b/app/src/main/res/layout/fragment_clone_repository.xml @@ -1,156 +1,175 @@ - - - - + android:layout_margin="16dp"> - - - - - - - + + + android:layout_marginTop="12dp" + android:hint="@string/repository_url" + app:layout_constraintTop_toBottomOf="@id/tv_download_project"> - + - - - + - + android:layout_marginTop="16dp" + android:hint="@string/local_path" + app:endIconContentDescription="@string/pick_folder" + app:endIconDrawable="@drawable/ic_folder" + app:endIconMode="custom" + app:layout_constraintTop_toBottomOf="@id/repoUrlLayout"> + + + + + + + + - + - + - - - - - + android:layout_marginTop="16dp" + android:hint="@string/personal_access_token" + app:endIconMode="password_toggle" + app:layout_constraintTop_toBottomOf="@id/usernameLayout"> + + + + + + - - - - - + + + + + + + - + - - + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index e728ab5e68..563068d8d0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -19,10 +19,4 @@ Code on the Go Use simplified prompt - File added - File modified - File deleted - File untracked - File renamed - File conflicted diff --git a/resources/src/main/res/values/strings.xml b/resources/src/main/res/values/strings.xml index 5c286cf69e..ce66925024 100644 --- a/resources/src/main/res/values/strings.xml +++ b/resources/src/main/res/values/strings.xml @@ -1167,4 +1167,12 @@ Unable to load diff Diff Viewer Loading Git Diff… + No internet connection. Please check your network and try again. + Connection lost during download. Please check your internet connection and try again. + File added + File modified + File deleted + File untracked + File renamed + Conflicted File