Alright, so now that we've added the YouTubePlayerView widget to our layout, and we've got our Google API key set up and
added to this project,
it's time to write the code that'll actually go ahead and play the videos for us.
Now the first thing we need to do is initialize the player, which is how we tell it what our key is, and we
do that by calling the players initialize method.
So we want to be in YoutubeActivity, and the code we want to add needs to be below the code that we added here,
the layout.addview,
and if you remember the activity lifecycle from a few videos ago, the onCreate function's called when the
activity's launched.
So this is a good place to initialize the YouTubePlayer, and is where we're going to actually put it.
So we're going to type playerView.initialize parentheses, and it's going to be getString, then
another set of parentheses. It's going to
be R.string dot, then I'm going to select GOOGLE_API_KEY, closing parentheses comma this. So we've already got the
code to add the YouTube, or add a YouTubePlayerView to the layout,
so we just needed to add one line to initialize it. And we need to give it the Google API key that we
got in the previous video, and you can see on screen we're doing that by passing the resource ID to the
getString function.
So that looks up the ID in the string resources, and then returns the string, in our case the key. And initialize
also needs onInitializedListener object.
Now our class, if you recall, implements that interface and we've generated the basic stubs for the interface functions,
that's the onInitializationSuccess and onInitializationFailure functions you've got on lines 36 and 40. Then
remember that an interface is just a contract. The interface defines some functions that are required,
and anything claiming to implement that interface must provide implementations for the functions.
So imagine a bee interface. The interface basically says "Hey, I'm a bee, I can buzz and I can make honey".
Now when something implements the bee interface, it must be able to buzz, and it must be able to make honey.
Now it's not good enough just to be able to buzz. If it just buzzes but doesn't make honey, then it's not
a bee, it's a yellow jacket -
a wasp in Australia and the UK. Now our YouTubeActivity is a bee, or rather, it's a YouTubePlayer.OnInitialized
Listener object.
So in other words it implements the YouTubePlayer dot
OnInitializedListener, and that means it has the functions that the interface defines. So we know that it
must contain those functions, because Android Studio complained until we added them. Now if Android Studio
hadn't complained, then our code still wouldn't have compiled.
The compiler enforces that contract as well.
Alright so our YouTubeActivity can be used wherever an object of type OnInitializedListener is needed.
So anything that's given an instance of our YouTubeActivity class when it wants an OnInitializedListener,
can be sure that our class will have an onInitializedSuccess and also an onInitializedFailure functions.
Our functions don't do very much at the moment
but at least calling them won't crash the program. We implement the interface and they do exist.
So when a function needs an object of type YouTubePlayer.OnInitializedListener
I can just pass 'this' for that parameter, and that's what I'm doing on line 33.
So now that we've done that, the purpose of these two interface methods that we have to implement may become
clearer. If the initialization is successful, the player will call our onInitializationSuccess method.
If however it fails, our onInitializationFailure function gets called.
So that way, a code can know whether it's OK to go ahead and play videos.
Obviously if the initialization fails for some reason, then the videos won't play.
So we're going to deal with the failure first, and we'll use that to notify the user that something's gone
wrong by displaying a Toast message to the screen.
Alright so there's quite a few things that can go wrong, and a quick peek at the source code of YouTube initialization
results shows the various results that can be returned.
So let's jump directly to the source. I'm going to hold down the command key because I'm on a Mac, hold down
the Control key, otherwise, and click on down here the
YouTubeInitializationResult in the parameter lists. Now click on that.
So what we're interested in are the values of this Enum called YouTubeInitializationResult.
You can see there's quite a few of them.
So an Enum is basically a way to group Constants together into a type.
Now there's a bit more to it than that, and as you can see, an Enum can contain code as well
as just constants, but we're really only interested in these constant values at the start.
Their names give an indication of the sorts of things that can go wrong.
So the first one's easy - success means the initialization's succeeded,
so we shouldn't see that in the failure callback function, but there's also code dealing with a network
error, and an error connecting to YouTube itself.
That's the ERROR_CONNECTING_TO_SERVICE as well as various codes concerning the
YouTube service running on the device.
Now if Google's YouTube Player isn't installed, then you can't use this API and those codes reflect that.
So that's why we won't be able to test the finished app on an older emulator.
We're going to have to use something that's got Google Play installed to run this app, and you saw in
a previous section of this course, that we downloaded a version of the emulator that does support or
does have released system images that have got Google Play.
Now if you want more information on the detail of these error codes, I'll just bring up the browser and have a look.
There's a link there, and you can find out more relating to specifically what they're all about.
Now we could check to see which code we received, if there was an initialization failure, and display a suitable
message, but Google have made things a bit easier for us.
If we scroll past the summary, down here, of what these codes mean, there's a bit more detail on each one
a bit further down, and for many of them it's just really elaborating what was in the summary table.
Now let's just scroll down there and you can see that it starts talking about using getError dialogue or calling that, to get a localized error
dialogue that'll enable the user to resolve the error. And that can be used if Google's YouTube Player
has been disabled on the device,
if it's not installed, which is the SERVICE_MISSING result code, or if it's out of date.
So we could test for those three faults, and display the Google error dialogue,
but it gets even easier than that, because if we scroll down even further, almost to the bottom of the page,
there's a Boolean field there called isUserRecoverableError. Let's have a look at that,
this one here, isUserRecoverableError,
and that'll be true, if we should show the error dialogue.
So armed with all that information we can write the code to deal with an initialization failure.
So one thing that's not clear though in this documentation, is what the request code value is, that we
should use when getting the error dialogue.
Now Google do admit that sometimes their documentation isn't as helpful as it could be, and unfortunately this
is one of those times.
So I'm going to add another video at the end of this section, to show you how that request code's used,
but fortunately, Google do provide a lot of sample code.
So when you find yourself struggling to make sense of the documentation, have a look at the Google code
samples, and life will be a lot easier. In fact the code we're about to use is almost identical to the on
InitializationFailure callback, in a sample code that's included in the zip file we downloaded earlier
in this section.
Now hopefully the code generator will use more descriptive names for those parameters in future.
And if you we back and have a look at the code. Alright so looking at this code now for the failure.
Hopefully in the future,
in terms of these parameters, the first ones are YouTubePlayer.Provider object, provide us an
interface that defines the initialize method. We'll call that to start the player, or
we do call that to start the player in onCreate. The second parameter, that youTubeInitializationResult
that we've just been looking at, or is that, that's this one here.
So we're going to rename the parameters so that they reflect what they are, and add some Toast messages.
So let's change that.
So we're going to put, so instead of p zero, we'll call this one provider, and for the second one, instead of p one,
let's go for youTubeInitializationResult. Let's just do that so we can see things a little bit easier.
I'll get rid of the TODO not implemented any more, because we are implementing that now. Alright so let's start, we're going
to start by typing val REQUEST_CODE equals zero,
then if, then parentheses, youTubeInitializationResult.isUserRecoverableError. Then we're going
to do youTubeInitializationResult.getErrorDialog, and the arguments will be this comma REQUEST_CODE
parentheses dot show. Otherwise I'm going to add an else, and the else is going to say val errorMessage is equal to.
Let's put an error message here,
this is the error message that we're going to pop up on the screen,
"There was an error initializing the YouTubePlayer", let's make that all one word,
and we'll put the error message, "youTubeInitializationResult". Then we'll do Toast.makeText parentheses, this comma
error message, errorMessage comma Toast.LENGTH_LONG, that stays on the screen a bit longer, dot
show. Now I'm going to come back to that request that the getError dialogue method wants, the request code, but I've created
a constant so that we don't see the number 0 in the code and wonder what it's doing there.
We then check if user isUserRecoverableError is true, and use Google's error dialogue to display a message
to the user if it is. If it isn't, the code formats a string message to include the name of the result
Enum and uses a Toast message to display it on the screen.
Now I don't think we've used a Toast message before.
That's the name given for one of those messages that appear on your phone then fade away after a while.
It's a useful way to notify the user of something without distracting them, by making them click an
OK button. So in other words they get to see the message but can carry on working without being interrupted.
So to create one, we just call a static make text function. Now we have to give it a context, which is something
we've been doing a lot,
and the message that you want to show. And you can also choose to make it stay on the screen for a little
shorter time or a bit longer,
by using the Toaster.LENGTH_SHORT or Toast.LENGTH_LONG constants, and you'll see
that appearing on the screen when we run the app. Alright but we have got two errors at this point.
The parameters are declared as nullable types, and Kotlin doesn't let us go merrily calling functions
on something that could be null. Now is it likely that this callback function will be called with null arguments?
Probably not, but can we guarantee that?
Well no we can't. The YouTube API library hasn't been changed for a long time,
so I think it's unlikely that Google will add the atNoneNull and atNullable annotations to it.
Of course I don't know what Google are planning so that's just a guess, but I'm guessing on the side
of caution, and I'm not going to modify the function signature because I just can't be sure.
This library isn't open source, so we can't easily check the source code to see if null can be passed as an argument
or not. So we've got to work with what we've got. So the call to get error dialogue's easy.
We're just going to use a safe call operator, when calling the dot getError dialogue and the show methods.
So let's go ahead and add the safe call, two safe call operators.
So the first one is youTubeInitializationResult.
We want a questionmark in there, the safe call operator.
Then we want to do exactly the same for the show as well.
Now the condition above that, on line 45, that's not quite so obvious though, and that's because when we try to
use a safe call operator, there, we still get an error. We get another error in fact,'Type mismatch,
Required Boolean, Found Boolean, question mark'.
And the reason for this is that the condition must evaluate to a Boolean,
but we might have null, and we're using a nullable Boolean.
Now the solution makes perfect sense but isn't very intuitive.
In fact it looks like it involves doing something that programmers are taught not to do.
So we compare it to true.
Now let's just put the code in, then we'll talk about it, so down here we're going to add, at the end of the line, equals
equals true, and that fixes the error.
But as programmers were taught not to compare Booleans to true or false, because they are true or false,
but remember that we're not dealing with a Boolean here.
We've got a Boolean question mark, in other words a nullable Boolean.
So consequently the equals equals true is acceptable and fixes the error.
Now we could have performed an explicit null check as well, and that would also have worked,
but I think this one, this solution's a little bit tidier, and because null curl equal true,
we've also got an implicit null check in that condition.
And in fact Kotlin's picked up on that, and if we have a look we've actually got a suggestion here, that the
safe call operator on the next line is no longer needed.
So we can actually remove both of them, so come over here and have a look, 'Unnecessary safe
call on a non-null receiver of type YouTubeInitializationResult?
So consequently, we can actually now remove those. Alright so we've got that fixed.
Alright, so I'm going to end the video here. In the next one,
we need to put some work in to get the app ready to be running on the Google emulator.
So I'll see you in the next video.