Skip to content

Commit 4a44b5d

Browse files
(DOCSP-12165): Android phase 2 tutorial with bluehawk/text (#479)
1 parent 53a43c1 commit 4a44b5d

File tree

13 files changed

+399
-9
lines changed

13 files changed

+399
-9
lines changed

tutorial/kotlin-android/app/src/main/java/com/mongodb/tasktracker/LoginActivity.kt

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ class LoginActivity : AppCompatActivity() {
3535
}
3636

3737
private fun onLoginSuccess() {
38-
// successful login ends this activity, bringing the user back to the task activity
38+
// successful login ends this activity, bringing the user back to the project activity
3939
finish()
4040
}
4141

@@ -68,8 +68,10 @@ class LoginActivity : AppCompatActivity() {
6868

6969
if (createUser) {
7070
// register a user using the Realm App we created in the TaskTracker class
71+
// :code-block-start: create-user
72+
// :hide-start:
7173
taskApp.emailPassword.registerUserAsync(username, password) {
72-
// re-enable the buttons after user registration completes
74+
// re-enable the buttons after user registration returns a result
7375
createUserButton.isEnabled = true
7476
loginButton.isEnabled = true
7577
if (!it.isSuccess) {
@@ -81,10 +83,16 @@ class LoginActivity : AppCompatActivity() {
8183
login(false)
8284
}
8385
}
86+
// :replace-with:
87+
//// TODO: Register a new user with the supplied username and password when the "Create" button is pressed.
88+
// :hide-end:
89+
// :code-block-end:
8490
} else {
91+
// :code-block-start: login-user
92+
// :hide-start:
8593
val creds = Credentials.emailPassword(username, password)
8694
taskApp.loginAsync(creds) {
87-
// re-enable the buttons after
95+
// re-enable the buttons after user login returns a result
8896
loginButton.isEnabled = true
8997
createUserButton.isEnabled = true
9098
if (!it.isSuccess) {
@@ -93,6 +101,10 @@ class LoginActivity : AppCompatActivity() {
93101
onLoginSuccess()
94102
}
95103
}
104+
// :replace-with:
105+
//// TODO: Log in with the supplied username and password when the "Log in" button is pressed.
106+
// :hide-end:
107+
// :code-block-end:
96108
}
97109
}
98110
}

tutorial/kotlin-android/app/src/main/java/com/mongodb/tasktracker/MemberActivity.kt

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ class MemberActivity : AppCompatActivity() {
5454
.setCancelable(true)
5555
.setPositiveButton("Add User") { dialog, _ ->
5656
dialog.dismiss()
57+
// :code-block-start: add-new-member-to-project
58+
// :hide-start:
5759
val functionsManager: Functions = taskApp.getFunctions(user)
5860
functionsManager.callFunctionAsync(
5961
"addTeamMember",
@@ -65,12 +67,17 @@ class MemberActivity : AppCompatActivity() {
6567
TAG(),
6668
"Attempted to add team member. Result: ${result.get()}"
6769
)
70+
// rebuild the list of members to display the newly-added member
6871
setUpRecyclerView()
6972
} else {
7073
Log.e(TAG(), "failed to add team member with: " + result.error)
7174
Toast.makeText(this, result.error.errorMessage, Toast.LENGTH_LONG).show()
7275
}
7376
}
77+
// :replace-with:
78+
//// TODO: Add the new team member to the project by calling the `addTeamMember` Realm Function through `taskApp`.
79+
// :hide-end:
80+
// :code-block-end:
7481
}
7582
.setNegativeButton("Cancel") { dialog, _ ->
7683
dialog.cancel()
@@ -89,11 +96,14 @@ class MemberActivity : AppCompatActivity() {
8996
}
9097

9198
private fun setUpRecyclerView() {
99+
// :code-block-start: get-team-members
100+
// :hide-start:
92101
val functionsManager: Functions = taskApp.getFunctions(user)
93102
// get team members by calling a Realm Function which returns a list of members
94103
functionsManager.callFunctionAsync("getMyTeamMembers", ArrayList<String>(), ArrayList::class.java) { result ->
95104
if (result.isSuccess) {
96-
Log.v(TAG(), "team members value: ${result.get()}")
105+
Log.v(TAG(), "successfully fetched team members. Number of team members: ${result.get().size}")
106+
// The `getMyTeamMembers` function returns team members as Document objects. Convert them into Member objects so the MemberAdapter can display them.
97107
this.members = ArrayList(result.get().map { item -> Member(item as Document) })
98108
adapter = MemberAdapter(members, user!!)
99109
recyclerView.layoutManager = LinearLayoutManager(this)
@@ -104,5 +114,16 @@ class MemberActivity : AppCompatActivity() {
104114
Log.e(TAG(), "failed to get team members with: " + result.error)
105115
}
106116
}
117+
// :replace-with:
118+
//// TODO: Call the `getMyTeamMembers` function to get a list of team members, then display them in a RecyclerView with the following code:
119+
//// The `getMyTeamMembers` function returns team members as Document objects. Convert them into Member objects so the MemberAdapter can display them.
120+
//// this.members = ArrayList(result.get().map { item -> Member(item as Document) })
121+
//// adapter = MemberAdapter(members, user!!)
122+
//// recyclerView.layoutManager = LinearLayoutManager(this)
123+
//// recyclerView.adapter = adapter
124+
//// recyclerView.setHasFixedSize(true)
125+
//// recyclerView.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL))
126+
// :hide-end:
127+
// :code-block-end:
107128
}
108129
}

tutorial/kotlin-android/app/src/main/java/com/mongodb/tasktracker/ProjectActivity.kt

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ class ProjectActivity : AppCompatActivity() {
3939
// if no user is currently logged in, start the login activity so the user can authenticate
4040
startActivity(Intent(this, LoginActivity::class.java))
4141
} else {
42+
// :code-block-start: set-up-user-realm
43+
// :hide-start:
4244
// configure realm to use the current user and the partition corresponding to the user's project
4345
val config = SyncConfiguration.Builder(user!!, "user=${user!!.id}")
4446
.build()
@@ -51,28 +53,43 @@ class ProjectActivity : AppCompatActivity() {
5153
setUpRecyclerView(realm)
5254
}
5355
})
56+
// :replace-with:
57+
//// TODO: initialize a connection to a realm containing the user's User object
58+
// :hide-end:
59+
// :code-block-end:
5460
}
5561
}
5662

