Skip to content

Commit 0d4b29d

Browse files
authored
UI and ModelView improvements (#15)
1 parent 0abff76 commit 0d4b29d

File tree

3 files changed

+119
-67
lines changed

3 files changed

+119
-67
lines changed

app/src/main/res/layout/fragment_main.xml

Lines changed: 38 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,69 +11,77 @@
1111
<LinearLayout
1212
android:layout_width="match_parent"
1313
android:layout_height="match_parent"
14-
android:orientation="vertical">
14+
android:orientation="vertical"
15+
android:padding="16dp">
1516

1617
<TextView
1718
android:id="@+id/input_organisation_label"
1819
android:layout_width="wrap_content"
1920
android:layout_height="wrap_content"
20-
android:layout_marginBottom="8dp"
21-
android:text="Organisation" />
21+
android:layout_marginStart="4dp"
22+
android:layout_marginBottom="4dp"
23+
android:text="Organisation"
24+
android:textStyle="bold" />
2225

2326
<EditText
2427
android:id="@+id/input_organisation"
2528
android:layout_width="match_parent"
2629
android:layout_height="wrap_content"
27-
android:layout_margin="16dp"
30+
android:layout_marginBottom="16dp"
2831
android:hint="@string/enter_your_text_here"
2932
android:text="indoorvivants"
30-
android:inputType="text" />
33+
android:inputType="text"
34+
android:importantForAutofill="no" />
3135

3236
<TextView
3337
android:id="@+id/input_repository_label"
3438
android:layout_width="wrap_content"
3539
android:layout_height="wrap_content"
36-
android:layout_marginBottom="8dp"
37-
android:text="Repository" />
40+
android:layout_marginStart="4dp"
41+
android:layout_marginBottom="4dp"
42+
android:text="Repository"
43+
android:textStyle="bold" />
3844

3945
<EditText
4046
android:id="@+id/input_repository"
4147
android:layout_width="match_parent"
4248
android:layout_height="wrap_content"
43-
android:layout_margin="16dp"
49+
android:layout_marginBottom="16dp"
4450
android:hint="@string/enter_your_text_here"
4551
android:text="sn-bindgen"
46-
android:inputType="text" />
52+
android:inputType="text"
53+
android:importantForAutofill="no" />
4754

4855
<Button
4956
android:id="@+id/submit_button"
5057
android:layout_width="wrap_content"
5158
android:layout_height="wrap_content"
52-
android:layout_margin="16dp"
59+
android:layout_marginBottom="16dp"
5360
android:text="@string/lookup_on_scaladex" />
5461

55-
<!-- Styled TextView for Query Results -->
56-
<TextView
57-
android:id="@+id/query_result_text_view"
62+
<FrameLayout
5863
android:layout_width="match_parent"
59-
android:layout_height="wrap_content"
60-
android:background="#F0F0F0"
61-
android:padding="16dp"
62-
android:text="Query Result Will Appear Here"
63-
android:textColor="@android:color/black"
64-
android:textSize="18sp"
65-
android:textStyle="bold" />
64+
android:layout_height="wrap_content">
65+
66+
<ProgressBar
67+
android:id="@+id/progress_bar"
68+
android:layout_width="wrap_content"
69+
android:layout_height="wrap_content"
70+
android:layout_gravity="center"
71+
android:visibility="gone" />
6672

67-
<!-- <TextView-->
68-
<!-- android:id="@+id/message_text_view"-->
69-
<!-- android:layout_width="wrap_content"-->
70-
<!-- android:layout_height="wrap_content"-->
71-
<!-- android:textSize="24sp"-->
72-
<!-- app:layout_constraintBottom_toBottomOf="parent"-->
73-
<!-- app:layout_constraintEnd_toEndOf="parent"-->
74-
<!-- app:layout_constraintStart_toStartOf="parent"-->
75-
<!-- app:layout_constraintTop_toTopOf="parent" />-->
73+
<TextView
74+
android:id="@+id/query_result_text_view"
75+
android:layout_width="match_parent"
76+
android:layout_height="wrap_content"
77+
android:background="#F0F0F0"
78+
android:padding="16dp"
79+
android:text="Enter organization and repository to search"
80+
android:textColor="@android:color/black"
81+
android:textSize="16sp"
82+
android:minHeight="150dp" />
83+
</FrameLayout>
7684

7785
</LinearLayout>
7886

79-
</androidx.constraintlayout.widget.ConstraintLayout>
87+
</androidx.constraintlayout.widget.ConstraintLayout>

app/src/main/scala/com/example/scala_3_android_java/ui/main/MainFragment.scala

Lines changed: 50 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,27 @@ import android.view.View
88
import android.view.ViewGroup
99
import android.widget.Button
1010
import android.widget.EditText
11+
import android.widget.ProgressBar
1112
import android.widget.TextView
1213
import android.widget.Toast
13-
import com.example.core.Foo
1414
import com.example.scala_3_android_java.R
15-
import scala.util.{Left, Right}
16-
import java.util.concurrent.Executors
15+
import androidx.lifecycle.Observer
16+
import scala.compiletime.uninitialized
1717

1818
object MainFragment:
1919
def newInstance = MainFragment()
2020

2121
class MainFragment extends Fragment:
22-
private var mViewModel: MainViewModel = _
23-
// private var messageTextView: TextView = _
24-
private var inputOrgText: EditText = _
25-
private var inputRepoText: EditText = _
26-
private var submitButton: Button = _
27-
private var queryResultTextView: TextView = _
28-
private val executor = Executors.newSingleThreadExecutor()
22+
private var viewModel: MainViewModel = uninitialized
23+
private var inputOrgText: EditText = uninitialized
24+
private var inputRepoText: EditText = uninitialized
25+
private var submitButton: Button = uninitialized
26+
private var queryResultTextView: TextView = uninitialized
27+
private var progressBar: ProgressBar = uninitialized
2928

3029
override def onCreate(savedInstanceState: Bundle): Unit =
3130
super.onCreate(savedInstanceState)
32-
mViewModel = ViewModelProvider(this).get(classOf[MainViewModel])
33-
// TODO: Use the ViewModel
31+
viewModel = ViewModelProvider(this).get(classOf[MainViewModel])
3432

3533
override def onCreateView(inflater: LayoutInflater,
3634
container: ViewGroup,
@@ -39,41 +37,58 @@ class MainFragment extends Fragment:
3937

4038
override def onViewCreated(view: View, savedInstanceState: Bundle): Unit =
4139
super.onViewCreated(view, savedInstanceState)
40+
4241
// Find view references
4342
// messageTextView = view.findViewById(R.id.message_text_view)
4443
inputOrgText = view.findViewById(R.id.input_organisation)
4544
inputRepoText = view.findViewById(R.id.input_repository)
4645
submitButton = view.findViewById(R.id.submit_button)
4746
queryResultTextView = view.findViewById(R.id.query_result_text_view)
47+
progressBar = view.findViewById(R.id.progress_bar)
48+
4849
// // Observe the LiveData from ViewModel
49-
// mViewModel.getMessage.observe(getViewLifecycleOwner, (message: String) =>
50+
// viewModel.getMessage.observe(getViewLifecycleOwner, (message: String) =>
5051
// // Update the UI when data changes
5152
// messageTextView.setText("\n" + message)
5253
// )
54+
55+
// Observe the ProjectInfoState LiveData
56+
viewModel.getProjectInfoState.observe(getViewLifecycleOwner, {
57+
case Initial =>
58+
progressBar.setVisibility(View.GONE)
59+
queryResultTextView.setText("Enter organization and repository to search")
60+
61+
case Loading =>
62+
progressBar.setVisibility(View.VISIBLE)
63+
queryResultTextView.setText("Loading...")
64+
65+
case Error(message) =>
66+
progressBar.setVisibility(View.GONE)
67+
queryResultTextView.setText("")
68+
Toast.makeText(getContext, s"An error occurred: $message", Toast.LENGTH_LONG).show()
69+
70+
case Success(projectInfo) =>
71+
progressBar.setVisibility(View.GONE)
72+
queryResultTextView.setText(
73+
s"""
74+
|Project: ${inputOrgText.getText}/${inputRepoText.getText}
75+
|Description: ${projectInfo.description}
76+
|Stars: ${projectInfo.stars}
77+
|${if projectInfo.topic.isEmpty then "" else s"Topics: ${projectInfo.topic.mkString(", ")}"}
78+
""".stripMargin)
79+
})
80+
5381
// Set up the button click listener
5482
submitButton.setOnClickListener(new View.OnClickListener() {
5583
override def onClick(v: View): Unit =
5684
// Get the input from the EditText
57-
val org = inputOrgText.getText.toString
58-
val repo = inputRepoText.getText.toString
59-
// Execute side effect on a background thread
60-
executor.execute(() =>
61-
val result = Foo.getProjectInfo(org, repo)
62-
// Update UI on the main thread
63-
getActivity.runOnUiThread(() =>
64-
result match
65-
case Left(error) =>
66-
queryResultTextView.setText("")
67-
// Optionally, you can show a Toast with the error
68-
Toast.makeText(getContext, s"An error occurred: $error", Toast.LENGTH_LONG).show()
69-
case Right(projectInfo) =>
70-
queryResultTextView.setText(s"${projectInfo.description}, stars: ${projectInfo.stars}")
71-
)
72-
)
73-
// Here you can send your user input to the ViewModel to update the data
74-
// mViewModel.processInput(userInput)
75-
})
85+
val org = inputOrgText.getText.toString.trim
86+
val repo = inputRepoText.getText.toString.trim
7687

77-
override def onDestroyView(): Unit =
78-
super.onDestroyView()
79-
executor.shutdown()
88+
// Validate input
89+
if org.isEmpty || repo.isEmpty then
90+
Toast.makeText(getContext, "Please enter both organization and repository", Toast.LENGTH_SHORT).show()
91+
else
92+
// Use the ViewModel to fetch project info
93+
viewModel.fetchProjectInfo(org, repo)
94+
})

