Skip to content

Commit 6647bc2

Browse files
committed
Merge remote-tracking branch 'origin/stage' into feat/ADFA-3125-filetree-drag-and-drop
# Conflicts: # app/src/main/java/com/itsaky/androidide/dnd/DropTargetCallback.kt # git-core/src/main/java/com/itsaky/androidide/git/core/GitRepositoryUrls.kt # resources/src/main/res/values/strings.xml
2 parents 4a3dfd9 + cb05a7d commit 6647bc2

File tree

18 files changed

+773
-67
lines changed

18 files changed

+773
-67
lines changed

app/src/main/java/com/itsaky/androidide/di/AppModule.kt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,15 @@ import com.itsaky.androidide.agent.GeminiMacroProcessor
66
import com.itsaky.androidide.agent.viewmodel.ChatViewModel
77
import com.itsaky.androidide.analytics.AnalyticsManager
88
import com.itsaky.androidide.analytics.IAnalyticsManager
9+
import com.itsaky.androidide.git.core.GitCredentialsManager
910
import com.itsaky.androidide.roomData.recentproject.RecentProjectRoomDatabase
1011
import com.itsaky.androidide.viewmodel.GitBottomSheetViewModel
1112
import com.itsaky.androidide.viewmodel.MainViewModel
1213
import kotlinx.coroutines.CoroutineScope
1314
import kotlinx.coroutines.Dispatchers
1415
import kotlinx.coroutines.SupervisorJob
1516
import org.koin.android.ext.koin.androidApplication
17+
import org.koin.android.ext.koin.androidContext
1618
import org.koin.dsl.module
1719
import org.koin.core.module.dsl.viewModel
1820

@@ -28,8 +30,10 @@ val coreModule =
2830
ChatViewModel()
2931
}
3032
viewModel {
31-
GitBottomSheetViewModel()
33+
GitBottomSheetViewModel(get())
3234
}
35+
viewModel { MainViewModel(get()) }
36+
3337

3438
single<CoroutineScope> {
3539
CoroutineScope(SupervisorJob() + Dispatchers.IO)
@@ -43,5 +47,6 @@ val coreModule =
4347
get<RecentProjectRoomDatabase>().recentProjectDao()
4448
}
4549

46-
viewModel { MainViewModel(get()) }
50+
single { GitCredentialsManager(get()) }
51+
4752
}

app/src/main/java/com/itsaky/androidide/fragments/git/GitBottomSheetFragment.kt

Lines changed: 93 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,36 @@ import android.view.View
1010
import android.widget.TextView
1111
import androidx.core.widget.doAfterTextChanged
1212
import androidx.fragment.app.Fragment
13-
import androidx.fragment.app.activityViewModels
1413
import androidx.lifecycle.lifecycleScope
1514
import androidx.recyclerview.widget.LinearLayoutManager
1615
import com.google.android.material.dialog.MaterialAlertDialogBuilder
1716
import com.itsaky.androidide.R
1817
import com.itsaky.androidide.activities.PreferencesActivity
18+
import com.itsaky.androidide.databinding.DialogGitCredentialsBinding
1919
import com.itsaky.androidide.databinding.FragmentGitBottomSheetBinding
2020
import com.itsaky.androidide.fragments.git.adapter.GitFileChangeAdapter
21+
import com.itsaky.androidide.git.core.GitCredentialsManager
2122
import com.itsaky.androidide.preferences.internal.GitPreferences
23+
import com.itsaky.androidide.utils.flashSuccess
2224
import com.itsaky.androidide.viewmodel.GitBottomSheetViewModel
2325
import kotlinx.coroutines.flow.collectLatest
2426
import kotlinx.coroutines.flow.combine
2527
import kotlinx.coroutines.launch
28+
import org.koin.androidx.viewmodel.ext.android.activityViewModel
2629