5763
override fun onCreate(savedInstanceState: Bundle?) {
5864
super.onCreate(savedInstanceState)
5965
setContentView(R.layout.activity_project)
60-
6166
recyclerView = findViewById(R.id.project_list)
6267
}
6368

69+
// :code-block-start: on-stop-close-realm
70+
// :hide-start:
6471
override fun onStop() {
6572
super.onStop()
6673
user.run {
6774
userRealm?.close()
6875
}
6976
}
77+
// :replace-with:
78+
//// TODO: always ensure that the user realm closes when the activity ends via the onStop lifecycle method
79+
// :hide-end:
80+
// :code-block-end:
7081

82+
// :code-block-start: on-destroy-close-realm
83+
// :hide-start:
7184
override fun onDestroy() {
7285
super.onDestroy()
7386
userRealm?.close()
7487
recyclerView.adapter = null
7588
}
89+
// :replace-with:
90+
//// TODO: always ensure that the user realm closes when the activity ends via the onDestroy lifecycle method
91+
// :hide-end:
92+
// :code-block-end:
7693

7794
override fun onCreateOptionsMenu(menu: Menu): Boolean {
7895
menuInflater.inflate(R.menu.activity_task_menu, menu)
@@ -101,8 +118,15 @@ class ProjectActivity : AppCompatActivity() {
101118

102119
private fun setUpRecyclerView(realm: Realm) {
103120
// query for a user object in our user realm, which should only contain our user object
121+
// :code-block-start: fetch-synced-user-safely
122+
// :hide-start:
104123
val syncedUsers : RealmResults<User> = realm.where<User>().sort("_id").findAll()
105124
val syncedUser : User? = syncedUsers.getOrNull(0) // since there might be no user objects in the results, default to "null"
125+
// :replace-with:
126+
//// TODO: query the realm to get a copy of the currently logged in user's User object (or null, if the trigger didn't create it yet)
127+
//var syncedUser : User? = null
128+
// :hide-end:
129+
// :code-block-end:
106130

107131
// if a user object exists, create the recycler view and the corresponding adapter
108132
if (syncedUser != null) {
@@ -122,11 +146,17 @@ class ProjectActivity : AppCompatActivity() {
122146
// if the user object doesn't yet exist (that is, if there are no users in the user realm), call this function again when it is created
123147
Log.i(TAG(), "User object not yet initialized, waiting for initialization via Trigger before displaying projects.")
124148
// change listener on a query for our user object lets us know when the user object has been created by the auth trigger
149+
// :code-block-start: user-init-change-listener
150+
// :hide-start:
125151
val changeListener = OrderedRealmCollectionChangeListener<RealmResults<User>> { results, changeSet ->
126152
Log.i(TAG(), "User object initialized, displaying project list.")
127153
setUpRecyclerView(realm)
128154
}
129-
syncedUsers?.addChangeListener(changeListener)
155+
syncedUsers.addChangeListener(changeListener)
156+
// :replace-with:
157+
//// TODO: set up a change listener that will set up the recycler view once our trigger initializes the user's User object
158+
// :hide-end:
159+
// :code-block-end:
130160
}
131161
}
132162
}

tutorial/kotlin-android/app/src/main/java/com/mongodb/tasktracker/TaskActivity.kt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ class TaskActivity : AppCompatActivity() {
4646

4747
// display the name of the project in the action bar via the title member variable of the Activity
4848
title = projectName
49+
50+
// :code-block-start: set-up-project-realm
51+
// :hide-start:
4952
val config = SyncConfiguration.Builder(user!!, partition)
5053
.build()
5154

@@ -57,15 +60,26 @@ class TaskActivity : AppCompatActivity() {
5760
setUpRecyclerView(realm, user, partition)
5861
}
5962
})
63+
// :replace-with:
64+
//// TODO: initialize a connection to a realm containing all of the Tasks in this project
65+
// :hide-end:
66+
// :code-block-end:
6067
}
6168
}
6269

