1 00:00:04,580 --> 00:00:06,660 G'day everyone, welcome back. 2 00:00:06,660 --> 00:00:14,360 In this video, we're going to remove the dependency that our AddEditFragment has on the content resolver. 3 00:00:14,360 --> 00:00:20,500 Instead of saving the task details, it will just pass them on to TaskTimerViewModel, 4 00:00:20,500 --> 00:00:23,620 and the ViewModel will take care of saving them. 5 00:00:23,620 --> 00:00:30,140 We've got a small optimisation in AddEditFragment's code to save the task details. 6 00:00:30,140 --> 00:00:34,860 It's described in the comment at the start of the saveTask function. 7 00:00:34,860 --> 00:00:38,760 We'll only save the details if something has changed. 8 00:00:38,760 --> 00:00:43,620 That's not business logic, and I'm going to leave that test in the fragment. 9 00:00:43,620 --> 00:00:49,760 If you're not sure whether something counts as business logic, and therefore whether it belongs in the model, 10 00:00:49,760 --> 00:00:55,220 ask the question, Would the program still give the same results without it? 11 00:00:55,220 --> 00:01:02,000 Here, the program would still do the same thing if we saved the data, regardless of whether it had changed or not. 12 00:01:02,000 --> 00:01:06,760 It would be less efficient, obviously, but the results would be the same. 13 00:01:06,760 --> 00:01:13,580 We have got a bit of business logic in here. The code checks to see if the task name isn't empty. 14 00:01:13,580 --> 00:01:20,260 That's not something the UI should be concerned with, so that test will be moved to the ViewModel. 15 00:01:20,260 --> 00:01:24,120 I'm also going to reverse the decision that we made earlier. 16 00:01:24,120 --> 00:01:30,780 When we looked at Kotlin data classes, we decided not to make Task a data class. 17 00:01:30,780 --> 00:01:35,680 That decision was made before Google deprecated the Loader classes. 18 00:01:35,680 --> 00:01:39,860 While changing the code from using a Loader to using a ViewModel, 19 00:01:39,860 --> 00:01:44,320 it became clear that a data class would provide some benefit. 20 00:01:44,320 --> 00:01:52,860 The important benefit we'll get, is the ability to compare two Task objects, so I'm going to make my Task a data class. 21 00:01:52,860 --> 00:02:00,940 You'll see why in a moment. 22 00:02:00,940 --> 00:02:07,140 While we're here, we've got a warning that id won't be serialized into a parcel. 23 00:02:07,140 --> 00:02:10,660 That was fine, but now we're going to need it to be. 24 00:02:10,660 --> 00:02:17,800 The fix is to move it into the primary constructor, 25 00:02:17,800 --> 00:02:26,960 and this time I'll accept Android Studio's suggestion, and remove the empty class body. 26 00:02:26,960 --> 00:02:32,980 Just to be clear, I've made those changes because the app was originally written to use a Loader. 27 00:02:32,980 --> 00:02:37,860 Google's decision to deprecate the Loader classes came as a bit of a surprise, 28 00:02:37,860 --> 00:02:41,660 and we've modified the app to use a ViewModel instead. 29 00:02:41,660 --> 00:02:46,180 By using a data class, we can make our next bit of code simpler. 30 00:02:46,180 --> 00:02:52,320 Okay, time to make some changes to AddEditFragment. 31 00:02:52,320 --> 00:02:59,420 At the moment the saveTask function is using values from the EditText widgets in the layout. 32 00:02:59,420 --> 00:03:05,880 Our ViewModel has no knowledge of those widgets, and we're going to pass it a Task object instead. 33 00:03:05,880 --> 00:03:13,900 Later in the course, you'll see another place where we need to create a new task from the contents of the EditText widgets. 34 00:03:13,900 --> 00:03:18,140 To save time later, I'm going to create a new function to do that. 35 00:03:18,140 --> 00:03:25,400 I'll call it task from UI, and add it before the saveTask function. 36 00:03:25,400 --> 00:03:30,940 We'll define the function, we'll get value for sortOrder. 37 00:03:30,940 --> 00:03:39,120 If it's not empty, then we're going to return the values that already exist by parsing it from the string, 38 00:03:39,120 --> 00:03:53,220 Otherwise, we'll return a zero, we'll make our new task, assign it an ID, and then return it. 39 00:03:53,220 --> 00:03:58,560 As we said, if there's a value in the sortOrder widget, we'll convert it to an integer. 40 00:03:58,560 --> 00:04:00,800 Otherwise, we'll set it to zero. 41 00:04:00,800 --> 00:04:07,360 When we create the new task, we'll use the contents of the EditText widgets for its properties. 42 00:04:07,360 --> 00:04:12,900 If we were editing an existing task, we also store the existing task ID. 43 00:04:12,900 --> 00:04:19,959 I used the Elvis operator on line 73, to assign the value 0 if the fragment's task is null. 44 00:04:19,959 --> 00:04:22,920 In other words, if we're adding a new task. 45 00:04:22,920 --> 00:04:25,840 That removes a lot of code from saveTask. 46 00:04:25,840 --> 00:04:37,860 In fact, it's now easier to delete the entire contents of saveTask and start again, rather than trying to change it. 47 00:04:37,860 --> 00:04:41,080 Let's define the function. 48 00:04:41,080 --> 00:04:50,440 We'll add a comment so that we remember what this is for. 49 00:04:50,440 --> 00:04:58,160 We'll create our new Task object, and we'll get the values from our EditText widgets. 50 00:04:58,160 --> 00:05:03,340 Then we'll check to see if it's the same task that we were given, when the fragment was created. 51 00:05:03,340 --> 00:05:05,700 The tasks we're editing, in other words. 52 00:05:05,700 --> 00:05:09,860 If they're not the same, then at least one thing has changed, 53 00:05:09,860 --> 00:05:13,000 and we'll call the saveTask function in the ViewModel. 54 00:05:13,000 --> 00:05:17,860 We haven't written that yet but we'll do that next. 55 00:05:17,860 --> 00:05:20,920 This is why I made the task a data class. 56 00:05:20,920 --> 00:05:27,880 We can compare two task instances, because a data class provides an equals function for us. 57 00:05:27,880 --> 00:05:34,540 We've got a suggestion the dot equals can be replaced with the binary operator. 58 00:05:34,540 --> 00:05:40,020 Use a light bulb to accept the suggestion. 59 00:05:40,020 --> 00:05:43,580 If you're used to Java, that may horrify you. 60 00:05:43,580 --> 00:05:51,540 Don't worry. Using the equals equals and the not equals operators with data classes works fine, in Kotlin. 61 00:05:51,540 --> 00:06:05,800 Our AddEditFragment doesn't have a ViewModel, so we'd better give it one. 62 00:06:05,800 --> 00:06:11,340 I've declared that slightly differently to the way it's declared in MainActivityFragment. 63 00:06:11,340 --> 00:06:16,900 The difference is subtle but important, and it's a good point to explain it. 64 00:06:16,900 --> 00:06:24,620 To see the difference, I'll copy the declaration from MainActivityFragment, and paste it below this one. 65 00:06:24,620 --> 00:06:29,240 Okay, the difference is in what we're passing as the scope. 66 00:06:29,240 --> 00:06:33,460 In MainActivityFragment we pass the fragment's activity. 67 00:06:33,460 --> 00:06:36,440 Here, we're passing the fragment. 68 00:06:36,440 --> 00:06:40,260 In this app, our fragments don't communicate with each other. 69 00:06:40,260 --> 00:06:46,560 They're totally independent, and aren't observing the same LiveData objects in the ViewModel. 70 00:06:46,560 --> 00:06:50,280 Sometimes, you will want fragments to share some data. 71 00:06:50,280 --> 00:06:54,300 In that case, you can pass the activity as the scope. 72 00:06:54,300 --> 00:06:59,020 Both fragments will then be observing data in the same ViewModel instance, 73 00:06:59,020 --> 00:07:05,520 and that ViewModel will exist as long as the activity does, surviving configuration changes too. 74 00:07:05,520 --> 00:07:09,700 You can find more information about this, here. 75 00:07:09,700 --> 00:07:14,940 The code samples have been converted to Kotlin. They're no longer just in Java. 76 00:07:14,940 --> 00:07:19,520 Unfortunately, there's an error in the Kotlin code at the moment. 77 00:07:19,520 --> 00:07:23,240 On the Java tab, for the example, 78 00:07:23,240 --> 00:07:33,560 both the master fragment and the detail fragment are passing the activity, 79 00:07:33,560 --> 00:07:35,560 and the document refers to that. 80 00:07:35,560 --> 00:07:45,220 At the moment, in the Kotlin code, and I'll click the Kotlin tab, it is this that is being passed in to both fragments. 81 00:07:45,220 --> 00:07:51,620 When you're reading through this, the text only makes sense if activity is passed as the scope there. 82 00:07:51,620 --> 00:07:56,580 So hopefully, by the time you watch this, they'll have corrected the documentation. 83 00:07:56,580 --> 00:08:04,340 I'll go through all this with some slides soon. It's worth reading through this document, after watching that video. 84 00:08:04,340 --> 00:08:08,980 Now, I've said that our fragments don't communicate with each other, and that's true. 85 00:08:08,980 --> 00:08:14,740 However, AddEditFragment's going to update the database via the ViewModel 86 00:08:14,740 --> 00:08:20,000 When that happens, the ViewModel will provide changes to the MainActivityFragment. 87 00:08:20,000 --> 00:08:25,220 Will that work, if the two fragments aren't sharing the same ViewModel instance? 88 00:08:25,220 --> 00:08:30,980 If you get the right answer to that question, then you obviously understand how all this fits together. 89 00:08:30,980 --> 00:08:37,419 Don't worry if you got the wrong answer. We've got some diagrams coming up that will explain how it all works. 90 00:08:37,419 --> 00:08:41,740 I think it's worth demonstrating, rather than just telling you the answer. 91 00:08:41,740 --> 00:08:47,560 I'll delete the declaration that I've just pasted in, 92 00:08:47,560 --> 00:08:53,360 and we'll leave AddEditFragment observing its own ViewModel, just to see what happens. 93 00:08:53,360 --> 00:08:59,980 Okay. I'll stop the video there. In the next video, we'll add the saveTask function to the view model, 94 00:08:59,980 --> 00:09:04,600 and test the changes, and we'll fix the error that we've got in the saveTask function. 95 00:09:04,600 --> 00:09:07,380 See you in the next one.