app/src/main/scala/com/example/scala_3_android_java/ui/main/MainViewModel.scala

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,44 @@ package com.example.scala_3_android_java.ui.main
33
import androidx.lifecycle.LiveData
44
import androidx.lifecycle.MutableLiveData
55
import androidx.lifecycle.ViewModel
6+
import com.example.core.{Description, Foo}
7+
import java.util.concurrent.Executors
8+
9+
// Sealed trait for UI state
10+
sealed trait ProjectInfoState
11+
case object Loading extends ProjectInfoState
12+
case class Error(message: String) extends ProjectInfoState
13+
case class Success(projectInfo: Description) extends ProjectInfoState
14+
case object Initial extends ProjectInfoState
615

716
class MainViewModel extends ViewModel:
817
private val message = new MutableLiveData[String]
9-
// Initialize with Hello World message
18+
private val projectInfoState = new MutableLiveData[ProjectInfoState]
19+
private val executor = Executors.newSingleThreadExecutor()
20+
21+
// Initialize with Hello World message and Initial state
1022
message.setValue("Hello, Scala 3 Android!")
23+
projectInfoState.setValue(Initial)
1124

12-
// Getter for the LiveData
25+
// Getters for the LiveData
1326
def getMessage: LiveData[String] = message
27+
def getProjectInfoState: LiveData[ProjectInfoState] = projectInfoState
1428

1529
// Method to update the message
1630
def setMessage(newMessage: String): Unit =
1731
message.setValue(newMessage)
32+
33+
// Method to fetch project information
34+
def fetchProjectInfo(org: String, repo: String): Unit =
35+
projectInfoState.setValue(Loading)
36+
executor.execute(() =>
37+
val state =
38+
Foo.getProjectInfo(org, repo) match
39+
case Left(error) => Error(error)
40+
case Right(projectInfo) => Success(projectInfo)
41+
projectInfoState.postValue(state)
42+
)
43+
44+
override def onCleared(): Unit =
45+
super.onCleared()
46+
executor.shutdown()

0 commit comments

Comments
 (0)