Skip to content
This repository was archived by the owner on Jul 2, 2025. It is now read-only.

Commit 62eb7fe

Browse files
ListView
1 parent c88db07 commit 62eb7fe

File tree

6 files changed

+159
-38
lines changed

6 files changed

+159
-38
lines changed

android/canonical/app/src/main/java/com/google/samples/quickstart/canonical/ProfileFragment.kt

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import android.view.LayoutInflater
77
import android.view.View
88
import android.view.ViewGroup
99
import android.widget.Button
10+
import android.widget.ListView
1011
import androidx.fragment.app.Fragment
1112
import androidx.fragment.app.activityViewModels
1213
import androidx.lifecycle.Observer
@@ -21,6 +22,23 @@ class ProfileFragment : Fragment() {
2122
private lateinit var observer : Observer<Boolean>
2223
private lateinit var binding : FragmentProfileBinding
2324

25+
private fun downloadPhotoAndSetView(view: View) {
26+
val url = profileVM.getUserPhotoURL()
27+
if (url != "") {
28+
Log.d(PROFILE_TAG, url)
29+
DownloadImageTask(view.findViewById(R.id.usr_img))
30+
.execute(url)
31+
}
32+
}
33+
34+
private fun setRunHistory() {
35+
val runHistoryListForView : ArrayList<ProfileViewModel.SingleRun>
36+
= profileVM.getRunHistoryListForView()
37+
val runHistoryAdapter = RunHistoryAdapter(requireContext(), runHistoryListForView)
38+
val runHistoryListView = view?.findViewById<ListView>(R.id.run_history_list_view)
39+
runHistoryListView?.adapter = runHistoryAdapter
40+
}
41+
2442
override fun onCreate(savedInstanceState: Bundle?) {
2543
super.onCreate(savedInstanceState)
2644
// Add login status change listener.
@@ -41,6 +59,7 @@ class ProfileFragment : Fragment() {
4159
}
4260
// set LifeCycle owner with MeFragment. Observe will be destroyed when MeFragment is destroyed
4361
signInVM.getFirebaseAuthLogStatusLiveData().observe(this, observer)
62+
4463
}
4564

4665
override fun onCreateView(
@@ -63,15 +82,7 @@ class ProfileFragment : Fragment() {
6382
}
6483
// update UI
6584
downloadPhotoAndSetView(view)
66-
}
67-
68-
private fun downloadPhotoAndSetView(view: View) {
69-
val url = profileVM.getUserPhotoURL()
70-
if (url != "") {
71-
Log.d(PROFILE_TAG, url)
72-
DownloadImageTask(view.findViewById(R.id.usr_img))
73-
.execute(url)
74-
}
85+
setRunHistory()
7586
}
7687

7788
companion object {

android/canonical/app/src/main/java/com/google/samples/quickstart/canonical/ProfileViewModel.kt

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import android.widget.ImageView
88
import androidx.lifecycle.MutableLiveData
99
import androidx.lifecycle.ViewModel
1010
import com.google.firebase.auth.FirebaseUser
11+
import com.google.firebase.firestore.CollectionReference
1112
import com.google.firebase.firestore.DocumentReference
1213
import com.google.firebase.firestore.FieldValue
1314
import com.google.firebase.firestore.ktx.firestore
@@ -26,12 +27,19 @@ class ProfileViewModel : ViewModel() {
2627
var totalDistanceMeters : MutableLiveData<Long> = MutableLiveData(0),
2728
var totalEnergyCalories : MutableLiveData<Long> = MutableLiveData(0),
2829
var totalTimeMillisecond : MutableLiveData<Long> = MutableLiveData(0),
29-
var singleRunIDList : MutableLiveData<ArrayList<String>> = MutableLiveData(ArrayList())
30+
var runHistoryList : ArrayList<HashMap<String, Any>> =ArrayList()
3031
)
3132

33+
data class SingleRun (
34+
var time : String,
35+
var dateTime : String
36+
)
37+
38+
private lateinit var runUserDocRef : DocumentReference
39+
private lateinit var runCollectionRef : CollectionReference
3240
private lateinit var curAppUser: AppUser
41+
private var runHistoryListForView: ArrayList<SingleRun> = ArrayList()
3342
private var timeHMSString: MutableLiveData<String> = MutableLiveData(DEFAULT_TIME)
34-
private lateinit var runUserDocRef : DocumentReference
3543

3644
private fun getUid() : String {
3745
return curAppUser.uid
@@ -41,6 +49,12 @@ class ProfileViewModel : ViewModel() {
4149
return curAppUser.totalTimeMillisecond.value
4250
}
4351

52+
private fun getRunHistoryList() : ArrayList<HashMap<String, Any>> {
53+
val reversedRunHistory = curAppUser.runHistoryList
54+
reversedRunHistory.reverse()
55+
return reversedRunHistory
56+
}
57+
4458
private fun setTotalDistanceMeters(totalDistanceMeters : Long) {
4559
curAppUser.totalDistanceMeters.value = totalDistanceMeters
4660
}
@@ -53,8 +67,18 @@ class ProfileViewModel : ViewModel() {
5367
curAppUser.totalTimeMillisecond.value = totalTimeMillisecond
5468
}
5569

56-
private fun setSingleRunIDList(singleRunIDList : ArrayList<String>) {
57-
curAppUser.singleRunIDList.value = singleRunIDList
70+
private fun setRunHistoryList(runHistoryList : ArrayList<HashMap<String, Any>>) {
71+
curAppUser.runHistoryList = runHistoryList
72+
}
73+
74+
private fun setRunHistoryListForView() {
75+
runHistoryListForView.clear()
76+
val runHistoryListOfHashMap = getRunHistoryList()
77+
for (singleRun in runHistoryListOfHashMap) {
78+
val singleRunTime : String = convertMStoStringHMS(singleRun[KEY_SINGLE_RUN_TIME] as Long)
79+
val singleRunTimestamp : String = singleRun[KEY_SINGLE_RUN_TIMESTAMP] as String
80+
runHistoryListForView.add(SingleRun(singleRunTime, singleRunTimestamp))
81+
}
5882
}
5983

6084
private fun convertMStoStringHMS(millionSeconds : Long) : String {
@@ -72,9 +96,6 @@ class ProfileViewModel : ViewModel() {
7296
Log.d(PROFILE_VM_TAG, "setTimeHMSString hms: $hms")
7397
}
7498

75-
private fun getNewSingleRunningRecordRef(singleRunningTimeMillionSeconds : Long, time: String) {
76-
77-
}
7899

79100
private fun syncAppUserStatistic() {
80101
runUserDocRef.get()
@@ -83,7 +104,8 @@ class ProfileViewModel : ViewModel() {
83104
setTotalDistanceMeters(document.data!![KEY_TOTAL_DIS_M] as Long)
84105
setTotalEnergyCalories(document.data!![KEY_TOTAL_EN_CAL] as Long)
85106
setTotalTimeMillisecond(document.data!![KEY_TOTAL_TIME_MS] as Long)
86-
setSingleRunIDList(document.data!![KEY_SINGLE_RUN_ID_LIST] as ArrayList<String>)
107+
setRunHistoryList(document.data!![KEY_RUN_HISTORY] as ArrayList<HashMap<String, Any>>)
108+
setRunHistoryListForView()
87109
setTimeHMSString()
88110
}
89111
.addOnFailureListener {
@@ -107,11 +129,20 @@ class ProfileViewModel : ViewModel() {
107129
return timeHMSString
108130
}
109131

132+
fun getTotalEnergyCaloriesMutableLiveData() : MutableLiveData<Long> {
133+
return curAppUser.totalEnergyCalories
134+
}
135+
136+
fun getRunHistoryListForView(): ArrayList<SingleRun> {
137+
return runHistoryListForView
138+
}
139+
110140
fun initAppUser(curFirebaseUser : FirebaseUser) {
111141
Log.d(PROFILE_VM_TAG, "initAppUser")
112142
curAppUser = AppUser(curFirebaseUser.displayName ?: "", curFirebaseUser.email ?: "",
113143
curFirebaseUser.uid, curFirebaseUser.photoUrl.toString())
114144
runUserDocRef = Firebase.firestore.collection(USER_COLLECTION_NAME).document(getUid())
145+
runCollectionRef = Firebase.firestore.collection(RUN_COLLECTION_NAME)
115146
syncAppUserStatistic()
116147
}
117148

@@ -122,41 +153,42 @@ class ProfileViewModel : ViewModel() {
122153
)
123154
Log.d(PROFILE_VM_TAG, "newTotalTimeMillisecond $newTotalTimeMillisecond")
124155

125-
val singleRunDocRef = Firebase.firestore.collection(RUN_COLLECTION_NAME).document()
126156
val singleRunData = hashMapOf(
127157
KEY_SINGLE_RUN_TIME to singleRunningTimeMillionSeconds,
128158
KEY_SINGLE_RUN_TIMESTAMP to timestamp
129159
)
160+
130161
val updateRunUserData = hashMapOf(
131162
KEY_TOTAL_TIME_MS to newTotalTimeMillisecond,
132-
KEY_SINGLE_RUN_ID_LIST to FieldValue.arrayUnion(singleRunDocRef.id)
163+
KEY_TOTAL_EN_CAL to (newTotalTimeMillisecond?.div(10000) ?: 0),
164+
KEY_RUN_HISTORY to FieldValue.arrayUnion(singleRunData)
133165
)
134166

135-
Firebase.firestore.runBatch { batch ->
136-
// Create single run record
137-
batch.set(singleRunDocRef, singleRunData)
138167

139-
// update user statistics
168+
Firebase.firestore.runBatch { batch ->
140169
batch.update(runUserDocRef, updateRunUserData)
141170

142171
}
143172
.addOnSuccessListener {
144173
Log.d(PROFILE_VM_TAG, "Upload record successfully")
145174
syncAppUserStatistic()
175+
// No need to call Adapter.notifyDataSetChanged()
176+
// Each time we access profile page, just create a new one.
146177
}
147178
}
148179

149180

150181
companion object {
151182
const val PROFILE_VM_TAG = "ProfileVM"
183+
152184
const val USER_COLLECTION_NAME = "RunUser"
153185
const val RUN_COLLECTION_NAME = "SingleRun"
154186
const val KEY_USR_NAME = "UserName"
155187
const val KEY_USR_EMAIL = "Email"
156188
const val KEY_TOTAL_DIS_M = "TotalDistanceMeters"
157189
const val KEY_TOTAL_EN_CAL = "TotalEnergyCalories"
158190
const val KEY_TOTAL_TIME_MS = "TotalTimeMillisecond"
159-
const val KEY_SINGLE_RUN_ID_LIST = "SingleRunIDList"
191+
const val KEY_RUN_HISTORY = "RunHistory"
160192

161193
const val KEY_SINGLE_RUN_TIME = "Time"
162194
const val KEY_SINGLE_RUN_TIMESTAMP = "Timestamp"
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.google.samples.quickstart.canonical
2+
3+
import android.content.Context
4+
import android.view.LayoutInflater
5+
import android.view.View
6+
import android.view.ViewGroup
7+
import android.widget.BaseAdapter
8+
import android.widget.TextView
9+
10+
11+
class RunHistoryAdapter(
12+
context: Context,
13+
private val runHistoryArrayList: ArrayList<ProfileViewModel.SingleRun>) : BaseAdapter() {
14+
15+
private val inflater: LayoutInflater
16+
= context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
17+
18+
override fun getCount(): Int {
19+
return runHistoryArrayList.size
20+
}
21+
22+
override fun getItem(position: Int): Any {
23+
return runHistoryArrayList[position]
24+
}
25+
26+
override fun getItemId(position: Int): Long {
27+
return position.toLong()
28+
}
29+
30+
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
31+
// Get view for row item
32+
val rowView = convertView ?: inflater.inflate(R.layout.single_run_item, parent, false)
33+
val timeTextView = rowView.findViewById<TextView>(R.id.single_run_time)
34+
val datetimeTextView = rowView.findViewById<TextView>(R.id.single_run_datetime)
35+
36+
val singleRun = getItem(position) as ProfileViewModel.SingleRun
37+
timeTextView.text = singleRun.time
38+
datetimeTextView.text = singleRun.dateTime
39+
40+
return rowView
41+
}
42+
43+
}

android/canonical/app/src/main/java/com/google/samples/quickstart/canonical/SignInViewModel.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ class SignInViewModel : ViewModel() {
7979
ProfileViewModel.KEY_TOTAL_DIS_M to 0L,
8080
ProfileViewModel.KEY_TOTAL_EN_CAL to 0L,
8181
ProfileViewModel.KEY_TOTAL_TIME_MS to 0L,
82-
ProfileViewModel.KEY_SINGLE_RUN_ID_LIST to arrayListOf<String>()
82+
ProfileViewModel.KEY_RUN_HISTORY to arrayListOf<HashMap<String, Any>>()
8383
)
8484
val ref = db.collection(ProfileViewModel.USER_COLLECTION_NAME).document(firebaseUser.uid)
8585
ref.get()

android/canonical/app/src/main/res/layout/fragment_profile.xml

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@
6464
android:textColor="@android:color/black"
6565
android:textSize="14sp" />
6666

67+
<Button
68+
android:id="@+id/logout_button"
69+
android:layout_width="wrap_content"
70+
android:layout_height="wrap_content"
71+
android:layout_marginBottom="16dp"
72+
android:text="@string/logout_button_text" />
73+
6774
</RelativeLayout>
6875

6976
<LinearLayout
@@ -132,7 +139,7 @@
132139
android:layout_marginBottom="5dp"
133140
android:gravity="center"
134141
android:fontFamily="sans-serif-light"
135-
android:text="@string/usr_run_energy"
142+
android:text="@{profileViewModel.getTimeHMSStringMutableLiveData()}"
136143
android:textColor="@android:color/white"
137144
android:textSize="30sp" />
138145

@@ -152,16 +159,12 @@
152159

153160
</LinearLayout>
154161

155-
<Button
156-
android:id="@+id/logout_button"
157-
android:layout_width="wrap_content"
158-
android:layout_height="wrap_content"
159-
android:layout_marginBottom="20dp"
160-
android:text="@string/logout_button_text"
161-
app:layout_constraintBottom_toBottomOf="parent"
162-
app:layout_constraintEnd_toEndOf="parent"
163-
app:layout_constraintStart_toStartOf="parent"
164-
/>
165-
166-
</androidx.constraintlayout.widget.ConstraintLayout>
162+
<ListView
163+
android:id="@+id/run_history_list_view"
164+
android:layout_width="match_parent"
165+
android:layout_height="0dp"
166+
app:layout_constraintVertical_weight="1"
167+
app:layout_constraintTop_toBottomOf="@+id/usr_statistic_bar"/>
168+
169+
</androidx.constraintlayout.widget.ConstraintLayout>
167170
</layout>
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<LinearLayout
3+
xmlns:android="http://schemas.android.com/apk/res/android"
4+
android:layout_width="match_parent"
5+
android:layout_height="wrap_content"
6+
android:layout_margin="30dp"
7+
android:orientation="horizontal">
8+
9+
<TextView
10+
android:id="@+id/single_run_datetime"
11+
android:layout_width="match_parent"
12+
android:layout_height="match_parent"
13+
android:layout_weight="1"
14+
android:layout_margin="10dp"
15+
android:fontFamily="sans-serif-light"
16+
android:gravity="center"
17+
android:text="2020-08-08 19:51:26"
18+
android:textSize="20sp"
19+
20+
/>
21+
22+
<TextView
23+
android:id="@+id/single_run_time"
24+
android:layout_width="match_parent"
25+
android:layout_height="match_parent"
26+
android:layout_weight="1"
27+
android:fontFamily="sans-serif-light"
28+
android:textSize="30sp"
29+
android:text="00:00:00"
30+
android:gravity="center"
31+
/>
32+
</LinearLayout>

0 commit comments

Comments
 (0)