1 00:00:05,250 --> 00:00:11,770 Alright, so now that we've added the YouTubePlayerView widget to our layout, and we've got our Google API key set up and 2 00:00:11,770 --> 00:00:13,300 added to this project, 3 00:00:13,300 --> 00:00:17,770 it's time to write the code that'll actually go ahead and play the videos for us. 4 00:00:17,770 --> 00:00:23,020 Now the first thing we need to do is initialize the player, which is how we tell it what our key is, and we 5 00:00:23,020 --> 00:00:26,110 do that by calling the players initialize method. 6 00:00:26,110 --> 00:00:31,950 So we want to be in YoutubeActivity, and the code we want to add needs to be below the code that we added here, 7 00:00:31,950 --> 00:00:33,580 the layout.addview, 8 00:00:33,580 --> 00:00:39,070 and if you remember the activity lifecycle from a few videos ago, the onCreate function's called when the 9 00:00:39,070 --> 00:00:40,330 activity's launched. 10 00:00:40,330 --> 00:00:45,940 So this is a good place to initialize the YouTubePlayer, and is where we're going to actually put it. 11 00:00:45,940 --> 00:00:55,290 So we're going to type playerView.initialize parentheses, and it's going to be getString, then 12 00:00:55,290 --> 00:00:56,910 another set of parentheses. It's going to 13 00:00:56,910 --> 00:01:08,680 be R.string dot, then I'm going to select GOOGLE_API_KEY, closing parentheses comma this. So we've already got the 14 00:01:08,680 --> 00:01:12,280 code to add the YouTube, or add a YouTubePlayerView to the layout, 15 00:01:12,280 --> 00:01:17,440 so we just needed to add one line to initialize it. And we need to give it the Google API key that we 16 00:01:17,440 --> 00:01:22,810 got in the previous video, and you can see on screen we're doing that by passing the resource ID to the 17 00:01:22,810 --> 00:01:24,180 getString function. 18 00:01:24,180 --> 00:01:30,950 So that looks up the ID in the string resources, and then returns the string, in our case the key. And initialize 19 00:01:30,950 --> 00:01:34,720 also needs onInitializedListener object. 20 00:01:34,720 --> 00:01:41,200 Now our class, if you recall, implements that interface and we've generated the basic stubs for the interface functions, 21 00:01:41,200 --> 00:01:48,940 that's the onInitializationSuccess and onInitializationFailure functions you've got on lines 36 and 40. Then 22 00:01:48,940 --> 00:01:55,000 remember that an interface is just a contract. The interface defines some functions that are required, 23 00:01:55,000 --> 00:02:01,720 and anything claiming to implement that interface must provide implementations for the functions. 24 00:02:01,720 --> 00:02:07,940 So imagine a bee interface. The interface basically says "Hey, I'm a bee, I can buzz and I can make honey". 25 00:02:07,940 --> 00:02:14,110 Now when something implements the bee interface, it must be able to buzz, and it must be able to make honey. 26 00:02:14,110 --> 00:02:18,820 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 27 00:02:18,820 --> 00:02:20,240 a bee, it's a yellow jacket - 28 00:02:20,240 --> 00:02:28,390 a wasp in Australia and the UK. Now our YouTubeActivity is a bee, or rather, it's a YouTubePlayer.OnInitialized 29 00:02:28,390 --> 00:02:30,220 Listener object. 30 00:02:30,220 --> 00:02:32,930 So in other words it implements the YouTubePlayer dot 31 00:02:32,930 --> 00:02:38,140 OnInitializedListener, and that means it has the functions that the interface defines. So we know that it 32 00:02:38,140 --> 00:02:43,960 must contain those functions, because Android Studio complained until we added them. Now if Android Studio 33 00:02:43,960 --> 00:02:47,410 hadn't complained, then our code still wouldn't have compiled. 34 00:02:47,410 --> 00:02:50,070 The compiler enforces that contract as well. 35 00:02:50,070 --> 00:02:57,010 Alright so our YouTubeActivity can be used wherever an object of type OnInitializedListener is needed. 36 00:02:57,010 --> 00:03:02,740 So anything that's given an instance of our YouTubeActivity class when it wants an OnInitializedListener, 37 00:03:02,740 --> 00:03:09,610 can be sure that our class will have an onInitializedSuccess and also an onInitializedFailure functions. 38 00:03:09,610 --> 00:03:11,350 Our functions don't do very much at the moment 39 00:03:11,350 --> 00:03:16,690 but at least calling them won't crash the program. We implement the interface and they do exist. 40 00:03:16,690 --> 00:03:21,690 So when a function needs an object of type YouTubePlayer.OnInitializedListener 41 00:03:21,690 --> 00:03:26,170 I can just pass 'this' for that parameter, and that's what I'm doing on line 33. 42 00:03:26,170 --> 00:03:31,600 So now that we've done that, the purpose of these two interface methods that we have to implement may become 43 00:03:31,600 --> 00:03:38,990 clearer. If the initialization is successful, the player will call our onInitializationSuccess method. 44 00:03:38,990 --> 00:03:43,430 If however it fails, our onInitializationFailure function gets called. 45 00:03:43,430 --> 00:03:47,600 So that way, a code can know whether it's OK to go ahead and play videos. 46 00:03:47,600 --> 00:03:52,250 Obviously if the initialization fails for some reason, then the videos won't play. 47 00:03:52,250 --> 00:03:57,140 So we're going to deal with the failure first, and we'll use that to notify the user that something's gone 48 00:03:57,140 --> 00:04:00,940 wrong by displaying a Toast message to the screen. 49 00:04:00,940 --> 00:04:05,420 Alright so there's quite a few things that can go wrong, and a quick peek at the source code of YouTube initialization 50 00:04:05,420 --> 00:04:09,440 results shows the various results that can be returned. 51 00:04:09,440 --> 00:04:14,070 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 52 00:04:14,070 --> 00:04:17,050 the Control key, otherwise, and click on down here the 53 00:04:17,050 --> 00:04:21,769 YouTubeInitializationResult in the parameter lists. Now click on that. 54 00:04:21,769 --> 00:04:27,240 So what we're interested in are the values of this Enum called YouTubeInitializationResult. 55 00:04:27,240 --> 00:04:28,790 You can see there's quite a few of them. 56 00:04:28,790 --> 00:04:33,560 So an Enum is basically a way to group Constants together into a type. 57 00:04:33,560 --> 00:04:38,200 Now there's a bit more to it than that, and as you can see, an Enum can contain code as well 58 00:04:38,200 --> 00:04:43,490 as just constants, but we're really only interested in these constant values at the start. 59 00:04:43,490 --> 00:04:47,000 Their names give an indication of the sorts of things that can go wrong. 60 00:04:47,000 --> 00:04:50,840 So the first one's easy - success means the initialization's succeeded, 61 00:04:50,840 --> 00:04:55,770 so we shouldn't see that in the failure callback function, but there's also code dealing with a network 62 00:04:55,770 --> 00:04:58,550 error, and an error connecting to YouTube itself. 63 00:04:58,550 --> 00:05:04,890 That's the ERROR_CONNECTING_TO_SERVICE as well as various codes concerning the 64 00:05:04,890 --> 00:05:07,160 YouTube service running on the device. 65 00:05:07,160 --> 00:05:13,010 Now if Google's YouTube Player isn't installed, then you can't use this API and those codes reflect that. 66 00:05:13,010 --> 00:05:16,820 So that's why we won't be able to test the finished app on an older emulator. 67 00:05:16,820 --> 00:05:21,090 We're going to have to use something that's got Google Play installed to run this app, and you saw in 68 00:05:21,090 --> 00:05:25,670 a previous section of this course, that we downloaded a version of the emulator that does support or 69 00:05:25,670 --> 00:05:29,420 does have released system images that have got Google Play. 70 00:05:29,420 --> 00:05:37,070 Now if you want more information on the detail of these error codes, I'll just bring up the browser and have a look. 71 00:05:37,070 --> 00:05:42,110 There's a link there, and you can find out more relating to specifically what they're all about. 72 00:05:42,110 --> 00:05:47,660 Now we could check to see which code we received, if there was an initialization failure, and display a suitable 73 00:05:47,660 --> 00:05:50,690 message, but Google have made things a bit easier for us. 74 00:05:50,690 --> 00:05:55,730 If we scroll past the summary, down here, of what these codes mean, there's a bit more detail on each one 75 00:05:55,730 --> 00:06:01,400 a bit further down, and for many of them it's just really elaborating what was in the summary table. 76 00:06:01,400 --> 00:06:07,920 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 77 00:06:07,920 --> 00:06:12,960 dialogue that'll enable the user to resolve the error. And that can be used if Google's YouTube Player 78 00:06:12,960 --> 00:06:15,110 has been disabled on the device, 79 00:06:15,110 --> 00:06:20,480 if it's not installed, which is the SERVICE_MISSING result code, or if it's out of date. 80 00:06:20,480 --> 00:06:24,920 So we could test for those three faults, and display the Google error dialogue, 81 00:06:24,920 --> 00:06:29,960 but it gets even easier than that, because if we scroll down even further, almost to the bottom of the page, 82 00:06:29,960 --> 00:06:35,120 there's a Boolean field there called isUserRecoverableError. Let's have a look at that, 83 00:06:35,120 --> 00:06:37,700 this one here, isUserRecoverableError, 84 00:06:37,700 --> 00:06:40,730 and that'll be true, if we should show the error dialogue. 85 00:06:40,730 --> 00:06:46,310 So armed with all that information we can write the code to deal with an initialization failure. 86 00:06:46,310 --> 00:06:51,200 So one thing that's not clear though in this documentation, is what the request code value is, that we 87 00:06:51,200 --> 00:06:53,310 should use when getting the error dialogue. 88 00:06:53,310 --> 00:06:58,460 Now Google do admit that sometimes their documentation isn't as helpful as it could be, and unfortunately this 89 00:06:58,460 --> 00:07:00,080 is one of those times. 90 00:07:00,080 --> 00:07:05,180 So I'm going to add another video at the end of this section, to show you how that request code's used, 91 00:07:05,180 --> 00:07:09,590 but fortunately, Google do provide a lot of sample code. 92 00:07:09,590 --> 00:07:14,060 So when you find yourself struggling to make sense of the documentation, have a look at the Google code 93 00:07:14,060 --> 00:07:20,810 samples, and life will be a lot easier. In fact the code we're about to use is almost identical to the on 94 00:07:20,810 --> 00:07:26,720 InitializationFailure callback, in a sample code that's included in the zip file we downloaded earlier 95 00:07:26,720 --> 00:07:28,080 in this section. 96 00:07:28,080 --> 00:07:32,900 Now hopefully the code generator will use more descriptive names for those parameters in future. 97 00:07:32,900 --> 00:07:38,460 And if you we back and have a look at the code. Alright so looking at this code now for the failure. 98 00:07:38,460 --> 00:07:39,780 Hopefully in the future, 99 00:07:39,780 --> 00:07:45,420 in terms of these parameters, the first ones are YouTubePlayer.Provider object, provide us an 100 00:07:45,420 --> 00:07:50,070 interface that defines the initialize method. We'll call that to start the player, or 101 00:07:50,070 --> 00:07:56,520 we do call that to start the player in onCreate. The second parameter, that youTubeInitializationResult 102 00:07:56,520 --> 00:07:59,700 that we've just been looking at, or is that, that's this one here. 103 00:07:59,700 --> 00:08:04,560 So we're going to rename the parameters so that they reflect what they are, and add some Toast messages. 104 00:08:04,560 --> 00:08:05,190 So let's change that. 105 00:08:05,190 --> 00:08:11,560 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, 106 00:08:11,560 --> 00:08:21,460 let's go for youTubeInitializationResult. Let's just do that so we can see things a little bit easier. 107 00:08:21,460 --> 00:08:26,370 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 108 00:08:26,370 --> 00:08:33,179 to start by typing val REQUEST_CODE equals zero, 109 00:08:33,179 --> 00:08:43,350 then if, then parentheses, youTubeInitializationResult.isUserRecoverableError. Then we're going 110 00:08:43,350 --> 00:08:53,260 to do youTubeInitializationResult.getErrorDialog, and the arguments will be this comma REQUEST_CODE 111 00:08:53,260 --> 00:09:02,440 parentheses dot show. Otherwise I'm going to add an else, and the else is going to say val errorMessage is equal to. 112 00:09:02,440 --> 00:09:03,420 Let's put an error message here, 113 00:09:03,420 --> 00:09:06,200 this is the error message that we're going to pop up on the screen, 114 00:09:06,200 --> 00:09:18,350 "There was an error initializing the YouTubePlayer", let's make that all one word, 115 00:09:18,350 --> 00:09:28,120 and we'll put the error message, "youTubeInitializationResult". Then we'll do Toast.makeText parentheses, this comma 116 00:09:28,120 --> 00:09:36,770 error message, errorMessage comma Toast.LENGTH_LONG, that stays on the screen a bit longer, dot 117 00:09:36,770 --> 00:09:44,260 show. Now I'm going to come back to that request that the getError dialogue method wants, the request code, but I've created 118 00:09:44,260 --> 00:09:48,700 a constant so that we don't see the number 0 in the code and wonder what it's doing there. 119 00:09:48,700 --> 00:09:54,880 We then check if user isUserRecoverableError is true, and use Google's error dialogue to display a message 120 00:09:54,880 --> 00:09:59,980 to the user if it is. If it isn't, the code formats a string message to include the name of the result 121 00:09:59,980 --> 00:10:03,710 Enum and uses a Toast message to display it on the screen. 122 00:10:03,710 --> 00:10:06,250 Now I don't think we've used a Toast message before. 123 00:10:06,250 --> 00:10:11,110 That's the name given for one of those messages that appear on your phone then fade away after a while. 124 00:10:11,110 --> 00:10:15,990 It's a useful way to notify the user of something without distracting them, by making them click an 125 00:10:15,990 --> 00:10:21,400 OK button. So in other words they get to see the message but can carry on working without being interrupted. 126 00:10:21,400 --> 00:10:26,770 So to create one, we just call a static make text function. Now we have to give it a context, which is something 127 00:10:26,770 --> 00:10:28,030 we've been doing a lot, 128 00:10:28,030 --> 00:10:32,170 and the message that you want to show. And you can also choose to make it stay on the screen for a little 129 00:10:32,170 --> 00:10:34,030 shorter time or a bit longer, 130 00:10:34,030 --> 00:10:39,190 by using the Toaster.LENGTH_SHORT or Toast.LENGTH_LONG constants, and you'll see 131 00:10:39,190 --> 00:10:43,900 that appearing on the screen when we run the app. Alright but we have got two errors at this point. 132 00:10:43,900 --> 00:10:48,700 The parameters are declared as nullable types, and Kotlin doesn't let us go merrily calling functions 133 00:10:48,700 --> 00:10:53,980 on something that could be null. Now is it likely that this callback function will be called with null arguments? 134 00:10:53,980 --> 00:10:56,370 Probably not, but can we guarantee that? 135 00:10:56,370 --> 00:11:01,030 Well no we can't. The YouTube API library hasn't been changed for a long time, 136 00:11:01,030 --> 00:11:07,100 so I think it's unlikely that Google will add the atNoneNull and atNullable annotations to it. 137 00:11:07,100 --> 00:11:11,650 Of course I don't know what Google are planning so that's just a guess, but I'm guessing on the side 138 00:11:11,650 --> 00:11:16,600 of caution, and I'm not going to modify the function signature because I just can't be sure. 139 00:11:16,600 --> 00:11:22,450 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 140 00:11:22,450 --> 00:11:27,910 or not. So we've got to work with what we've got. So the call to get error dialogue's easy. 141 00:11:27,910 --> 00:11:33,520 We're just going to use a safe call operator, when calling the dot getError dialogue and the show methods. 142 00:11:33,520 --> 00:11:38,750 So let's go ahead and add the safe call, two safe call operators. 143 00:11:38,750 --> 00:11:40,960 So the first one is youTubeInitializationResult. 144 00:11:40,960 --> 00:11:43,970 We want a questionmark in there, the safe call operator. 145 00:11:43,970 --> 00:11:47,980 Then we want to do exactly the same for the show as well. 146 00:11:47,980 --> 00:11:54,140 Now the condition above that, on line 45, that's not quite so obvious though, and that's because when we try to 147 00:11:54,140 --> 00:12:00,650 use a safe call operator, there, we still get an error. We get another error in fact,'Type mismatch, 148 00:12:00,650 --> 00:12:04,080 Required Boolean, Found Boolean, question mark'. 149 00:12:04,080 --> 00:12:08,630 And the reason for this is that the condition must evaluate to a Boolean, 150 00:12:08,630 --> 00:12:12,020 but we might have null, and we're using a nullable Boolean. 151 00:12:12,020 --> 00:12:16,070 Now the solution makes perfect sense but isn't very intuitive. 152 00:12:16,070 --> 00:12:20,030 In fact it looks like it involves doing something that programmers are taught not to do. 153 00:12:20,030 --> 00:12:22,020 So we compare it to true. 154 00:12:22,020 --> 00:12:27,230 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 155 00:12:27,230 --> 00:12:30,230 equals true, and that fixes the error. 156 00:12:30,230 --> 00:12:36,170 But as programmers were taught not to compare Booleans to true or false, because they are true or false, 157 00:12:36,170 --> 00:12:39,020 but remember that we're not dealing with a Boolean here. 158 00:12:39,020 --> 00:12:42,290 We've got a Boolean question mark, in other words a nullable Boolean. 159 00:12:42,290 --> 00:12:47,340 So consequently the equals equals true is acceptable and fixes the error. 160 00:12:47,340 --> 00:12:51,710 Now we could have performed an explicit null check as well, and that would also have worked, 161 00:12:51,710 --> 00:12:56,780 but I think this one, this solution's a little bit tidier, and because null curl equal true, 162 00:12:56,780 --> 00:12:59,930 we've also got an implicit null check in that condition. 163 00:12:59,930 --> 00:13:05,300 And in fact Kotlin's picked up on that, and if we have a look we've actually got a suggestion here, that the 164 00:13:05,300 --> 00:13:07,910 safe call operator on the next line is no longer needed. 165 00:13:07,910 --> 00:13:12,090 So we can actually remove both of them, so come over here and have a look, 'Unnecessary safe 166 00:13:12,090 --> 00:13:17,000 call on a non-null receiver of type YouTubeInitializationResult? 167 00:13:17,000 --> 00:13:24,520 So consequently, we can actually now remove those. Alright so we've got that fixed. 168 00:13:24,520 --> 00:13:27,580 Alright, so I'm going to end the video here. In the next one, 169 00:13:27,580 --> 00:13:32,080 we need to put some work in to get the app ready to be running on the Google emulator. 170 00:13:32,080 --> 00:13:33,480 So I'll see you in the next video.