Alright, so for the last video in this section, we're going to look at what happens when we get an error, and
the user resolves the cause of that error.
Now we've seen how to check isUserRecoverableError error, and show the error dialogue if they can recover,
but what we haven't done, is look at how our app can respond if the user does fix the error.
So let's start by reminding ourselves of what happens on the device.
So I've got my Nexus 5 emulator loaded, running Android Oreo API 26.
Now, we'll just swing back over to that, and you can see that it's set up as from the last video.
Now I can't uninstall YouTube from the emulator, but what I can do is disable it. So
what I'm going to do is close down the app, and
I'm going to go into settings and then Apps and notifications,
go into settings, then Apps and notifications down here.
That's the new name for it by the way,
and now from there, tap in App info, takes me to list of the apps that are installed on this, this
device, this emulator,
and YouTube's going to be right down the bottom, because it's in alphabetical order. So there's the YouTube Player, so
we're going to click on that. Well actually I'm not going to click on that YouTube Player because that's
actually our app. We're going to click on the one directly above it, which is the YouTube App that we're
trying to disable. You can see there's now an option for this to disable the YouTube App, so click on Disable,
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
button to eventually get back to the main screen again, then I can just close this down.
So let's now run the app and see what happens. I'm going to click on that again to start it, and
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,
and that's because YouTube's disabled, and you can see here that the users give it a chance to fix the problem,
in this case by enabling YouTube on their device.
So I'm going to tap that, Enable YouTube App. Once I do that, we get taken into the settings for the YouTube App.
OK I'm going to click on enable, then I'm going to click on back, and you can see what's happened here.
Even though the YouTube App is now enabled, and the user in fact fixed the problem, but
our app's are not responding to the fix. So we can get around that by going back, and clicking on Play
a single video. It then works, and obviously that time the video starts playing.
So, that's pretty impressive but it isn't the greatest user experience.
Now what they'll probably expect, is once they'd fixed the problem, in other words once they'd enabled YouTube, the video
should start playing straight away without them having to go back and sort of restart or replay the video.
And in our purpose as App Developers, we live to keep our users happy.
So what can we actually do about this?
Well remember that request code that we passed to the getErrorDialog method in the YoutubeActivity class? Let's just
go back and have a look at that.
Go back to our YouTubeActivity class, and this request code that I'm talking about here on line 57, and
we're then passing that as you can see on line 60, to the youTubeInitializationResult.getErrorDialog.
Now the documentation for the getErrorDialog function that we looked at, mentioned something about the
second parameter, so let's just go and bring that up briefly.
And you can see here, talking about the Parameters requestCode, "The requestCode given when calling start
ActivityForResult". It doesn't actually give us any more information than that.
Now, so we've used startActivity to launch our other two activities for MainActivity when the buttons were tapped. Start
ActivityForResult is similar, but it also allows a result to be passed back to the starting activity.
So with a bit of guesswork, remembering that the source for the YouTube library isn't available,
we can try to retrieve the result, but as it happens we'll actually fail.
And that's why we don't cover this in the Java version of this course,
but enough students have asked, and we've included it here in the Kotlin version,
and we'll hopefully find time to address this, to add this video in the Java Android version of the course.
So what we're going to do is, we're going to fail to get a useful response back from the dialogue,
but what we do get turns out to be good enough.
So let's start by having a look at what startActivity is all about.
So we do a search in Google for it, so Google, and we're going to do a search for startActivityForResult.
And you can see we've got this result here, 'Getting a Result from an Activity', in the Google documentation.
So basically we can start another activity, and receive a callback, if we use startActivityForResult instead
of startActivity.
Now there's a section if you scroll down and read this, on how to receive the results, a bit further in
this document, and we'll get the result when the other activity calls our onActivityResult method, and
passes it the original request code and also our result code.
And further, the document also talks about a resultCode of RESULT_
OK, and you can see this here, if resultCode equals RESULT_OK as an example there. And
we can check that, and again a bit more Googling, and
by following the reference links in our last search, we'll eventually get to another website, which
I'm just going to paste in here now to save a bit of time, and we'll eventually get to here,
and you can see we've got RESULT_
OK is minus one, constant value down here, and RESULT_CANCELLED is zero.
So we can use that and put that to good effect.
Now this next bit that I'm going to show you is a sort of a leap of faith,
after all we haven't actually called any variant of startActivity, as far as we know.
We've just used the getErrorDialog in our code to request that dialogue. You can see the code there on line 60.
But what we don't know when the YouTube classes or what they're actually doing behind the scenes.
Now we do provide a reference to our YoutubeActivity when we pass this to getErrorDialog, and you can see
that again on line 60.
So that method's quite capable of using a dialogue fragment, or even another activity on our behalf,
to display the error dialog. Now the YouTube documentation mentions startActivityForResult.
So all we can really do here is create an onActivityResult function, and see if it gets called.
This is the leap of faith that we're talking about. Now all this may give you some idea why we didn't include
this in the previous version of the course. It does get slightly worst,
but let's go and give it a go and see what happens anyway.
So I'm to going to come down here to the end of YoutubeActivity, and I'm going to put this function
right down the bottom. I'm going to override that, so we've got this
onActivityResult as you can see there, so I'm going to select that one.
So let's actually add some code for it.
Now we don't want the super call, so let's get rid of that.
So we'll start with some logging, so Log.d parentheses tag comma, we'll call it onActivityResult, because obviously
we want to see this being logged, that it's been called, called, then, with request code, dollar request code, and
for result code, actually what we'll do just to make it more sensible here,
we'll actually change this. We'll start with response code, resultCode, we'll close that down,
so obviously Kotlin's getting a bit confused by what I'm typing here but the code will still work.
So call the response code, resultCode for request dollar requestCode.
OK, so on the next line if parenthesis requestCode, that's equal to DIALOG capitals underscore REQUEST
underscore CODE, open a code block, Log.d TAG intent question mark
dot toString, then a Log.d parentheses TAG intent question mark again, dot
extras dot toString, and we've got an error there but we'll fix that in a moment.
Now when you use startActivityForResult, you provide a requestCode. When the started activity
calls your onActivityResult, it sends back that requestCode.
So that's just requestCode here that we've got here in the log on line 112, that's actually showing
us a parameter for the onActivityResult.
And that's because you may be starting more than one activity, and you need to know which result goes
with which one.
And that's why I've used the DIALOG_REQUEST_CODE constant, and why we check it here,
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
that. We'll put them immediately below the YoutubeActivity TAG, so private
val DIALOG_REQUEST_CODE equals one, and obviously by doing that we'll fix the error that we had in our code.
Now we're going to use that constant when calling the getErrorDialog method.
So let's go back and do that. That's in the onInitializationFailure. Alright up here, so at the moment
we've got getErrorDialog and we've got two arguments. We've got 'this' and we've got this request code.
We're going to change this request code now to DIALOG_REQUEST_CODE, our constant we've just defined, like so,
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
Result function, let's go back down to the bottom again.
Now all we're doing at the moment, is logging the values that we get back.
So we'd log the resultCode and requestCode and also the contents of the intent.
So let's actually see what we do get back. So we're going to go back to our app, so
firstly what I'm going to do is disable our YouTube again, the YouTube App.
So I'm going to go back into Apps and notifications, App info, scroll
right down to the bottom. There's our YoutTube App,
we're going to disable that, Disable app,
close this down. Alright, and we should get the same error we got last time. I click on Play a single video.
The app, we got the same error that we got last time about enabling YouTube. Now if we've guessed correctly
at this point, based on the hints in the documentation,
we should get our onActivityResult function called, with a result of minus one,
when we re-enable the YouTube App. So I'm going to open the Logcat first, then what I'm going to do,
I'm going to filter the Logcat on YoutubeActivity, YoutubeActivity.
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
got something here now, back in our log.
And you can see we've got request code, I'll put,
I should've actually put request code there but you can see that we've got one returned there, and the response code
returned was zero.
So one was for the request code which is in line with the DIALOG_REQUEST_CODE, and the result we got
back was zero.
So, there's basically a problem there. We were expecting minus 1 for the resultCode but we got the resultCode
of zero, which wasn't expected. So that implies that the user cancelled.
Now I won't do this, but by all means do it yourself.
but if we ran through that test again, and didn't enable YouTube, we'd still get zero return.
So it seems that the getErrorDialog can't detect what the user did, once they've been taken into the settings,
so just returns zero, in other words, regardless.
So if we can't tell if the user's enabled YouTube, what actually can we do next?
Well, what are our options in other words.
Well if they did enable YouTube, we'd initialize our player again, but if they didn't enable YouTube,
is there any harm in initializing the player again?
What's the worst that can happen. We just end up right back here.
So let's actually see how that works out.
What we're going to do is we're going to ignore the response code and initialize the player again if the
user did go into the settings, and our onActivityResult function gets called.
So basically all we want to do is leave the logging there, and on the line after the two logs we're
going to put playerView, and we get an error there which we'll talk about shortly, so playerView.initialize
parentheses, getString parentheses R.string.GOOGLE_API_KEY parentheses comma this, then closing
parentheses.
Now we're getting that error because playerView's currently local to the onCreate method,
so in other words we need to make the playerView available throughout the activity. So we come back up
to the onCreate method, there's our playerView.
So let's move that declaration. Move it out of there and I'm going to put it up here, just below our constant
that we defined up here, the val for the DIALOG_REQUEST_CODE.
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
by lazy, which we talked about before, then add a CODE block around that code.
So, we have to use a lazy initialization which we've done here, because you can't instantiate a YouTube playerView
until after we add it to the layout in onCreate, in case you're wondering why I've done it that way.
Alright so with that done now, we should find that if the log had got an error, in our onActivityResult as you
can see, so how does it behave at this point. Let's go back to our emulator first.
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
it is disabled. Click on YouTube, disable that. OK we'll get out of that, then we'll run our app again,
Play a single video, enable YouTube app, then we'll go back and I'll attempt to initialize a player again. This just
resultS in the same thing. we're getting an error dialog appearing again, with the option to enable the app.
Now if the user really doesn't want to enable YouTube, they'll have to dismiss the dialogue with the
back button or by tapping the screen outside the dialogue.
But what happens if we do enable YouTube? Click on enable, click on Enable the YouTube button again, and go back.
This time you can see it now has enabled it and the video's started playing.
So that's a definite improvement.
Now the app will crash if we try to play anything from a StandaloneActivity, when YouTube's disabled
of course. If you want to catch the 'activity not found' exception in there to prevent a crash,
that would make a good exercise for you to try, but
I'm not going to go through a solution for that. It's basic exception handling,
and we've been doing a fair bit of that throughout the course.
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
from Flickr, which is an image sharing site. So I'll see you in the next video.