1 00:00:05,440 --> 00:00:06,689 Alright, so let's continue on now 2 00:00:06,689 --> 00:00:09,120 updating our application, our calculator app. 3 00:00:09,120 --> 00:00:12,150 Back in MainActivity, our first OnClickListener 4 00:00:12,150 --> 00:00:14,800 is appending the button's caption 5 00:00:14,800 --> 00:00:17,600 to the edit text widget's contents. 6 00:00:17,600 --> 00:00:19,770 So, that's at the start of onCreate. 7 00:00:19,770 --> 00:00:23,350 You can see it doing that lines 15 through 18. 8 00:00:23,350 --> 00:00:25,140 So, the new number widget's going to 9 00:00:25,140 --> 00:00:26,340 have its values set from our 10 00:00:26,340 --> 00:00:29,920 view model's LiveData object instead. 11 00:00:29,920 --> 00:00:32,500 So what we need to do here is call a function 12 00:00:32,500 --> 00:00:34,660 in the view model to tell it that 13 00:00:34,660 --> 00:00:37,200 a digit button was tapped. 14 00:00:37,200 --> 00:00:39,800 I'm gonna call the function digitPressed 15 00:00:39,800 --> 00:00:42,960 and we'll be pasting the button's caption as an argument. 16 00:00:42,960 --> 00:00:44,027 We haven't written it yet, so we're gonna 17 00:00:44,027 --> 00:00:46,170 get an error when I actually do this, 18 00:00:46,170 --> 00:00:48,040 in fact we'll get two errors because 19 00:00:48,040 --> 00:00:50,400 we haven't subscribed to the view model yet. 20 00:00:50,400 --> 00:00:51,970 But one thing at a time, 21 00:00:51,970 --> 00:00:54,720 so what we're gonna do first is delete these two lines, 22 00:00:56,266 --> 00:00:59,160 we're gonna replace that with viewModel 23 00:00:59,160 --> 00:01:01,823 which again, we haven't defined yet, .digitPressed. 24 00:01:04,971 --> 00:01:07,477 Then we need two parentheses, v as Button, 25 00:01:09,000 --> 00:01:13,443 closing parentheses, .text.toString, 26 00:01:14,840 --> 00:01:16,740 then we've got the closing parentheses 27 00:01:17,978 --> 00:01:20,240 for toString and the closing parentheses for that line. 28 00:01:20,240 --> 00:01:22,690 I'm tempted to modify the other two listeners 29 00:01:22,690 --> 00:01:24,410 to avoid jumping back and forth between 30 00:01:24,410 --> 00:01:26,740 our two classes, but I think it's clearer 31 00:01:26,740 --> 00:01:28,860 if we stick to one thing at a time. 32 00:01:28,860 --> 00:01:31,250 So let's write the digitPressed function 33 00:01:31,250 --> 00:01:33,760 in CalculatorViewModel next. 34 00:01:33,760 --> 00:01:36,520 We'll go back to the CalculatorViewModel class 35 00:01:36,520 --> 00:01:38,140 and I'm gonna put this before 36 00:01:38,140 --> 00:01:39,823 the perform operation function. 37 00:01:40,760 --> 00:01:43,823 So I'm gonna start by typing fun digitPressed, 38 00:01:45,280 --> 00:01:48,083 parentheses, caption: String. 39 00:01:50,443 --> 00:01:51,920 And the terms of the code, 40 00:01:51,920 --> 00:01:53,740 you're gonna start by putting if, 41 00:01:53,740 --> 00:01:57,790 parentheses, newNumber.value 42 00:01:58,710 --> 00:02:01,790 is not equal to null, closing parentheses. 43 00:02:01,790 --> 00:02:04,712 We'll open a code block, then we're gonna type 44 00:02:04,712 --> 00:02:09,673 newNumber.value is equal to newNumber.value 45 00:02:12,330 --> 00:02:14,980 plus caption, and we'll need to add 46 00:02:14,980 --> 00:02:16,580 an else to that code block, 47 00:02:16,580 --> 00:02:19,089 else gets its own code block, 48 00:02:19,089 --> 00:02:23,473 newNumber.value is equal to caption. 49 00:02:25,000 --> 00:02:26,350 I've effectively, digitPressed, 50 00:02:26,350 --> 00:02:28,700 that's this digitPressed function. 51 00:02:28,700 --> 00:02:30,640 It does the same thing as the code 52 00:02:30,640 --> 00:02:32,230 we've removed from MainActivity. 53 00:02:32,230 --> 00:02:34,080 It appends the button's caption 54 00:02:34,080 --> 00:02:38,160 to the current value of a newNumber LiveData object. 55 00:02:38,160 --> 00:02:39,860 We do need a null check there that we've got 56 00:02:39,860 --> 00:02:43,100 on line 20 because value may well be null. 57 00:02:43,100 --> 00:02:45,620 If we don't check for null, we'll get the string null 58 00:02:45,620 --> 00:02:47,640 appearing at the start of our number. 59 00:02:47,640 --> 00:02:49,900 Alright, so back to MainActivity, 60 00:02:49,900 --> 00:02:52,870 and let's now look for, or look at, the next listener. 61 00:02:52,870 --> 00:02:55,970 This next listener is the listener 62 00:02:55,970 --> 00:02:59,660 that responds to one of the operand buttons being tapped. 63 00:02:59,660 --> 00:03:01,640 We'll do something similar here. 64 00:03:01,640 --> 00:03:03,930 We'll call a function in the view model 65 00:03:03,930 --> 00:03:06,560 and pass it the caption of the button. 66 00:03:06,560 --> 00:03:08,010 There's a lot more code in this function 67 00:03:08,010 --> 00:03:09,640 so instead of deleting it, what I'm going to do 68 00:03:09,640 --> 00:03:11,760 is cut it to the clipboard. 69 00:03:11,760 --> 00:03:13,460 That'll save typing when we create 70 00:03:13,460 --> 00:03:16,490 the corresponding function in the view model. 71 00:03:16,490 --> 00:03:20,142 What I'm going to do first is the OnClickListener, 72 00:03:20,142 --> 00:03:20,975 that's the first line there, 73 00:03:20,975 --> 00:03:24,190 I'm going to type viewModel, which again 74 00:03:24,190 --> 00:03:26,390 we haven't yet defined, the function 75 00:03:26,390 --> 00:03:28,433 will be called operandPressed, 76 00:03:29,990 --> 00:03:31,940 which again we haven't created yet, 77 00:03:31,940 --> 00:03:35,170 two parentheses, v as Button, 78 00:03:35,170 --> 00:03:38,383 closing parentheses, .text.toString. 79 00:03:40,000 --> 00:03:40,940 We've got the two parentheses, 80 00:03:40,940 --> 00:03:43,340 to close off toString and then the parentheses 81 00:03:43,340 --> 00:03:47,860 to close off the operandPressed function call. 82 00:03:47,860 --> 00:03:49,270 I'm gonna copy the rest of this text 83 00:03:49,270 --> 00:03:53,140 or rather, cut this text out of this listener 84 00:03:53,140 --> 00:03:54,810 as mentioned, and we're gonna go back 85 00:03:54,810 --> 00:03:57,480 to our CalculatorViewModel class. 86 00:03:57,480 --> 00:03:59,730 We're gonna write the operandPressed function. 87 00:03:59,730 --> 00:04:03,093 I'm gonna put that below the digitPressed function. 88 00:04:04,700 --> 00:04:07,860 We'll start by typing fun, that's operandPressed. 89 00:04:09,690 --> 00:04:13,750 It's got op called String, parentheses, 90 00:04:13,750 --> 00:04:15,770 and you have to write curly braces 91 00:04:15,770 --> 00:04:17,680 to make it a function and we'll paste in 92 00:04:17,680 --> 00:04:20,829 that code from our MainActivity class. 93 00:04:20,829 --> 00:04:22,260 Once again here, we want to make sure 94 00:04:22,260 --> 00:04:25,010 we click cancel when asked to import 95 00:04:25,010 --> 00:04:28,770 the synthetic imports, so I'm going to cancel that. 96 00:04:28,770 --> 00:04:30,420 The reason is firstly, we don't need 97 00:04:30,420 --> 00:04:31,990 that first line because we're not 98 00:04:31,990 --> 00:04:34,390 going to be dealing with the UI. 99 00:04:34,390 --> 00:04:36,090 Alright, so I'm gonna cancel that. 100 00:04:38,450 --> 00:04:40,550 Firstly, this first line here, 101 00:04:40,550 --> 00:04:42,310 we don't actually need that because 102 00:04:42,310 --> 00:04:44,610 we've already cast the view to be a button 103 00:04:44,610 --> 00:04:46,960 and passed its text already as the op argument 104 00:04:46,960 --> 00:04:48,210 so I'm gonna delete that. 105 00:04:49,400 --> 00:04:51,480 There are a couple of things to fix though. 106 00:04:51,480 --> 00:04:54,090 Firstly, newNumber, which we're using here 107 00:04:54,090 --> 00:04:57,380 on line 30 is a LiveData object in here 108 00:04:57,380 --> 00:05:00,937 so we need to get its value rather than its text. 109 00:05:00,937 --> 00:05:02,860 I'm gonna change text there to value 110 00:05:04,610 --> 00:05:06,723 but also with a safe call operator. 111 00:05:08,071 --> 00:05:09,571 I'm gonna remove the .toString 112 00:05:10,783 --> 00:05:13,730 and call .toDouble instead. 113 00:05:13,730 --> 00:05:15,221 That's the first part, then down here 114 00:05:15,221 --> 00:05:19,090 where it's newNumber.setText, 115 00:05:19,090 --> 00:05:21,140 we need to change that to value as well, 116 00:05:21,140 --> 00:05:22,890 equals, and get rid of the parentheses 117 00:05:22,890 --> 00:05:24,940 around the two double quotes. 118 00:05:24,940 --> 00:05:27,840 It's possible that newNumber may not have a value, 119 00:05:27,840 --> 00:05:29,410 which is why we're using a safe call, 120 00:05:29,410 --> 00:05:31,240 why I added a safe call before 121 00:05:31,240 --> 00:05:33,970 converting it to a double on line 30. 122 00:05:33,970 --> 00:05:36,500 As a result, I've got an error here, when we try 123 00:05:36,500 --> 00:05:39,797 and pass value to the performOperation function. 124 00:05:39,797 --> 00:05:42,700 Value is actually a nullable double 125 00:05:42,700 --> 00:05:45,310 and pendingOperation expects a non-null argument. 126 00:05:45,310 --> 00:05:47,890 What we can do to resolve this 127 00:05:47,890 --> 00:05:50,000 is wrap the call in a null test 128 00:05:50,000 --> 00:05:52,870 and let Kotlin perform a smart cast for us. 129 00:05:52,870 --> 00:05:57,070 To do that, after the value line on 30, 130 00:05:57,070 --> 00:05:59,250 I'm gonna put if, parentheses, 131 00:05:59,250 --> 00:06:01,133 value is not equal to null, 132 00:06:02,519 --> 00:06:04,380 open a code block, I'm gonna paste in 133 00:06:04,380 --> 00:06:08,310 that performOperation into that code block. 134 00:06:08,310 --> 00:06:09,530 The last change to this function 135 00:06:09,530 --> 00:06:11,250 is similar to what we've done earlier. 136 00:06:11,250 --> 00:06:12,830 We're attempting to now update 137 00:06:12,830 --> 00:06:14,930 the operation widget and we should 138 00:06:14,930 --> 00:06:18,130 actually be using another LiveData object instead. 139 00:06:18,130 --> 00:06:21,140 Let's declare that LiveData object 140 00:06:21,140 --> 00:06:23,440 below the other two, so we're going to need 141 00:06:23,440 --> 00:06:25,510 a val here for operation, val operation 142 00:06:26,440 --> 00:06:30,333 is equal to, it's also a MutableLiveData object. 143 00:06:31,170 --> 00:06:33,090 It's a string, you'll have to write in 144 00:06:33,090 --> 00:06:34,880 less than and greater than signs, 145 00:06:34,880 --> 00:06:37,770 and add parentheses as well. 146 00:06:37,770 --> 00:06:39,340 Then once we've done that, we can change 147 00:06:39,340 --> 00:06:43,540 this operation.text to operation.value 148 00:06:44,710 --> 00:06:47,253 to save it to our MutableLiveData object. 149 00:06:48,500 --> 00:06:51,090 Okay, just clean up some lines here. 150 00:06:51,090 --> 00:06:53,110 Alright, so that's two listeners changed. 151 00:06:53,110 --> 00:06:54,760 There's one more listener we need to change. 152 00:06:54,760 --> 00:06:56,260 Let's go back to MainActivity. 153 00:06:57,360 --> 00:07:00,406 This third listener that we need to change. 154 00:07:00,406 --> 00:07:01,850 You're gonna do the same pressers here, 155 00:07:01,850 --> 00:07:03,460 add a call to the appropriate function 156 00:07:03,460 --> 00:07:05,670 in the view model, and cut the code out of here 157 00:07:05,670 --> 00:07:07,500 and paste it into the new function 158 00:07:07,500 --> 00:07:10,962 in our CalculatorViewModel. 159 00:07:10,962 --> 00:07:13,587 Within the buttonNeg.setOnClickListener, 160 00:07:13,587 --> 00:07:18,493 I'm gonna do a call to viewModel.negPressed, 161 00:07:19,580 --> 00:07:22,153 parentheses, of course we getting errors 162 00:07:22,153 --> 00:07:23,760 because it hasn't been created yet. 163 00:07:23,760 --> 00:07:25,713 We're gonna cut out this code, 164 00:07:28,103 --> 00:07:30,400 we're gonna go back to our CalculatorViewModel class 165 00:07:30,400 --> 00:07:32,680 and let's add this function below the other two, 166 00:07:32,680 --> 00:07:36,890 below the digitPressed and operandPressed functions. 167 00:07:36,890 --> 00:07:41,890 This one is negPressed, no parameters for this one. 168 00:07:43,330 --> 00:07:44,530 We'll paste in the code. 169 00:07:45,830 --> 00:07:47,560 Once again, I'm gonna cancel the suggestion 170 00:07:47,560 --> 00:07:49,083 to add the synthetic import. 171 00:07:50,020 --> 00:07:52,690 We'll just move this up so I can see a bit easier. 172 00:07:52,690 --> 00:07:54,720 Fixing the errors, it's the same as before. 173 00:07:54,720 --> 00:07:57,900 We just need to use the value property instead of text. 174 00:07:57,900 --> 00:07:58,870 It's pretty easy there because 175 00:07:58,870 --> 00:08:00,990 all the references there are actually 176 00:08:00,990 --> 00:08:03,200 highlighted in red for us, so firstly 177 00:08:03,200 --> 00:08:08,200 newNumber.text is newNumber.value, delete that out. 178 00:08:08,630 --> 00:08:12,223 NewNumber.setText, change that to value. 179 00:08:14,371 --> 00:08:16,440 That's gonna be equal to, remove the parentheses 180 00:08:16,440 --> 00:08:19,510 around the double quotes and minus sign. 181 00:08:19,510 --> 00:08:22,507 Next we've got newNumber.setText. 182 00:08:22,507 --> 00:08:24,657 We're gonna change that to newNumber.value. 183 00:08:25,570 --> 00:08:27,600 That's gonna be equal now to, 184 00:08:27,600 --> 00:08:30,600 remove the parentheses from doubleValue.toString. 185 00:08:30,600 --> 00:08:33,890 You've still got the parentheses on the end of toString. 186 00:08:33,890 --> 00:08:36,775 Finally, to catch the NumberFormatException, 187 00:08:36,775 --> 00:08:39,429 newNumber, instead of .text, it's .value 188 00:08:40,530 --> 00:08:43,000 equals, remove the parentheses, 189 00:08:43,000 --> 00:08:44,830 and leave the double quotes in there. 190 00:08:44,830 --> 00:08:47,760 You can see that we've still got an error here though. 191 00:08:47,760 --> 00:08:50,590 This error here, we're calling the isEmpty function 192 00:08:50,590 --> 00:08:52,520 on something that may be null. 193 00:08:52,520 --> 00:08:53,780 But this particular scenario, 194 00:08:53,780 --> 00:08:55,650 rather than using a safe call, 195 00:08:55,650 --> 00:08:57,500 I'm going to add a null check. 196 00:08:57,500 --> 00:09:00,260 The reason is that Kotlin can't perform 197 00:09:00,260 --> 00:09:02,920 a safe cast on this line down here, 198 00:09:02,920 --> 00:09:05,520 this value.toDouble, so it's also showing 199 00:09:05,520 --> 00:09:06,940 an error there for that reason, 200 00:09:06,940 --> 00:09:09,630 but a null check will fix that error as well. 201 00:09:09,630 --> 00:09:10,890 What we need to do is change this line, 202 00:09:10,890 --> 00:09:13,800 it's currently got if parentheses value.isEmpty. 203 00:09:13,800 --> 00:09:15,090 We'll add a null check in there. 204 00:09:15,090 --> 00:09:20,090 If value is equal to null or value.isEmpty, 205 00:09:21,100 --> 00:09:23,960 or, that's two pipe characters, 206 00:09:23,960 --> 00:09:26,183 that then fixes the remaining errors. 207 00:09:27,110 --> 00:09:30,120 That's our viewModel class finished for now anyway. 208 00:09:30,120 --> 00:09:31,630 We're gonna come back to it to sort out 209 00:09:31,630 --> 00:09:33,010 that problem that I mentioned. 210 00:09:33,010 --> 00:09:34,700 We shouldn't really be exposing 211 00:09:34,700 --> 00:09:36,810 MutableLiveData objects but we'll see 212 00:09:36,810 --> 00:09:39,710 how to deal with that once we've got the app working. 213 00:09:39,710 --> 00:09:41,493 Before leaving though, just make a check here. 214 00:09:41,493 --> 00:09:43,470 Just make sure that the imports, 215 00:09:43,470 --> 00:09:45,620 or check the imports rather, to make sure 216 00:09:45,620 --> 00:09:48,080 that there's no synthetic imports hanging around. 217 00:09:48,080 --> 00:09:51,450 You can see in my case that there's no imports there. 218 00:09:51,450 --> 00:09:52,720 The last thing we now need to do 219 00:09:52,720 --> 00:09:57,508 is to subscribe MainActivity to our CalculatorViewModel. 220 00:09:57,508 --> 00:10:00,540 Going back to MainActivity and specifically in 221 00:10:00,540 --> 00:10:03,290 the onCreate function, we're going to 222 00:10:03,290 --> 00:10:05,832 use the viewModelProviders class 223 00:10:05,832 --> 00:10:07,846 to get a reference to the view model. 224 00:10:07,846 --> 00:10:08,950 This code I'm about to type in 225 00:10:08,950 --> 00:10:10,070 is lifted straight out of that 226 00:10:10,070 --> 00:10:12,940 example code we were looking at earlier. 227 00:10:12,940 --> 00:10:14,090 Just as a warning, we'll actually 228 00:10:14,090 --> 00:10:16,670 get an error as we go through this. 229 00:10:16,670 --> 00:10:18,620 If I type in, below the setContentView, 230 00:10:20,476 --> 00:10:24,927 val viewModel is equal to ViewModelProviders. 231 00:10:26,720 --> 00:10:28,323 I actually want providers here, 232 00:10:29,510 --> 00:10:32,260 but notice we haven't got an option for providers. 233 00:10:32,260 --> 00:10:34,030 We want providers and not provider 234 00:10:34,030 --> 00:10:35,550 but that doesn't appear in the suggestions 235 00:10:35,550 --> 00:10:37,130 but we'll see why in a moment. 236 00:10:37,130 --> 00:10:38,280 I'm gonna continue typing this. 237 00:10:38,280 --> 00:10:42,633 Providers.of, parentheses, this, parentheses, 238 00:10:43,507 --> 00:10:45,490 .get, then in parentheses again, 239 00:10:45,490 --> 00:10:50,490 it's gonna be CalculatorViewModel::class.java, parentheses. 240 00:10:53,060 --> 00:10:54,410 The only difference to the code 241 00:10:54,410 --> 00:10:57,790 in the example is that we used ::class.java 242 00:10:57,790 --> 00:10:59,807 rather than .class because this is Kotlin 243 00:10:59,807 --> 00:11:03,590 and the example we were looking at earlier is in Java. 244 00:11:03,590 --> 00:11:05,220 Well, there's actually another difference, 245 00:11:05,220 --> 00:11:06,630 we've got an error at the moment, 246 00:11:06,630 --> 00:11:08,200 so switching back to that document, 247 00:11:08,200 --> 00:11:10,470 go back to our browser again. 248 00:11:10,470 --> 00:11:11,510 We'll have a look at the top left here. 249 00:11:11,510 --> 00:11:13,540 There's this sub option here, 250 00:11:13,540 --> 00:11:15,680 adding components to your project. 251 00:11:15,680 --> 00:11:17,300 I'm gonna click on that. 252 00:11:17,300 --> 00:11:19,610 That's gonna open up another page. 253 00:11:19,610 --> 00:11:21,693 If you scroll down and have a bit of a look there, 254 00:11:23,800 --> 00:11:25,280 you can see lifecycle, we've got some 255 00:11:25,280 --> 00:11:28,630 information there about dependencies 256 00:11:28,630 --> 00:11:32,200 that are needed for LiveData, lifecycle, and ViewModel. 257 00:11:32,200 --> 00:11:33,520 We haven't actually done this step yet 258 00:11:33,520 --> 00:11:35,170 and that's why we didn't get the suggestion 259 00:11:35,170 --> 00:11:38,010 in the drop down and why we've got an error. 260 00:11:38,010 --> 00:11:39,530 We need to actually follow the advice here 261 00:11:39,530 --> 00:11:42,730 and add the appropriate things to our build.gradle file. 262 00:11:42,730 --> 00:11:44,280 Now, I could have referred you 263 00:11:44,280 --> 00:11:46,423 to this link first and avoided the errors 264 00:11:46,423 --> 00:11:48,438 but it's such an easy thing to forget 265 00:11:48,438 --> 00:11:50,410 that seeing what happens when you do forget, 266 00:11:50,410 --> 00:11:52,390 I think, is very useful. 267 00:11:52,390 --> 00:11:54,190 I scroll down as you saw and a little way 268 00:11:54,190 --> 00:11:56,300 down that page you can see this lifecycle section. 269 00:11:56,300 --> 00:11:59,860 We want ViewModel and LiveData 270 00:11:59,860 --> 00:12:01,890 and you can see that's the top dependency here. 271 00:12:01,890 --> 00:12:03,270 What we need to do again, as I mentioned, 272 00:12:03,270 --> 00:12:05,720 is add to our build.gradle file. 273 00:12:05,720 --> 00:12:07,854 Google started spreading the version numbers out 274 00:12:07,854 --> 00:12:10,140 so here you can see that they've defined 275 00:12:10,140 --> 00:12:14,770 the lifecycle version to be 1.1.1 at the moment 276 00:12:14,770 --> 00:12:16,810 as of the time I'm recording this. 277 00:12:16,810 --> 00:12:18,330 We're gonna copy these three lines, 278 00:12:18,330 --> 00:12:20,330 starting from the def lifecycle_version, 279 00:12:21,195 --> 00:12:22,520 including the comment, and including this 280 00:12:22,520 --> 00:12:24,760 next line, the implementation line. 281 00:12:24,760 --> 00:12:27,440 We'll take a copy of that and we want to 282 00:12:27,440 --> 00:12:31,199 actually go back to our module's build.gradle file. 283 00:12:31,199 --> 00:12:36,199 So I'll open up that, click on our module app. 284 00:12:37,551 --> 00:12:40,240 I'm gonna double click on the build.gradle file. 285 00:12:41,465 --> 00:12:45,090 What we're going to do is paste in those three lines 286 00:12:45,090 --> 00:12:47,520 as the first entry below the dependencies, 287 00:12:47,520 --> 00:12:49,470 or in the dependencies section. 288 00:12:49,470 --> 00:12:52,030 Using that lifecycle_version declaration, 289 00:12:52,030 --> 00:12:53,570 I think, makes it much easier to 290 00:12:53,570 --> 00:12:56,580 update the version of all lifecycle dependencies 291 00:12:56,580 --> 00:12:59,950 at once rather than hunting through the list to find them. 292 00:12:59,950 --> 00:13:01,322 While we're here though, we've also got 293 00:13:01,322 --> 00:13:02,790 this other warning which we've seen 294 00:13:02,790 --> 00:13:06,917 in other projects that the Kotlin-stdlib-jre is deprecated 295 00:13:08,700 --> 00:13:11,500 and should be replaced with jdk instead. 296 00:13:11,500 --> 00:13:13,050 At the moment, Android Studio is still 297 00:13:13,050 --> 00:13:16,520 generating new projects with that deprecated dependency 298 00:13:16,520 --> 00:13:17,820 and they're obviously going to change 299 00:13:17,820 --> 00:13:20,223 at some point so if you don't see this warning, 300 00:13:20,223 --> 00:13:22,750 then you can ignore this bit, that's fine. 301 00:13:22,750 --> 00:13:25,260 But I'll update the deprecated dependency 302 00:13:25,260 --> 00:13:26,093 which is easy to spot because it's got 303 00:13:26,093 --> 00:13:28,040 a line striking it out. 304 00:13:28,040 --> 00:13:30,270 I'm gonna change, we're gonna delete the re 305 00:13:30,270 --> 00:13:34,570 in jre and change that with jdk and we need 306 00:13:34,570 --> 00:13:38,620 to sync now and that should have fixed the error. 307 00:13:38,620 --> 00:13:40,560 It also removed the warning, 308 00:13:40,560 --> 00:13:42,190 so I'll wait for that to finish, 309 00:13:42,190 --> 00:13:44,290 then we'll go back and check MainActivity. 310 00:13:45,970 --> 00:13:48,660 You can see that it was downloading some components. 311 00:13:48,660 --> 00:13:53,370 It's actually now synced and we can close down that window. 312 00:13:53,370 --> 00:13:56,180 You can see we've no longer got a warning there after jdk. 313 00:13:56,180 --> 00:13:57,850 If you go back to MainActivity, 314 00:13:57,850 --> 00:13:59,850 we've now got an option there to import. 315 00:14:00,760 --> 00:14:01,593 It may come up automatically or 316 00:14:01,593 --> 00:14:03,220 you may need to import it yourself. 317 00:14:04,380 --> 00:14:06,300 Once you do that, you can see that 318 00:14:06,300 --> 00:14:08,580 we've now got ViewModelProviders being valid. 319 00:14:08,580 --> 00:14:09,520 You can see the import has now 320 00:14:09,520 --> 00:14:11,263 been added onto line 3 as well. 321 00:14:12,130 --> 00:14:14,550 Now, often that'll actually auto-import 322 00:14:14,550 --> 00:14:17,470 for you automatically, but you can see 323 00:14:17,470 --> 00:14:19,870 in my case I needed to do an Alt + Enter 324 00:14:19,870 --> 00:14:21,790 to import ViewModelProviders 325 00:14:21,790 --> 00:14:24,210 from Android.arch.lifecycle. 326 00:14:24,210 --> 00:14:27,267 Just do that if it doesn't import automatically. 327 00:14:27,267 --> 00:14:28,270 Now that we've actually imported that, 328 00:14:28,270 --> 00:14:30,010 you will find that ViewModelProviders, 329 00:14:30,010 --> 00:14:32,520 with the S, will appear as a suggestion. 330 00:14:32,520 --> 00:14:34,480 Personally I've got no idea why it was happy 331 00:14:34,480 --> 00:14:37,810 to suggest ViewModelProvider without the S 332 00:14:37,810 --> 00:14:39,460 but not the one we wanted. 333 00:14:39,460 --> 00:14:42,588 After all they both come from Android.arch.lifecycle. 334 00:14:42,588 --> 00:14:44,300 All of this is very new and it's also 335 00:14:44,300 --> 00:14:47,570 in the process of being moved into Android X. 336 00:14:47,570 --> 00:14:49,887 Google are sorting through their support libraries 337 00:14:49,887 --> 00:14:51,350 and there's a blog post about it 338 00:14:51,350 --> 00:14:53,663 that I think is quite interesting to check out. 339 00:14:55,840 --> 00:14:58,355 Life should be a lot simpler after the migration 340 00:14:58,355 --> 00:15:00,300 and we'll no longer have things like 341 00:15:00,300 --> 00:15:02,440 a v7 support library with 342 00:15:02,440 --> 00:15:04,263 a minimum supported Android version. 343 00:15:05,400 --> 00:15:06,760 But that said, I don't recommend that 344 00:15:06,760 --> 00:15:08,690 you switch to these new libraries yet 345 00:15:08,690 --> 00:15:10,055 and you can find more information 346 00:15:10,055 --> 00:15:11,473 about them as you scroll down. 347 00:15:12,800 --> 00:15:15,306 It's talking about the old Android package names 348 00:15:15,306 --> 00:15:17,790 and the new Android package names there. 349 00:15:17,790 --> 00:15:19,060 Again, I don't suggest that you switch 350 00:15:19,060 --> 00:15:20,830 to the new libraries just yet. 351 00:15:20,830 --> 00:15:22,060 If you read through that, you'll find that 352 00:15:22,060 --> 00:15:23,850 this does actually mention the same thing, 353 00:15:23,850 --> 00:15:26,320 to not switch yet from production code. 354 00:15:26,320 --> 00:15:28,050 The next version of Android Studio 355 00:15:28,050 --> 00:15:30,950 will perform the migration of your dependencies 356 00:15:30,950 --> 00:15:33,270 automatically so hopefully Android X 357 00:15:33,270 --> 00:15:35,480 will be out of beta around the time 358 00:15:35,480 --> 00:15:38,430 Android Studio 3.2 is released. 359 00:15:38,430 --> 00:15:39,490 Until then though, stick with 360 00:15:39,490 --> 00:15:41,513 these dependencies that I've shown you. 361 00:15:42,411 --> 00:15:43,290 Alright, so creating an instance 362 00:15:43,290 --> 00:15:45,180 of your view model isn't that easy. 363 00:15:45,180 --> 00:15:48,230 You create, and I'll go back to the code now, 364 00:15:48,230 --> 00:15:50,220 you actually create the static of method 365 00:15:50,220 --> 00:15:52,580 or call the static of method of 366 00:15:52,580 --> 00:15:54,590 the ViewModelProviders class to get 367 00:15:54,590 --> 00:15:56,720 a ViewModelProvider, and then you call 368 00:15:56,720 --> 00:16:00,810 its get method to get the view model type you want. 369 00:16:00,810 --> 00:16:03,740 Here actually what we want is a CalculatorViewModel, 370 00:16:03,740 --> 00:16:08,280 so that's what we specified on line 16 when calling get. 371 00:16:08,280 --> 00:16:09,620 The code to start observing 372 00:16:09,620 --> 00:16:12,230 the view model's LiveData objects is simple. 373 00:16:12,230 --> 00:16:14,503 I'm gonna type that in and then talk about it. 374 00:16:15,402 --> 00:16:19,610 We're going to type viewModel.result.observe 375 00:16:21,660 --> 00:16:24,750 then in parentheses, this comma, 376 00:16:24,750 --> 00:16:28,970 then we want observer, I'm gonna choose this one here. 377 00:16:28,970 --> 00:16:32,980 Specify string, less than and greater than sign 378 00:16:32,980 --> 00:16:35,030 to separate it, and then we want 379 00:16:35,030 --> 00:16:39,978 our left and right curly braces and within there 380 00:16:39,978 --> 00:16:42,790 we're gonna type stringResult 381 00:16:42,790 --> 00:16:46,340 and the arrow, the dash greater than sign, 382 00:16:46,340 --> 00:16:50,390 result.setText and in parentheses 383 00:16:50,390 --> 00:16:51,897 it's gonna be stringResult. 384 00:16:53,095 --> 00:16:54,260 We've got the closing parentheses 385 00:16:54,260 --> 00:16:57,890 closing right curly brace and then closing parentheses. 386 00:16:57,890 --> 00:17:01,175 Right now I need to do a similar thing to newNumber. 387 00:17:01,175 --> 00:17:06,175 viewModel.newNumber.observe, 388 00:17:06,359 --> 00:17:09,079 and we're gonna type this then observer again 389 00:17:11,367 --> 00:17:12,586 and less than greater than, 390 00:17:12,586 --> 00:17:14,063 we're gonna specify string again. 391 00:17:14,970 --> 00:17:17,560 Then we've got our left and right curly braces again 392 00:17:17,560 --> 00:17:18,990 and this time we're gonna type stringNumber 393 00:17:21,740 --> 00:17:25,540 and the arrow which is dash greater than sign, 394 00:17:25,540 --> 00:17:29,940 then newNumber.setText. 395 00:17:29,940 --> 00:17:33,460 In parentheses, it's gonna be stringNumber. 396 00:17:33,460 --> 00:17:35,480 The third line, pretty similar there. 397 00:17:35,480 --> 00:17:40,480 Model., this time it's gonna be operation.observe, 398 00:17:40,560 --> 00:17:44,520 parentheses, this comma, and it's gonna be observer. 399 00:17:44,520 --> 00:17:46,760 Observer again, and again in 400 00:17:46,760 --> 00:17:48,330 the less than and greater than sign 401 00:17:49,737 --> 00:17:51,330 we're gonna put string again 402 00:17:51,330 --> 00:17:52,900 and left and right curly braces. 403 00:17:52,900 --> 00:17:54,850 This time it's gonna be stringOperation 404 00:17:56,040 --> 00:17:58,028 and the dash greater than sign, the arrow, 405 00:17:58,028 --> 00:18:01,390 then we're gonna type operation.text 406 00:18:01,390 --> 00:18:03,507 equals stringOperation. 407 00:18:05,810 --> 00:18:07,460 We set up observer now on each of 408 00:18:07,460 --> 00:18:10,200 the LiveData objects that we're interested in. 409 00:18:10,200 --> 00:18:12,030 The observe function needs an owner, 410 00:18:12,030 --> 00:18:14,170 which will be our activity in this case. 411 00:18:14,170 --> 00:18:16,400 It also needs an observer instance. 412 00:18:16,400 --> 00:18:19,000 Observer is just an interface and you can see there 413 00:18:19,000 --> 00:18:21,450 that we're using landers to provide the callback 414 00:18:21,450 --> 00:18:24,780 that'll be executed when the observed object changes. 415 00:18:24,780 --> 00:18:28,110 If the view model's result LiveData object changes, 416 00:18:28,110 --> 00:18:30,720 our result, which is text, will be set to the new result 417 00:18:30,720 --> 00:18:33,950 and the same thing happens for the other two widgets. 418 00:18:33,950 --> 00:18:36,600 That's the theory, let's see if it actually works. 419 00:18:36,600 --> 00:18:38,100 I'm gonna run the application. 420 00:18:39,400 --> 00:18:40,740 I'll just fast forward this bit 421 00:18:40,740 --> 00:18:43,313 while it's actually starting the emulator. 422 00:18:48,240 --> 00:18:50,123 Okay, we'll just make this a bit bigger. 423 00:18:54,900 --> 00:18:56,330 Alright, so let's do a quick test here. 424 00:18:56,330 --> 00:19:00,173 I'm gonna enter 56 and tap on the modification. 425 00:19:01,270 --> 00:19:03,670 You can see there the display is updated 426 00:19:03,670 --> 00:19:05,900 and we've got the number 56 in the result 427 00:19:05,900 --> 00:19:07,530 and we've also got the modification 428 00:19:07,530 --> 00:19:09,410 as the operation to be performed. 429 00:19:09,410 --> 00:19:13,250 We type seven and equals, you can see 430 00:19:13,250 --> 00:19:14,991 the answer is 392.0. 431 00:19:14,991 --> 00:19:19,090 So far so good, but what about rotating the device? 432 00:19:19,090 --> 00:19:21,250 Let's select division and 433 00:19:21,250 --> 00:19:22,743 I'm gonna rotate the device. 434 00:19:25,060 --> 00:19:27,330 I can see the display there still looks fine, 435 00:19:27,330 --> 00:19:29,560 and if everything's working, 392 should be 436 00:19:29,560 --> 00:19:31,530 divided by the next number. 437 00:19:31,530 --> 00:19:36,530 Let's enter four and equals and you can see that worked. 438 00:19:36,890 --> 00:19:39,380 We got the result 98.0. 439 00:19:39,380 --> 00:19:41,330 We're not saving the state of our app, 440 00:19:41,330 --> 00:19:42,930 the variables for the operation 441 00:19:42,930 --> 00:19:45,130 and the operand, but it still works 442 00:19:45,130 --> 00:19:47,270 after a configuration change. 443 00:19:47,270 --> 00:19:48,760 The view model isn't destroyed 444 00:19:48,760 --> 00:19:52,109 and recreated when the activity is. 445 00:19:52,109 --> 00:19:53,970 When the activity gets recreated, 446 00:19:53,970 --> 00:19:56,390 it subscribes to the view model again 447 00:19:56,390 --> 00:19:58,720 and everything's just as it was 448 00:19:58,720 --> 00:20:00,420 before the activity was destroyed. 449 00:20:01,920 --> 00:20:03,490 That's pretty cool, so I'm gonna finish 450 00:20:03,490 --> 00:20:06,180 the video now just by reviewing our code. 451 00:20:06,180 --> 00:20:08,420 Firstly, looking back at MainActivity. 452 00:20:08,420 --> 00:20:10,210 I think you'd agree that's significantly 453 00:20:10,210 --> 00:20:12,850 easier now, really quite simple. 454 00:20:12,850 --> 00:20:14,790 It really doesn't do anything now 455 00:20:14,790 --> 00:20:17,610 that isn't directly related to the UI. 456 00:20:17,610 --> 00:20:18,921 It's really just setting up, 457 00:20:18,921 --> 00:20:21,950 after the layout is inflated on line 15 458 00:20:23,200 --> 00:20:24,980 there's a call there to the getContentView 459 00:20:24,980 --> 00:20:26,383 on the start of onCreate. 460 00:20:27,636 --> 00:20:28,490 The next bit of code there observes 461 00:20:28,490 --> 00:20:30,530 the LiveData objects and updates 462 00:20:30,530 --> 00:20:32,680 the various widgets when they change. 463 00:20:32,680 --> 00:20:33,990 Everything really there is just 464 00:20:33,990 --> 00:20:36,070 setting up for the listeners. 465 00:20:36,070 --> 00:20:37,250 The listeners don't attempt to do 466 00:20:37,250 --> 00:20:38,940 any processing here, they're just 467 00:20:38,940 --> 00:20:41,540 calling functions in the view model 468 00:20:41,540 --> 00:20:44,170 and the view model actually does all the work. 469 00:20:44,170 --> 00:20:45,550 What we've done is we've separated 470 00:20:45,550 --> 00:20:48,240 the display from the app's logic 471 00:20:48,240 --> 00:20:50,040 and the activity's only concerned with 472 00:20:50,040 --> 00:20:54,020 displaying the data and responding to user interaction. 473 00:20:54,020 --> 00:20:56,050 In the CalculatorViewModel class, 474 00:20:56,050 --> 00:20:58,800 we've got all the code that deals with the app's logic. 475 00:20:59,990 --> 00:21:01,800 This is still a very simple app 476 00:21:01,800 --> 00:21:03,190 and we'll be seeing view models 477 00:21:03,190 --> 00:21:05,040 that do a bit more later. 478 00:21:05,040 --> 00:21:06,900 Notice that the CalculatorViewModel class 479 00:21:06,900 --> 00:21:09,210 doesn't have any knowledge about its activity. 480 00:21:09,210 --> 00:21:10,210 In fact, it doesn't even know 481 00:21:10,210 --> 00:21:11,680 that there is an activity. 482 00:21:11,680 --> 00:21:13,180 We could use it from a fragment 483 00:21:13,180 --> 00:21:16,250 or a service and it would still work. 484 00:21:16,250 --> 00:21:18,880 What it does do is expose LiveData objects 485 00:21:18,880 --> 00:21:21,360 for the results it needs to pass back 486 00:21:21,360 --> 00:21:22,490 and doesn't have to worry about 487 00:21:22,490 --> 00:21:24,998 whether there's something observing them or not. 488 00:21:24,998 --> 00:21:26,860 LiveData takes care of unsubscribing 489 00:21:26,860 --> 00:21:29,750 any observers if they're no longer active. 490 00:21:29,750 --> 00:21:31,610 When they subscribe again, they get 491 00:21:31,610 --> 00:21:35,100 the latest values from the LiveData objects. 492 00:21:35,100 --> 00:21:36,370 Alright, so I'm gonna stop the video here 493 00:21:36,370 --> 00:21:38,450 because this turned into a marathon one. 494 00:21:38,450 --> 00:21:39,870 In the next video, we're gonna make 495 00:21:39,870 --> 00:21:41,748 a slight change to the view model 496 00:21:41,748 --> 00:21:45,760 so that it doesn't expose MutableLiveData objects. 497 00:21:45,760 --> 00:21:47,060 See you in the next video.