1 00:00:04,870 --> 00:00:10,130 Alright, so for the last video in this section, we're going to look at what happens when we get an error, and 2 00:00:10,130 --> 00:00:12,590 the user resolves the cause of that error. 3 00:00:12,590 --> 00:00:18,540 Now we've seen how to check isUserRecoverableError error, and show the error dialogue if they can recover, 4 00:00:18,730 --> 00:00:24,160 but what we haven't done, is look at how our app can respond if the user does fix the error. 5 00:00:24,410 --> 00:00:27,750 So let's start by reminding ourselves of what happens on the device. 6 00:00:28,130 --> 00:00:32,900 So I've got my Nexus 5 emulator loaded, running Android Oreo API 26. 7 00:00:32,980 --> 00:00:37,620 Now, we'll just swing back over to that, and you can see that it's set up as from the last video. 8 00:00:37,780 --> 00:00:43,080 Now I can't uninstall YouTube from the emulator, but what I can do is disable it. So 9 00:00:43,330 --> 00:00:45,280 what I'm going to do is close down the app, and 10 00:00:45,500 --> 00:00:48,100 I'm going to go into settings and then Apps and notifications, 11 00:00:51,080 --> 00:00:54,530 go into settings, then Apps and notifications down here. 12 00:00:55,070 --> 00:00:56,760 That's the new name for it by the way, 13 00:00:56,920 --> 00:01:02,160 and now from there, tap in App info, takes me to list of the apps that are installed on this, this 14 00:01:02,260 --> 00:01:03,590 device, this emulator, 15 00:01:03,890 --> 00:01:07,950 and YouTube's going to be right down the bottom, because it's in alphabetical order. So there's the YouTube Player, so 16 00:01:08,170 --> 00:01:12,230 we're going to click on that. Well actually I'm not going to click on that YouTube Player because that's 17 00:01:12,230 --> 00:01:16,100 actually our app. We're going to click on the one directly above it, which is the YouTube App that we're 18 00:01:16,110 --> 00:01:23,450 trying to disable. You can see there's now an option for this to disable the YouTube App, so click on Disable, 19 00:01:23,810 --> 00:01:28,920 and again its telling us what will happen but we're OK with that, so click on Disable App. Alright, now click on the back 20 00:01:28,920 --> 00:01:33,580 button to eventually get back to the main screen again, then I can just close this down. 21 00:01:34,230 --> 00:01:39,780 So let's now run the app and see what happens. I'm going to click on that again to start it, and 22 00:01:39,930 --> 00:01:47,040 swing over to the emulator. OK I'm going to play a single video, and you can see now we're getting a different message, 23 00:01:47,060 --> 00:01:53,630 and that's because YouTube's disabled, and you can see here that the users give it a chance to fix the problem, 24 00:01:53,700 --> 00:01:56,770 in this case by enabling YouTube on their device. 25 00:01:57,150 --> 00:02:04,900 So I'm going to tap that, Enable YouTube App. Once I do that, we get taken into the settings for the YouTube App. 26 00:02:05,080 --> 00:02:09,340 OK I'm going to click on enable, then I'm going to click on back, and you can see what's happened here. 27 00:02:09,340 --> 00:02:13,870 Even though the YouTube App is now enabled, and the user in fact fixed the problem, but 28 00:02:13,870 --> 00:02:18,470 our app's are not responding to the fix. So we can get around that by going back, and clicking on Play 29 00:02:18,470 --> 00:02:24,400 a single video. It then works, and obviously that time the video starts playing. 30 00:02:24,790 --> 00:02:29,680 So, that's pretty impressive but it isn't the greatest user experience. 31 00:02:29,800 --> 00:02:34,900 Now what they'll probably expect, is once they'd fixed the problem, in other words once they'd enabled YouTube, the video 32 00:02:34,900 --> 00:02:40,270 should start playing straight away without them having to go back and sort of restart or replay the video. 33 00:02:40,620 --> 00:02:44,620 And in our purpose as App Developers, we live to keep our users happy. 34 00:02:44,620 --> 00:02:46,630 So what can we actually do about this? 35 00:02:46,960 --> 00:02:52,120 Well remember that request code that we passed to the getErrorDialog method in the YoutubeActivity class? Let's just 36 00:02:52,120 --> 00:02:53,220 go back and have a look at that. 37 00:02:55,380 --> 00:03:01,900 Go back to our YouTubeActivity class, and this request code that I'm talking about here on line 57, and 38 00:03:01,900 --> 00:03:09,050 we're then passing that as you can see on line 60, to the youTubeInitializationResult.getErrorDialog. 39 00:03:09,060 --> 00:03:13,660 Now the documentation for the getErrorDialog function that we looked at, mentioned something about the 40 00:03:13,660 --> 00:03:16,090 second parameter, so let's just go and bring that up briefly. 41 00:03:20,830 --> 00:03:25,810 And you can see here, talking about the Parameters requestCode, "The requestCode given when calling start 42 00:03:25,810 --> 00:03:30,370 ActivityForResult". It doesn't actually give us any more information than that. 43 00:03:30,370 --> 00:03:35,540 Now, so we've used startActivity to launch our other two activities for MainActivity when the buttons were tapped. Start 44 00:03:35,560 --> 00:03:42,310 ActivityForResult is similar, but it also allows a result to be passed back to the starting activity. 45 00:03:42,310 --> 00:03:46,880 So with a bit of guesswork, remembering that the source for the YouTube library isn't available, 46 00:03:46,930 --> 00:03:51,340 we can try to retrieve the result, but as it happens we'll actually fail. 47 00:03:51,820 --> 00:03:55,230 And that's why we don't cover this in the Java version of this course, 48 00:03:55,360 --> 00:03:58,920 but enough students have asked, and we've included it here in the Kotlin version, 49 00:03:59,100 --> 00:04:04,920 and we'll hopefully find time to address this, to add this video in the Java Android version of the course. 50 00:04:05,020 --> 00:04:09,930 So what we're going to do is, we're going to fail to get a useful response back from the dialogue, 51 00:04:10,120 --> 00:04:13,270 but what we do get turns out to be good enough. 52 00:04:13,270 --> 00:04:17,300 So let's start by having a look at what startActivity is all about. 53 00:04:17,860 --> 00:04:25,600 So we do a search in Google for it, so Google, and we're going to do a search for startActivityForResult. 54 00:04:30,680 --> 00:04:34,770 And you can see we've got this result here, 'Getting a Result from an Activity', in the Google documentation. 55 00:04:38,200 --> 00:04:45,120 So basically we can start another activity, and receive a callback, if we use startActivityForResult instead 56 00:04:45,120 --> 00:04:46,420 of startActivity. 57 00:04:46,830 --> 00:04:51,360 Now there's a section if you scroll down and read this, on how to receive the results, a bit further in 58 00:04:51,360 --> 00:04:57,810 this document, and we'll get the result when the other activity calls our onActivityResult method, and 59 00:04:57,810 --> 00:05:01,350 passes it the original request code and also our result code. 60 00:05:01,530 --> 00:05:04,760 And further, the document also talks about a resultCode of RESULT_ 61 00:05:04,770 --> 00:05:09,700 OK, and you can see this here, if resultCode equals RESULT_OK as an example there. And 62 00:05:09,970 --> 00:05:12,930 we can check that, and again a bit more Googling, and 63 00:05:12,940 --> 00:05:17,540 by following the reference links in our last search, we'll eventually get to another website, which 64 00:05:17,580 --> 00:05:23,450 I'm just going to paste in here now to save a bit of time, and we'll eventually get to here, 65 00:05:24,800 --> 00:05:26,360 and you can see we've got RESULT_ 66 00:05:26,390 --> 00:05:32,280 OK is minus one, constant value down here, and RESULT_CANCELLED is zero. 67 00:05:32,300 --> 00:05:34,850 So we can use that and put that to good effect. 68 00:05:34,850 --> 00:05:38,250 Now this next bit that I'm going to show you is a sort of a leap of faith, 69 00:05:38,420 --> 00:05:42,680 after all we haven't actually called any variant of startActivity, as far as we know. 70 00:05:42,680 --> 00:05:49,440 We've just used the getErrorDialog in our code to request that dialogue. You can see the code there on line 60. 71 00:05:49,700 --> 00:05:54,170 But what we don't know when the YouTube classes or what they're actually doing behind the scenes. 72 00:05:54,330 --> 00:05:59,720 Now we do provide a reference to our YoutubeActivity when we pass this to getErrorDialog, and you can see 73 00:05:59,720 --> 00:06:01,230 that again on line 60. 74 00:06:01,460 --> 00:06:06,890 So that method's quite capable of using a dialogue fragment, or even another activity on our behalf, 75 00:06:07,250 --> 00:06:13,130 to display the error dialog. Now the YouTube documentation mentions startActivityForResult. 76 00:06:13,340 --> 00:06:19,550 So all we can really do here is create an onActivityResult function, and see if it gets called. 77 00:06:19,550 --> 00:06:23,750 This is the leap of faith that we're talking about. Now all this may give you some idea why we didn't include 78 00:06:23,750 --> 00:06:27,590 this in the previous version of the course. It does get slightly worst, 79 00:06:27,650 --> 00:06:29,930 but let's go and give it a go and see what happens anyway. 80 00:06:30,430 --> 00:06:36,420 So I'm to going to come down here to the end of YoutubeActivity, and I'm going to put this function 81 00:06:36,420 --> 00:06:38,370 right down the bottom. I'm going to override that, so we've got this 82 00:06:41,120 --> 00:06:44,100 onActivityResult as you can see there, so I'm going to select that one. 83 00:06:44,490 --> 00:06:46,030 So let's actually add some code for it. 84 00:06:46,030 --> 00:06:48,770 Now we don't want the super call, so let's get rid of that. 85 00:06:48,770 --> 00:06:56,910 So we'll start with some logging, so Log.d parentheses tag comma, we'll call it onActivityResult, because obviously 86 00:06:56,990 --> 00:07:05,130 we want to see this being logged, that it's been called, called, then, with request code, dollar request code, and 87 00:07:07,920 --> 00:07:11,930 for result code, actually what we'll do just to make it more sensible here, 88 00:07:11,940 --> 00:07:28,310 we'll actually change this. We'll start with response code, resultCode, we'll close that down, 89 00:07:32,310 --> 00:07:36,580 so obviously Kotlin's getting a bit confused by what I'm typing here but the code will still work. 90 00:07:36,790 --> 00:07:43,480 So call the response code, resultCode for request dollar requestCode. 91 00:07:43,740 --> 00:07:55,860 OK, so on the next line if parenthesis requestCode, that's equal to DIALOG capitals underscore REQUEST 92 00:07:56,530 --> 00:08:06,520 underscore CODE, open a code block, Log.d TAG intent question mark 93 00:08:06,590 --> 00:08:15,520 dot toString, then a Log.d parentheses TAG intent question mark again, dot 94 00:08:15,590 --> 00:08:21,580 extras dot toString, and we've got an error there but we'll fix that in a moment. 95 00:08:21,590 --> 00:08:27,800 Now when you use startActivityForResult, you provide a requestCode. When the started activity 96 00:08:27,800 --> 00:08:31,000 calls your onActivityResult, it sends back that requestCode. 97 00:08:31,000 --> 00:08:35,960 So that's just requestCode here that we've got here in the log on line 112, that's actually showing 98 00:08:35,960 --> 00:08:38,549 us a parameter for the onActivityResult. 99 00:08:38,750 --> 00:08:43,549 And that's because you may be starting more than one activity, and you need to know which result goes 100 00:08:43,549 --> 00:08:44,470 with which one. 101 00:08:44,660 --> 00:08:50,790 And that's why I've used the DIALOG_REQUEST_CODE constant, and why we check it here, 102 00:08:51,110 --> 00:08:55,090 so better to clear that to get rid of that error. So let's go back up to the top of the class and do 103 00:08:55,090 --> 00:09:01,090 that. We'll put them immediately below the YoutubeActivity TAG, so private 104 00:09:02,060 --> 00:09:11,870 val DIALOG_REQUEST_CODE equals one, and obviously by doing that we'll fix the error that we had in our code. 105 00:09:11,870 --> 00:09:15,490 Now we're going to use that constant when calling the getErrorDialog method. 106 00:09:15,650 --> 00:09:19,680 So let's go back and do that. That's in the onInitializationFailure. Alright up here, so at the moment 107 00:09:20,890 --> 00:09:28,190 we've got getErrorDialog and we've got two arguments. We've got 'this' and we've got this request code. 108 00:09:28,320 --> 00:09:32,970 We're going to change this request code now to DIALOG_REQUEST_CODE, our constant we've just defined, like so, 109 00:09:36,170 --> 00:09:43,710 and now that we're not using the request code val here, so we'll get rid of that. Alright so back to our onActivity 110 00:09:43,710 --> 00:09:46,570 Result function, let's go back down to the bottom again. 111 00:09:46,620 --> 00:09:49,880 Now all we're doing at the moment, is logging the values that we get back. 112 00:09:50,100 --> 00:09:55,040 So we'd log the resultCode and requestCode and also the contents of the intent. 113 00:09:55,260 --> 00:09:59,740 So let's actually see what we do get back. So we're going to go back to our app, so 114 00:09:59,970 --> 00:10:03,420 firstly what I'm going to do is disable our YouTube again, the YouTube App. 115 00:10:03,800 --> 00:10:09,370 So I'm going to go back into Apps and notifications, App info, scroll 116 00:10:09,660 --> 00:10:14,070 right down to the bottom. There's our YoutTube App, 117 00:10:14,110 --> 00:10:17,010 we're going to disable that, Disable app, 118 00:10:18,010 --> 00:10:26,270 close this down. Alright, and we should get the same error we got last time. I click on Play a single video. 119 00:10:26,470 --> 00:10:32,430 The app, we got the same error that we got last time about enabling YouTube. Now if we've guessed correctly 120 00:10:32,430 --> 00:10:35,720 at this point, based on the hints in the documentation, 121 00:10:35,910 --> 00:10:41,530 we should get our onActivityResult function called, with a result of minus one, 122 00:10:41,610 --> 00:10:47,940 when we re-enable the YouTube App. So I'm going to open the Logcat first, then what I'm going to do, 123 00:10:48,230 --> 00:10:54,750 I'm going to filter the Logcat on YoutubeActivity, YoutubeActivity. 124 00:10:56,020 --> 00:11:03,350 So let's go back and click on Enable YouTube App, click on enable, then we're going to click on back, and we can see we've 125 00:11:03,350 --> 00:11:05,780 got something here now, back in our log. 126 00:11:06,520 --> 00:11:08,360 And you can see we've got request code, I'll put, 127 00:11:08,640 --> 00:11:13,550 I should've actually put request code there but you can see that we've got one returned there, and the response code 128 00:11:13,550 --> 00:11:14,700 returned was zero. 129 00:11:14,860 --> 00:11:19,910 So one was for the request code which is in line with the DIALOG_REQUEST_CODE, and the result we got 130 00:11:19,910 --> 00:11:21,060 back was zero. 131 00:11:21,440 --> 00:11:26,560 So, there's basically a problem there. We were expecting minus 1 for the resultCode but we got the resultCode 132 00:11:26,570 --> 00:11:31,450 of zero, which wasn't expected. So that implies that the user cancelled. 133 00:11:31,490 --> 00:11:34,400 Now I won't do this, but by all means do it yourself. 134 00:11:34,420 --> 00:11:40,520 but if we ran through that test again, and didn't enable YouTube, we'd still get zero return. 135 00:11:40,520 --> 00:11:46,250 So it seems that the getErrorDialog can't detect what the user did, once they've been taken into the settings, 136 00:11:46,250 --> 00:11:48,720 so just returns zero, in other words, regardless. 137 00:11:48,920 --> 00:11:53,880 So if we can't tell if the user's enabled YouTube, what actually can we do next? 138 00:11:54,800 --> 00:11:56,630 Well, what are our options in other words. 139 00:11:56,920 --> 00:12:02,510 Well if they did enable YouTube, we'd initialize our player again, but if they didn't enable YouTube, 140 00:12:02,840 --> 00:12:05,120 is there any harm in initializing the player again? 141 00:12:05,240 --> 00:12:08,290 What's the worst that can happen. We just end up right back here. 142 00:12:08,900 --> 00:12:10,750 So let's actually see how that works out. 143 00:12:10,910 --> 00:12:15,540 What we're going to do is we're going to ignore the response code and initialize the player again if the 144 00:12:15,550 --> 00:12:20,240 user did go into the settings, and our onActivityResult function gets called. 145 00:12:20,240 --> 00:12:25,310 So basically all we want to do is leave the logging there, and on the line after the two logs we're 146 00:12:25,310 --> 00:12:32,200 going to put playerView, and we get an error there which we'll talk about shortly, so playerView.initialize 147 00:12:33,640 --> 00:12:44,620 parentheses, getString parentheses R.string.GOOGLE_API_KEY parentheses comma this, then closing 148 00:12:44,620 --> 00:12:46,110 parentheses. 149 00:12:46,120 --> 00:12:50,400 Now we're getting that error because playerView's currently local to the onCreate method, 150 00:12:50,590 --> 00:12:55,650 so in other words we need to make the playerView available throughout the activity. So we come back up 151 00:12:55,660 --> 00:12:59,910 to the onCreate method, there's our playerView. 152 00:13:00,710 --> 00:13:07,880 So let's move that declaration. Move it out of there and I'm going to put it up here, just below our constant 153 00:13:07,880 --> 00:13:11,340 that we defined up here, the val for the DIALOG_REQUEST_CODE. 154 00:13:14,000 --> 00:13:20,700 Now what we need to do is make a little bit of a change to that, so val playerView equals, so we'll change that to 155 00:13:20,700 --> 00:13:27,070 by lazy, which we talked about before, then add a CODE block around that code. 156 00:13:27,390 --> 00:13:33,420 So, we have to use a lazy initialization which we've done here, because you can't instantiate a YouTube playerView 157 00:13:33,420 --> 00:13:38,340 until after we add it to the layout in onCreate, in case you're wondering why I've done it that way. 158 00:13:38,520 --> 00:13:44,070 Alright so with that done now, we should find that if the log had got an error, in our onActivityResult as you 159 00:13:44,070 --> 00:13:49,290 can see, so how does it behave at this point. Let's go back to our emulator first. 160 00:13:49,440 --> 00:13:53,700 Actually I'll close the app down, so I think I've re-enabled it, so I'll just get back in and make sure that 161 00:13:53,700 --> 00:14:03,510 it is disabled. Click on YouTube, disable that. OK we'll get out of that, then we'll run our app again, 162 00:14:06,620 --> 00:14:15,070 Play a single video, enable YouTube app, then we'll go back and I'll attempt to initialize a player again. This just 163 00:14:15,070 --> 00:14:21,100 resultS in the same thing. we're getting an error dialog appearing again, with the option to enable the app. 164 00:14:21,210 --> 00:14:25,630 Now if the user really doesn't want to enable YouTube, they'll have to dismiss the dialogue with the 165 00:14:25,630 --> 00:14:28,180 back button or by tapping the screen outside the dialogue. 166 00:14:28,330 --> 00:14:36,640 But what happens if we do enable YouTube? Click on enable, click on Enable the YouTube button again, and go back. 167 00:14:37,480 --> 00:14:41,530 This time you can see it now has enabled it and the video's started playing. 168 00:14:41,530 --> 00:14:43,660 So that's a definite improvement. 169 00:14:43,660 --> 00:14:48,350 Now the app will crash if we try to play anything from a StandaloneActivity, when YouTube's disabled 170 00:14:48,350 --> 00:14:52,780 of course. If you want to catch the 'activity not found' exception in there to prevent a crash, 171 00:14:52,840 --> 00:14:54,870 that would make a good exercise for you to try, but 172 00:14:55,070 --> 00:14:58,240 I'm not going to go through a solution for that. It's basic exception handling, 173 00:14:58,480 --> 00:15:00,910 and we've been doing a fair bit of that throughout the course. 174 00:15:00,920 --> 00:15:05,650 Alright, so that's the end of this section. In the next section we're going to create an app to download and view photos 175 00:15:06,010 --> 00:15:10,250 from Flickr, which is an image sharing site. So I'll see you in the next video.