1 00:00:00,000 --> 00:00:02,463 (futuristic electronic music) 2 00:00:02,463 --> 00:00:05,010 (keyboard keys clack) 3 00:00:05,010 --> 00:00:05,843 So at this point now, 4 00:00:05,843 --> 00:00:08,440 there's only a few more changes we need to make to the app 5 00:00:08,440 --> 00:00:11,040 now that we've moved the non-UI functionality 6 00:00:11,040 --> 00:00:12,680 out of MainActivity. 7 00:00:12,680 --> 00:00:13,870 The first thing I want to do, though, 8 00:00:13,870 --> 00:00:16,530 isn't directly related to this change. 9 00:00:16,530 --> 00:00:18,920 I recorded the original top10downloader videos 10 00:00:18,920 --> 00:00:21,710 before Kotlin allowed top level constants. 11 00:00:21,710 --> 00:00:23,860 We have covered that now in an earlier video, 12 00:00:23,860 --> 00:00:25,090 and I've been using them ever since, 13 00:00:25,090 --> 00:00:27,240 so I'm gonna start by moving our constants 14 00:00:27,240 --> 00:00:28,680 from our MainActivity class 15 00:00:29,920 --> 00:00:31,053 out of the Main class. 16 00:00:32,180 --> 00:00:33,790 Go up to the top there, 17 00:00:33,790 --> 00:00:35,890 and the ones we want to move here are TAG. 18 00:00:37,728 --> 00:00:39,340 We also have STATE and STATE_LIMIT, 19 00:00:39,340 --> 00:00:41,030 so I'm gonna start by moving TAG 20 00:00:42,660 --> 00:00:44,030 above the class definition, 21 00:00:44,030 --> 00:00:46,960 and also, for STATE_LIMIT and STATE_URL, 22 00:00:46,960 --> 00:00:48,830 we'll do the same thing there. 23 00:00:48,830 --> 00:00:50,620 Paste them up there. 24 00:00:50,620 --> 00:00:52,093 We'll also make them const, 25 00:00:58,830 --> 00:01:00,340 like so, 26 00:01:00,340 --> 00:01:02,110 and what I'll also do is take the opportunity 27 00:01:02,110 --> 00:01:06,070 to delete the toString function from the FeedEntry class. 28 00:01:06,070 --> 00:01:08,990 We used to, in a very early version of this app, 29 00:01:08,990 --> 00:01:11,010 before we created the proper layout for the dialer, 30 00:01:11,010 --> 00:01:12,460 so it's fairly redundant now. 31 00:01:14,398 --> 00:01:16,680 Plus, that cleans things up a little bit as well. 32 00:01:16,680 --> 00:01:18,150 All right, so on to what changes 33 00:01:18,150 --> 00:01:19,810 we need to make at this point. 34 00:01:19,810 --> 00:01:22,253 Well, firstly, looking at the onCreate function, 35 00:01:23,470 --> 00:01:25,670 it's attempting to download the data. 36 00:01:25,670 --> 00:01:27,270 So it should just now be subscribing 37 00:01:27,270 --> 00:01:30,140 to the ViewModel's LiveData instead, 38 00:01:30,140 --> 00:01:31,727 so I would probably start by creating 39 00:01:31,727 --> 00:01:34,630 our feedViewModel instance in onCreate. 40 00:01:34,630 --> 00:01:36,440 So I'm gonna do that for now. 41 00:01:36,440 --> 00:01:38,650 I will do that after this code 42 00:01:38,650 --> 00:01:41,250 for the savedInstanceState. 43 00:01:41,250 --> 00:01:42,083 So val 44 00:01:43,300 --> 00:01:44,133 feedViewModel 45 00:01:45,210 --> 00:01:46,220 equals 46 00:01:46,220 --> 00:01:47,740 as ViewModel, 47 00:01:47,740 --> 00:01:49,400 and notice, because we've added the entry in gradle, 48 00:01:49,400 --> 00:01:52,130 Providers is now an option we can select. 49 00:01:52,130 --> 00:01:54,840 So it's ViewModelProviders.of. 50 00:01:54,840 --> 00:01:57,727 It has this and then a right parentheses, 51 00:01:57,727 --> 00:01:58,640 .get. 52 00:01:58,640 --> 00:01:59,790 Then in parentheses again, 53 00:01:59,790 --> 00:02:04,363 FeedViewModel::class.java. 54 00:02:06,060 --> 00:02:06,900 Then on the next line, 55 00:02:06,900 --> 00:02:08,900 we're going to do a feedModel, 56 00:02:08,900 --> 00:02:10,477 or feedViewModel, I should say, 57 00:02:10,477 --> 00:02:14,400 .feedEntries.observe 58 00:02:14,400 --> 00:02:15,667 parentheses this,. 59 00:02:16,550 --> 00:02:17,930 On the next line, 60 00:02:17,930 --> 00:02:19,323 we're gonna type Observer, 61 00:02:20,510 --> 00:02:22,720 and the top there will be 62 00:02:22,720 --> 00:02:24,708 List of 63 00:02:24,708 --> 00:02:25,541 FeedEntry, 64 00:02:27,880 --> 00:02:29,600 and then we're gonna create a lambda function, 65 00:02:29,600 --> 00:02:32,143 so left and right curly braces, 66 00:02:33,080 --> 00:02:34,933 and it's gonna be feedEntries, 67 00:02:38,230 --> 00:02:40,063 then our arrow, 68 00:02:41,981 --> 00:02:42,898 feedAdapter 69 00:02:45,170 --> 00:02:47,243 .setFeedList, 70 00:02:48,170 --> 00:02:50,743 then in parentheses, feedEntries. 71 00:02:51,910 --> 00:02:53,230 Close your parentheses. 72 00:02:53,230 --> 00:02:54,930 Then we got our right curly brace 73 00:02:54,930 --> 00:02:56,480 and then a closing parentheses. 74 00:02:57,534 --> 00:02:59,500 Now, this is very similar to what we've done before. 75 00:02:59,500 --> 00:03:01,590 Instead of setting the text in a widget, 76 00:03:01,590 --> 00:03:03,810 we're setting the data in our Adapter. 77 00:03:03,810 --> 00:03:06,670 That was originally handled by the DownloadData class, 78 00:03:06,670 --> 00:03:08,950 but now it's the job of MainActivity. 79 00:03:08,950 --> 00:03:10,930 The Adapter populates the List View, 80 00:03:10,930 --> 00:03:12,700 which is part of the UI, 81 00:03:12,700 --> 00:03:13,670 and we got an error there, 82 00:03:13,670 --> 00:03:17,020 unsurprisingly because we haven't yet created the Adapter. 83 00:03:17,020 --> 00:03:18,310 So let's go ahead and do that. 84 00:03:18,310 --> 00:03:20,604 We'll add that just after the onCreate call 85 00:03:20,604 --> 00:03:21,540 before the check for, 86 00:03:21,540 --> 00:03:24,440 if savedInstanceState is not equal to null. 87 00:03:24,440 --> 00:03:26,058 So do a val, 88 00:03:26,058 --> 00:03:27,620 feedAdapter is equal to 89 00:03:28,533 --> 00:03:29,770 and FeedAdapter, 90 00:03:29,770 --> 00:03:31,713 and in parentheses, this, 91 00:03:32,578 --> 00:03:35,173 and it's gonna be R.layout.list_record, 92 00:03:37,620 --> 00:03:38,453 comma, 93 00:03:38,453 --> 00:03:40,027 then EMPTY_FEED_LIST. 94 00:03:41,896 --> 00:03:43,476 That creates the Adapter, 95 00:03:43,476 --> 00:03:46,893 and we're gonna do an xmlListView.adapter 96 00:03:47,730 --> 00:03:49,310 is equal to, 97 00:03:49,310 --> 00:03:51,480 our newly created feedAdapter. 98 00:03:51,480 --> 00:03:53,000 So that's fixed one error. 99 00:03:53,000 --> 00:03:55,850 What we're doing when we observe a change in the log data, 100 00:03:55,850 --> 00:03:58,530 is setting the new data to the Adapter. 101 00:03:58,530 --> 00:04:00,860 Our Adapter hasn't been set up to do that. 102 00:04:00,860 --> 00:04:02,550 We just created a new one 103 00:04:02,550 --> 00:04:04,480 in the earlier version of this app. 104 00:04:04,480 --> 00:04:06,760 We need to make a small change to the Adapter, 105 00:04:06,760 --> 00:04:09,970 so that it provides a way for its data to be changed, 106 00:04:09,970 --> 00:04:12,200 and I'm talking about the setFeedList function 107 00:04:12,200 --> 00:04:14,860 that we're calling on line 52. 108 00:04:14,860 --> 00:04:17,730 So let's do that now so that this code makes more sense. 109 00:04:17,730 --> 00:04:20,800 I'm gonna add it to the start of the FeedAdapter class. 110 00:04:20,800 --> 00:04:23,742 Let's go ahead and open our FeedAdapter class, 111 00:04:23,742 --> 00:04:24,920 and we're gonna add it there, 112 00:04:24,920 --> 00:04:26,350 before our getView. 113 00:04:28,330 --> 00:04:30,610 'Kay, so it'll be fun setFeedList 114 00:04:33,434 --> 00:04:35,267 parentheses feedList:, 115 00:04:36,504 --> 00:04:39,017 and we're defining a List of FeedEntryObjects there. 116 00:04:39,017 --> 00:04:39,850 FeedEntry. 117 00:04:41,509 --> 00:04:42,342 Close your parentheses, 118 00:04:42,342 --> 00:04:45,730 and open up a left and right curly braces. 119 00:04:45,730 --> 00:04:49,010 Then we're gonna type this.applications 120 00:04:49,010 --> 00:04:51,220 equals feedList. 121 00:04:51,220 --> 00:04:54,373 Then we're gonna do a call to notifyDataSetChanged. 122 00:04:56,030 --> 00:04:57,640 So that's pretty straightforward. 123 00:04:57,640 --> 00:04:59,630 It just replaces the applications List 124 00:04:59,630 --> 00:05:03,200 with the new data that we provide as an argument, 125 00:05:03,200 --> 00:05:05,400 but we have got an error on line 28 126 00:05:05,400 --> 00:05:07,570 because applications is a val in the constructor, 127 00:05:07,570 --> 00:05:10,680 and you can see that's defined on line 21. 128 00:05:10,680 --> 00:05:13,440 If we gonna change it, obviously we need it to be a var. 129 00:05:13,440 --> 00:05:15,553 So we'll go ahead and change it to var there, 130 00:05:16,980 --> 00:05:18,610 and that fixes that error. 131 00:05:18,610 --> 00:05:21,120 So that's the only change to our Adapter, 132 00:05:21,120 --> 00:05:22,750 and that should've fixed another error in MainActivity, 133 00:05:22,750 --> 00:05:25,120 so let's go back and check that. 134 00:05:25,120 --> 00:05:27,710 It has fixed that, but it's now giving us another error. 135 00:05:27,710 --> 00:05:31,560 So Android Studio is now objecting to passing feedEntries 136 00:05:31,560 --> 00:05:33,860 because that function wants a non-null type, 137 00:05:33,860 --> 00:05:35,760 and feedEntries could be null. 138 00:05:35,760 --> 00:05:38,240 Now, we've taken steps to make sure it isn't. 139 00:05:38,240 --> 00:05:41,150 We've initialised it, if you recall, to be an empty List. 140 00:05:41,150 --> 00:05:43,220 So there are a few ways to fix this. 141 00:05:43,220 --> 00:05:45,130 The easiest, and the one I like the least, 142 00:05:45,130 --> 00:05:46,980 is to use the bang bang operator. 143 00:05:46,980 --> 00:05:49,510 So we could do something along the lines of, 144 00:05:49,510 --> 00:05:51,310 we've got feedAdapter.setFeedList, 145 00:05:51,310 --> 00:05:54,060 feedEntries in parentheses, add the bang bang operator. 146 00:05:55,060 --> 00:05:57,020 We know here that it's safe to do that 147 00:05:57,020 --> 00:05:59,110 because we've made sure that our LiveData object 148 00:05:59,110 --> 00:06:01,110 will always contain a List. 149 00:06:01,110 --> 00:06:02,690 The Kotlin language is still evolving, 150 00:06:02,690 --> 00:06:05,750 and you may need to resort to using the two exclamations, 151 00:06:05,750 --> 00:06:08,250 the bang bang operator, from time to time, 152 00:06:08,250 --> 00:06:09,440 and that's especially true 153 00:06:09,440 --> 00:06:11,510 when using Kotlin with Java classes, 154 00:06:11,510 --> 00:06:13,300 and most of the Android framework, if you recall, 155 00:06:13,300 --> 00:06:14,980 is currently written in Java, 156 00:06:14,980 --> 00:06:15,980 but, in this case, 157 00:06:15,980 --> 00:06:18,720 I think it's safer to use the Elvis operator 158 00:06:18,720 --> 00:06:20,700 and pass an empty List if, 159 00:06:20,700 --> 00:06:22,410 and only if, feedEntries is null. 160 00:06:22,410 --> 00:06:24,820 So what I'm gonna do is duplicate that whole line there, 161 00:06:24,820 --> 00:06:25,920 well, those two lines, 162 00:06:27,210 --> 00:06:28,960 comment out one, 163 00:06:28,960 --> 00:06:30,830 and instead, we're gonna change that 164 00:06:30,830 --> 00:06:31,907 to use an Elvis operator, 165 00:06:31,907 --> 00:06:35,233 and so basically, within the parentheses for setFeedList, 166 00:06:36,107 --> 00:06:37,350 we're gonna put feedEntries, 167 00:06:37,350 --> 00:06:38,910 get rid of the bang bang operator, 168 00:06:38,910 --> 00:06:42,548 and put ?:, which is the Elvis operator, 169 00:06:42,548 --> 00:06:43,881 EMPTY_FEED_LIST. 170 00:06:46,000 --> 00:06:48,460 So basically, if feedEntries is null, 171 00:06:48,460 --> 00:06:50,350 we'll pass that EMPTY_FEED_LIST 172 00:06:50,350 --> 00:06:53,013 that we declared in our feedViewModel. 173 00:06:53,910 --> 00:06:55,810 So even though it would be safe here, 174 00:06:55,810 --> 00:06:57,810 I like to avoid using the bang bang operator 175 00:06:57,810 --> 00:06:59,210 as much as possible. 176 00:06:59,210 --> 00:07:01,300 That approach really does make sure 177 00:07:01,300 --> 00:07:03,320 that our code won't crash. 178 00:07:03,320 --> 00:07:05,130 Now, once you're happy with this, 179 00:07:05,130 --> 00:07:07,590 you may prefer to create your own LiveData 180 00:07:07,590 --> 00:07:09,313 subclass that can't return null. 181 00:07:10,750 --> 00:07:11,610 If you do a bit of Googling, 182 00:07:11,610 --> 00:07:13,870 you can find some solutions to that. 183 00:07:13,870 --> 00:07:15,080 One such solution 184 00:07:17,050 --> 00:07:18,323 that we found Googling, 185 00:07:20,380 --> 00:07:21,213 and it gives you a bit of an entry 186 00:07:21,213 --> 00:07:23,360 and a way there to actually resolve that, 187 00:07:23,360 --> 00:07:24,240 but, with that said, 188 00:07:24,240 --> 00:07:27,060 I suggest you stick with things like the Elvis operator 189 00:07:27,060 --> 00:07:29,800 until you're really comfortable with LiveData, 190 00:07:29,800 --> 00:07:31,700 and that's because creating subclasses 191 00:07:31,700 --> 00:07:33,420 of things that you're still learning about 192 00:07:33,420 --> 00:07:34,970 probably isn't wise. 193 00:07:34,970 --> 00:07:37,740 You can end up writing code that you don't fully understand, 194 00:07:37,740 --> 00:07:41,320 and that, in turn, makes debugging a nightmare. 195 00:07:41,320 --> 00:07:43,293 All right, so getting back to our code again. 196 00:07:44,520 --> 00:07:46,190 Back to MainActivity. 197 00:07:46,190 --> 00:07:48,010 We've still got some entries, 198 00:07:48,010 --> 00:07:49,330 or three errors rather, 199 00:07:49,330 --> 00:07:51,270 but luckily, they're all trivial to fix. 200 00:07:51,270 --> 00:07:53,610 Well, trivial apart from the other error that you'll get. 201 00:07:53,610 --> 00:07:56,800 So, our call to downloadUrl, though, first. 202 00:07:56,800 --> 00:07:58,186 That could be changed 203 00:07:58,186 --> 00:08:00,155 to call the downloadUrl function 204 00:08:00,155 --> 00:08:01,788 of our ViewModel instead, 205 00:08:01,788 --> 00:08:02,849 so instead of downloadUrl, 206 00:08:02,849 --> 00:08:03,682 it can be 207 00:08:04,579 --> 00:08:06,746 feedViewModel.downloadUrl. 208 00:08:07,870 --> 00:08:09,100 That's one, and there's another one 209 00:08:09,100 --> 00:08:11,040 down here in onOptionsItemSelected, 210 00:08:11,040 --> 00:08:12,760 so let's change this one down here, 211 00:08:12,760 --> 00:08:15,320 ignoring the error on line 89 for now. 212 00:08:15,320 --> 00:08:17,810 So feedViewModel.downloadUrl, 213 00:08:20,570 --> 00:08:23,630 but notice when I do that, unresolved reference. 214 00:08:23,630 --> 00:08:25,610 The problem we've got here is that feedViewModel 215 00:08:25,610 --> 00:08:27,460 is local to onCreate. 216 00:08:27,460 --> 00:08:29,333 We'll go back and have a look at that. 217 00:08:30,400 --> 00:08:33,580 We're to find that, on line 50, for onCreate, it's a local. 218 00:08:33,580 --> 00:08:35,610 We have seen how to fix things like this. 219 00:08:35,610 --> 00:08:37,740 We just move it into a class variable 220 00:08:37,740 --> 00:08:40,220 and then use a lazy delegate to resolve it. 221 00:08:40,220 --> 00:08:41,140 So let's go ahead and do that, 222 00:08:41,140 --> 00:08:42,690 so I'm going to cut this out 223 00:08:44,010 --> 00:08:45,413 of our onCreate function. 224 00:08:46,750 --> 00:08:49,280 Paste it in as a class variable. 225 00:08:49,280 --> 00:08:51,280 We're gonna make it, instead, a private. 226 00:08:53,780 --> 00:08:54,613 That's feedViewModel. 227 00:08:54,613 --> 00:08:55,760 Then we're gonna add a definition, 228 00:08:55,760 --> 00:08:57,040 so it's :, 229 00:08:57,040 --> 00:08:58,823 the type is feedViewModel. 230 00:08:59,830 --> 00:09:01,280 Then we need to type by lazy. 231 00:09:02,130 --> 00:09:03,560 Instead of the equal sign, 232 00:09:03,560 --> 00:09:06,730 we're going to add a left and right curly braces. 233 00:09:06,730 --> 00:09:09,210 We'll have the left curly brace there and the right one 234 00:09:10,110 --> 00:09:12,820 surrounding the code that we need to be called, 235 00:09:12,820 --> 00:09:15,200 and that's now defined, as you can see, 236 00:09:15,200 --> 00:09:17,310 and scrolling down, looking at our error now, 237 00:09:17,310 --> 00:09:19,180 we can see we fixed the error that was 238 00:09:19,180 --> 00:09:21,660 present there on line 93. 239 00:09:21,660 --> 00:09:24,410 We've still got an error here, though, on line 89, 240 00:09:24,410 --> 00:09:27,490 relating to trying to invalidate the URL. 241 00:09:27,490 --> 00:09:30,207 If you recall, our ViewModel has got a function to do that, 242 00:09:30,207 --> 00:09:33,440 and we've now got a reference to it that we can use. 243 00:09:33,440 --> 00:09:36,310 So let's actually change this altogether 244 00:09:36,310 --> 00:09:37,143 to be a call to 245 00:09:38,096 --> 00:09:38,929 feedViewModel 246 00:09:41,087 --> 00:09:41,973 .invalidate. 247 00:09:43,090 --> 00:09:44,340 That should've fixed all the errors, 248 00:09:44,340 --> 00:09:45,820 and we're just about done, 249 00:09:45,820 --> 00:09:47,870 but one strange thing that can happen 250 00:09:47,870 --> 00:09:50,780 is when we move the log TAG into the top level, 251 00:09:50,780 --> 00:09:52,480 as we did earlier in this video, 252 00:09:52,480 --> 00:09:55,970 sometimes, you can find that there's a weird import 253 00:09:55,970 --> 00:09:57,290 that Android Studio will add. 254 00:09:57,290 --> 00:09:58,123 It'll be something like 255 00:09:58,123 --> 00:10:01,800 import android.content.contentValues. 256 00:10:01,800 --> 00:10:03,350 So if that does pop up, 257 00:10:03,350 --> 00:10:04,320 you'll need to remove that 258 00:10:04,320 --> 00:10:06,310 because you'll have a duplicate entry for TAG, 259 00:10:06,310 --> 00:10:08,050 and that may be causing you some errors. 260 00:10:08,050 --> 00:10:10,400 In my case, I haven't actually got that error there. 261 00:10:10,400 --> 00:10:11,650 All right, so does it work now? 262 00:10:11,650 --> 00:10:14,200 We've now made changes to all our code, 263 00:10:14,200 --> 00:10:15,860 or to most of our classes. 264 00:10:15,860 --> 00:10:19,050 The time now has come to actually start testing the app out. 265 00:10:19,050 --> 00:10:21,650 So let's work on testing that app in the next video.