70+
// :code-block-start: on-stop-close-realm
71+
// :hide-start:
6372
override fun onStop() {
6473
super.onStop()
6574
user.run {
6675
projectRealm.close()
6776
}
6877
}
78+
// :replace-with:
79+
//// TODO: always ensure that the project realm closes when the activity ends via the onStop lifecycle method
80+
// :hide-end:
81+
// :code-block-end:
82+
6983

7084
override fun onCreate(savedInstanceState: Bundle?) {
7185
super.onCreate(savedInstanceState)
@@ -81,11 +95,17 @@ class TaskActivity : AppCompatActivity() {
8195
.setCancelable(true)
8296
.setPositiveButton("Create") { dialog, _ -> run {
8397
dialog.dismiss()
98+
// :code-block-start: add-new-task-to-project
99+
// :hide-start:
84100
val task = Task(input.text.toString())
85101
// all realm writes need to occur inside of a transaction
86102
projectRealm.executeTransactionAsync { realm ->
87103
realm.insert(task)
88104
}
105+
// :replace-with:
106+
//// TODO: Add a new task to the project by inserting into the realm when the user clicks "create" for a new task.
107+
// :hide-end:
108+
// :code-block-end:
89109
}
90110
}
91111
.setNegativeButton("Cancel") { dialog, _ -> dialog.cancel()
@@ -98,19 +118,32 @@ class TaskActivity : AppCompatActivity() {
98118
}
99119
}
100120

121+
// :code-block-start: on-destroy-close-realm
122+
// :hide-start:
101123
override fun onDestroy() {
102124
super.onDestroy()
103125
recyclerView.adapter = null
104126
// if a user hasn't logged out when the activity exits, still need to explicitly close the realm
105127
projectRealm.close()
106128
}
129+
// :replace-with:
130+
//// TODO: always ensure that the project realm closes when the activity ends via the onDestroy lifecycle method
131+
// :hide-end:
132+
// :code-block-end:
133+
107134

108135
private fun setUpRecyclerView(realm: Realm, user: User?, partition: String) {
109136
// a recyclerview requires an adapter, which feeds it items to display.
110137
// Realm provides RealmRecyclerViewAdapter, which you can extend to customize for your application
111138
// pass the adapter a collection of Tasks from the realm
112139
// sort this collection so that the displayed order of Tasks remains stable across updates
140+
// :code-block-start:
141+
// :hide-start:
113142
adapter = TaskAdapter(realm.where<Task>().sort("_id").findAll(), user!!, partition)
143+
// :replace-with:
144+
//// TODO: Query the realm for Task objects, sorted by a stable order that remains consistent between runs.
145+
// :hide-end:
146+
// :code-block-end:
114147
recyclerView.layoutManager = LinearLayoutManager(this)
115148
recyclerView.adapter = adapter
116149
recyclerView.setHasFixedSize(true)

tutorial/kotlin-android/app/src/main/java/com/mongodb/tasktracker/TaskTracker.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,16 @@ class TaskTracker : Application() {
2424

2525
override fun onCreate() {
2626
super.onCreate()
27+
// :code-block-start: initialize-realm-and-create-app
28+
// :hide-start:
2729
Realm.init(this)
2830
taskApp = App(
2931
AppConfiguration.Builder(BuildConfig.MONGODB_REALM_APP_ID)
3032
.build())
33+
// :replace-with:
34+
//// TODO: Initialize the Realm SDK and create the App object we will use to communicate with the Realm backend.
35+
// :hide-end:
36+
// :code-block-end:
3137

3238
// Enable more logging in debug mode
3339
if (BuildConfig.DEBUG) {

tutorial/kotlin-android/app/src/main/java/com/mongodb/tasktracker/model/MemberAdapter.kt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ internal class MemberAdapter(private val data: ArrayList<Member>, private val us
4343
dialogBuilder.setMessage("Are you sure you want to remove this user from the project?")
4444
.setCancelable(true)
4545
.setPositiveButton("Remove User") { dialog, _ ->
46+
// :code-block-start: remove-user-from-project
47+
// :hide-start:
4648
val functionsManager: Functions = taskApp.getFunctions(user)
4749
functionsManager.callFunctionAsync("removeTeamMember",
4850
listOf(obj.name), Document::class.java) { result ->
@@ -58,6 +60,12 @@ internal class MemberAdapter(private val data: ArrayList<Member>, private val us
5860
}
5961
}
6062
}
63+
// :replace-with:
64+
//// TODO: Call the `removeTeamMember` Realm Function through `taskApp` to remove the selected user from the project.
65+
//// When the function completes, remember to dismiss the dialog.
66+
//// If the function successfully removes the team member, remove the team member from the displayed data and notify the Adapter that an item has been removed.
67+
// :hide-end:
68+
// :code-block-end:
6169
}
6270
.setNegativeButton("Cancel") { dialog, _ ->
6371
dialog.cancel()

tutorial/kotlin-android/app/src/main/java/com/mongodb/tasktracker/model/ProjectAdapter.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ internal class ProjectAdapter(data: RealmList<Project>, var user: User) : RealmR
5454
holder.itemView.setOnClickListener {
5555
run {
5656
// when a user clicks on a project, bring them to the task view for that project
57-
var intent : Intent = Intent(parent.context, TaskActivity::class.java)
57+
val intent : Intent = Intent(parent.context, TaskActivity::class.java)
5858
intent.putExtra(PARTITION_EXTRA_KEY, obj?.partition)
5959
intent.putExtra(PROJECT_NAME_EXTRA_KEY, obj?.name)
6060
parent.context.startActivity(intent)

tutorial/kotlin-android/app/src/main/java/com/mongodb/tasktracker/model/TaskAdapter.kt

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import org.bson.types.ObjectId
1818
* TaskAdapter: extends the Realm-provided RealmRecyclerViewAdapter to provide data for a RecyclerView to display
1919
* Realm objects on screen to a user.
2020
*/
21-
internal class TaskAdapter(data: OrderedRealmCollection<Task>, val user: io.realm.mongodb.User, val partition: String) : RealmRecyclerViewAdapter<Task, TaskAdapter.TaskViewHolder?>(data, true) {
21+
internal class TaskAdapter(data: OrderedRealmCollection<Task>, val user: io.realm.mongodb.User, private val partition: String) : RealmRecyclerViewAdapter<Task, TaskAdapter.TaskViewHolder?>(data, true) {
2222

2323
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TaskViewHolder {
2424
val itemView: View = LayoutInflater.from(parent.context).inflate(R.layout.task_view, parent, false)
@@ -83,6 +83,8 @@ internal class TaskAdapter(data: OrderedRealmCollection<Task>, val user: io.real
8383
}
8484

8585
private fun changeStatus(status: TaskStatus, _id: ObjectId?) {
86+
// :code-block-start: change-task-status
87+
// :hide-start:
8688
// need to create a separate instance of realm to issue an update, since this event is
8789
// handled by a background thread and realm instances cannot be shared across threads
8890
val config = SyncConfiguration.Builder(user, partition)
@@ -98,9 +100,18 @@ internal class TaskAdapter(data: OrderedRealmCollection<Task>, val user: io.real
98100
}
99101
// always close realms when you are done with them!
100102
realm.close()
103+
// :replace-with:
104+
//// TODO: Change the status of the specified Task object in the project realm.
105+
//// Step 1: Connect to the project realm using the `partition` member variable of the adapter.
106+
//// Step 2: Query the realm for the Task with the specified _id value.
107+
//// Step 3: Set the `statusEnum` property of the Task to the specified status value.
108+
// :hide-end:
109+
// :code-block-end:
101110
}
102111

103112
private fun removeAt(id: ObjectId) {
113+
// :code-block-start: delete-task
114+
// :hide-start:
104115
// need to create a separate instance of realm to issue an update, since this event is
105116
// handled by a background thread and realm instances cannot be shared across threads
106117
val config = SyncConfiguration.Builder(user, partition)
@@ -116,6 +127,13 @@ internal class TaskAdapter(data: OrderedRealmCollection<Task>, val user: io.real
116127
}
117128
// always close realms when you are done with them!
118129
realm.close()
130+
// :replace-with:
131+
//// TODO: Delete the specified Task object from the project realm.
132+
//// Step 1: Connect to the project realm using the `partition` member variable of the adapter.
133+
//// Step 2: Query the realm for the Task with the specified _id value.
134+
//// Step 3: Delete the Task from the project realm.
135+
// :hide-end:
136+
// :code-block-end:
119137
}
120138

121139
internal inner class TaskViewHolder(view: View) : RecyclerView.ViewHolder(view) {

tutorial/kotlin-android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ buildscript {
1010
}
1111
}
1212
dependencies {
13-
classpath 'com.android.tools.build:gradle:4.0.0'
13+
classpath 'com.android.tools.build:gradle:4.0.1'
1414
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
1515
classpath "io.realm:realm-gradle-plugin:10.0.0-BETA.8"
1616
}
Binary file not shown.

0 commit comments

Comments
 (0)