2730
class GitBottomSheetFragment : Fragment(R.layout.fragment_git_bottom_sheet) {
2831

29-
private val viewModel: GitBottomSheetViewModel by activityViewModels()
32+
private val viewModel: GitBottomSheetViewModel by activityViewModel()
3033
private lateinit var fileChangeAdapter: GitFileChangeAdapter
34+
private lateinit var credentialsManager: GitCredentialsManager
3135

3236
private var _binding: FragmentGitBottomSheetBinding? = null
3337
private val binding get() = _binding!!
3438

3539
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
3640
super.onViewCreated(view, savedInstanceState)
3741
_binding = FragmentGitBottomSheetBinding.bind(view)
42+
credentialsManager = GitCredentialsManager(requireContext())
3843

3944
fileChangeAdapter = GitFileChangeAdapter(
4045
onFileClicked = { change ->
@@ -51,6 +56,17 @@ class GitBottomSheetFragment : Fragment(R.layout.fragment_git_bottom_sheet) {
5156
binding.recyclerView.adapter = fileChangeAdapter
5257

5358
viewLifecycleOwner.lifecycleScope.launch {
59+
launch {
60+
viewModel.currentBranch.collectLatest { branchName ->
61+
if (branchName != null) {
62+
binding.tvBranchName.visibility = View.VISIBLE
63+
binding.tvBranchName.text = getString(R.string.current_branch_name, branchName)
64+
} else {
65+
binding.tvBranchName.visibility = View.GONE
66+
}
67+
}
68+
}
69+
5470
combine(
5571
viewModel.isGitRepository,
5672
viewModel.gitStatus
@@ -95,6 +111,7 @@ class GitBottomSheetFragment : Fragment(R.layout.fragment_git_bottom_sheet) {
95111
dialog.show(childFragmentManager, "CommitHistoryDialog")
96112
}
97113

114+
setupPullUI()
98115
}
99116

100117
override fun onResume() {
@@ -187,6 +204,80 @@ class GitBottomSheetFragment : Fragment(R.layout.fragment_git_bottom_sheet) {
187204
binding.commitButton.isEnabled = hasSummary && hasSelection && hasAuthor
188205
}
189206

207+
private fun setupPullUI() {
208+
viewLifecycleOwner.lifecycleScope.launch {
209+
viewModel.isGitRepository.collectLatest { isRepo ->
210+
binding.btnPull.visibility = if (isRepo) View.VISIBLE else View.GONE
211+
}
212+
}
213+
214+
viewLifecycleOwner.lifecycleScope.launch {
215+
viewModel.pullState.collectLatest { state ->
216+
when (state) {
217+
is GitBottomSheetViewModel.PullUiState.Idle -> {
218+
binding.btnPull.isEnabled = true
219+
binding.pullProgress.visibility = View.GONE
220+
}
221+
is GitBottomSheetViewModel.PullUiState.Pulling -> {
222+
binding.btnPull.isEnabled = false
223+
binding.pullProgress.visibility = View.VISIBLE
224+
}
225+
is GitBottomSheetViewModel.PullUiState.Success -> {
226+
binding.btnPull.isEnabled = true
227+
binding.pullProgress.visibility = View.GONE
228+
flashSuccess(R.string.pull_successful)
229+
viewModel.resetPullState()
230+
}
231+
is GitBottomSheetViewModel.PullUiState.Error -> {
232+
binding.btnPull.isEnabled = true
233+
binding.pullProgress.visibility = View.GONE
234+
val message =
235+
state.errorResId?.let { getString(it) } ?: state.message
236+
MaterialAlertDialogBuilder(requireContext())
237+
.setTitle(R.string.pull_failed)
238+
.setMessage(message)
239+
.setPositiveButton(android.R.string.ok, null)
240+
.show()
241+
}
242+
}
243+
}
244+
}
245+
246+
binding.btnPull.setOnClickListener {
247+
val username = credentialsManager.getUsername()
248+
val token = credentialsManager.getToken()
249+
if (!username.isNullOrBlank() && !token.isNullOrBlank()) {
250+
viewModel.pull(username, token)
251+
} else {
252+
showCredentialsDialog()
253+
}
254+
}
255+
}
256+
257+
private fun showCredentialsDialog() {
258+
val context = requireContext()
259+
val dialogBinding = DialogGitCredentialsBinding.inflate(layoutInflater)
260+
261+
dialogBinding.username.setText(credentialsManager.getUsername())
262+
dialogBinding.token.setText(credentialsManager.getToken())
263+
264+
MaterialAlertDialogBuilder(context)
265+
.setTitle(R.string.git_credentials_title)
266+
.setView(dialogBinding.root)
267+
.setPositiveButton(R.string.pull) { _, _ ->
268+
val username = dialogBinding.username.text?.toString()?.trim()
269+
val token = dialogBinding.token.text?.toString()?.trim()
270+
if (!username.isNullOrBlank() && !token.isNullOrBlank()) {
271+
viewModel.pull(username, token)
272+
}
273+
}
274+
.setNeutralButton(R.string.git_credentials_clear) { _, _ ->
275+
credentialsManager.clearCredentials()
276+
}
277+
.setNegativeButton(android.R.string.cancel, null)
278+
.show()
279+
}
280+
190281
override fun onDestroyView() {
191282
super.onDestroyView()
192283
_binding = null

app/src/main/java/com/itsaky/androidide/fragments/git/GitCommitHistoryDialog.kt

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,30 @@ import android.view.LayoutInflater
55
import android.view.View
66
import android.view.ViewGroup
77
import androidx.fragment.app.DialogFragment
8-
import androidx.fragment.app.activityViewModels
98
import androidx.lifecycle.lifecycleScope
109
import androidx.recyclerview.widget.DividerItemDecoration
1110
import androidx.recyclerview.widget.LinearLayoutManager
11+
import com.google.android.material.dialog.MaterialAlertDialogBuilder
1212
import com.itsaky.androidide.R
1313
import com.itsaky.androidide.databinding.DialogGitCommitHistoryBinding
14+
import com.itsaky.androidide.databinding.DialogGitCredentialsBinding
1415
import com.itsaky.androidide.fragments.git.adapter.GitCommitHistoryAdapter
16+
import com.itsaky.androidide.git.core.GitCredentialsManager
1517
import com.itsaky.androidide.git.core.models.CommitHistoryUiState
18+
import com.itsaky.androidide.utils.flashSuccess
1619
import com.itsaky.androidide.viewmodel.GitBottomSheetViewModel
1720
import kotlinx.coroutines.flow.collectLatest
1821
import kotlinx.coroutines.launch
22+
import org.koin.android.ext.android.inject
23+
import org.koin.androidx.viewmodel.ext.android.activityViewModel
1924

2025
class GitCommitHistoryDialog : DialogFragment() {
2126

2227
private var _binding: DialogGitCommitHistoryBinding? = null
2328
private val binding get() = _binding!!
24-
private val viewModel: GitBottomSheetViewModel by activityViewModels()
29+
private val viewModel: GitBottomSheetViewModel by activityViewModel()
2530
private lateinit var commitHistoryAdapter: GitCommitHistoryAdapter
31+
private val credentialsManager: GitCredentialsManager by inject()
2632

2733
override fun onCreate(savedInstanceState: Bundle?) {
2834
super.onCreate(savedInstanceState)
@@ -83,6 +89,84 @@ class GitCommitHistoryDialog : DialogFragment() {
8389
}
8490
}
8591
}
92+
93+
setupPushUI()
94+
}
95+
96+
private fun setupPushUI() {
97+
binding.btnPush.setOnClickListener {
98+
val username = credentialsManager.getUsername()
99+
val token = credentialsManager.getToken()
100+
if (!username.isNullOrBlank() && !token.isNullOrBlank()) {
101+
viewModel.push(username, token)
102+
} else {
103+
showCredentialsDialog()
104+
}
105+
}
106+
107+
viewLifecycleOwner.lifecycleScope.launch {
108+
viewModel.localCommitsCount.collectLatest { count ->
109+
binding.btnPush.visibility = if (count > 0) View.VISIBLE else View.GONE
110+
}
111+
}
112+
113+
viewLifecycleOwner.lifecycleScope.launch {
114+
viewModel.pushState.collectLatest { state ->
115+
when (state) {
116+
is GitBottomSheetViewModel.PushUiState.Idle -> {
117+
binding.btnPush.isEnabled = true
118+
binding.btnPush.text = getString(R.string.push)
119+
binding.pushProgress.visibility = View.GONE
120+
}
121+
is GitBottomSheetViewModel.PushUiState.Pushing -> {
122+
binding.btnPush.isEnabled = false
123+
binding.pushProgress.visibility = View.VISIBLE
124+
}
125+
is GitBottomSheetViewModel.PushUiState.Success -> {
126+
binding.btnPush.isEnabled = true
127+
binding.pushProgress.visibility = View.GONE
128+
flashSuccess(R.string.push_successful)
129+
viewModel.resetPushState()
130+
dismiss()
131+
}
132+
is GitBottomSheetViewModel.PushUiState.Error -> {
133+
binding.btnPush.isEnabled = true
134+
binding.pushProgress.visibility = View.GONE
135+
val message =
136+
state.errorResId?.let { getString(it) } ?: state.message
137+
MaterialAlertDialogBuilder(requireContext())
138+
.setTitle(R.string.push_failed)
139+
.setMessage(message)
140+
.setPositiveButton(android.R.string.ok, null)
141+
.show()
142+
}
143+
}
144+
}
145+
}
146+
147+
}
148+
149+
private fun showCredentialsDialog() {
150+
val dialogBinding = DialogGitCredentialsBinding.inflate(layoutInflater)
151+
152+
dialogBinding.username.setText(credentialsManager.getUsername())
153+
dialogBinding.token.setText(credentialsManager.getToken())
154+
155+
MaterialAlertDialogBuilder(requireContext())
156+
.setTitle(R.string.git_credentials_title)
157+
.setView(dialogBinding.root)
158+
.setPositiveButton(R.string.push) { _, _ ->
159+
val username = dialogBinding.username.text?.toString()?.trim()
160+
val token = dialogBinding.token.text?.toString()?.trim()
161+
if (!username.isNullOrBlank() && !token.isNullOrBlank()) {
162+
viewModel.push(username, token)
163+
}
164+
}
165+
.setNeutralButton(R.string.git_credentials_clear) { _, _ ->
166+
credentialsManager.clearCredentials()
167+
}
168+
.setNegativeButton(android.R.string.cancel, null)
169+
.show()
86170
}
87171

88172
override fun onDestroyView() {

app/src/main/java/com/itsaky/androidide/fragments/git/GitDiffViewerDialog.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,19 @@ import android.view.View
1212
import android.view.ViewGroup
1313
import androidx.core.content.ContextCompat.getColor
1414
import androidx.fragment.app.DialogFragment
15-
import androidx.fragment.app.activityViewModels
1615
import androidx.lifecycle.lifecycleScope
1716
import com.itsaky.androidide.R
1817
import com.itsaky.androidide.databinding.DialogGitDiffBinding
1918
import com.itsaky.androidide.viewmodel.GitBottomSheetViewModel
2019
import kotlinx.coroutines.Dispatchers
2120
import kotlinx.coroutines.launch
2221
import kotlinx.coroutines.withContext
22+
import org.koin.androidx.viewmodel.ext.android.activityViewModel
2323
import java.io.File
2424

2525
class GitDiffViewerDialog : DialogFragment() {
2626

27-
private val viewModel: GitBottomSheetViewModel by activityViewModels()
27+
private val viewModel: GitBottomSheetViewModel by activityViewModel()
2828

2929
private var filePath: String = ""
3030

app/src/main/java/com/itsaky/androidide/fragments/git/adapter/GitCommitHistoryAdapter.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,8 @@ class GitCommitHistoryAdapter :
3838
tvCommitMessage.text = commit.message
3939
tvCommitAuthor.text = commit.authorName
4040
tvCommitTime.text = dateFormat.format(Date(commit.timestamp))
41-
imgNotPushedCommit.setImageResource(if (commit.hasBeenPushed) R.drawable.ic_cloud_done else R.drawable.ic_upload)
42-
imgNotPushedCommit.contentDescription = if (commit.hasBeenPushed) {
41+
imgCommitStatus.setImageResource(if (commit.hasBeenPushed) R.drawable.ic_cloud_done else R.drawable.ic_upload)
42+
imgCommitStatus.contentDescription = if (commit.hasBeenPushed) {
4343
binding.root.context.getString(R.string.commit_pushed)
4444
} else {
4545
binding.root.context.getString(R.string.commit_not_pushed)

0 commit comments

Comments
 (0)