1 00:00:05,630 --> 00:00:11,120 In this video we going to add the code to display the full photo when one of the items in the recycler view 2 00:00:11,130 --> 00:00:13,640 list is clicked, or tapped, to be more accurate. 3 00:00:13,930 --> 00:00:20,080 So let's have a look at our diagram to see what we're doing. Now in the last few videos we finished 4 00:00:20,080 --> 00:00:25,670 the recycler item click listener, to let our app respond to clicks and long presses on the items 5 00:00:25,670 --> 00:00:27,250 in the recycler adapter. 6 00:00:27,560 --> 00:00:30,870 So we've implemented a lot of the functionality of this app. 7 00:00:30,870 --> 00:00:37,190 We just need to get the three activities working together to display the full photos, and allow the tags 8 00:00:37,190 --> 00:00:40,450 to be changed to search for different photos. 9 00:00:40,490 --> 00:00:43,050 So we're going to implement the search facility later, 10 00:00:43,280 --> 00:00:46,830 but in this video we're going to focus on displaying the full photo. 11 00:00:46,920 --> 00:00:52,460 Now in the process, we're going to review how to launch one activity from another using intents, 12 00:00:53,160 --> 00:00:55,870 and that's a fundamental aspect of Android programming, 13 00:00:56,100 --> 00:00:58,410 and one we first saw in the YouTube app. 14 00:00:58,460 --> 00:01:04,849 So let's get main activity to launch the photo details activity, when a photo is tapped in the list. 15 00:01:04,849 --> 00:01:11,090 So we're going to start by creating that base class, base activity, that our activities will extend. 16 00:01:11,360 --> 00:01:16,890 The reason for doing that is to share function and properties amongst the number of different activities, 17 00:01:16,940 --> 00:01:23,150 three in our case. By defining common functions in a single class, and then having the other classes extend 18 00:01:23,150 --> 00:01:26,880 it, they'll get access to everything that's defined in the base class, 19 00:01:26,970 --> 00:01:32,730 and that means we don't have to include the same function in each activity. They're only going to be sharing a single function, 20 00:01:32,840 --> 00:01:35,040 but it's still a very valuable technique. 21 00:01:36,330 --> 00:01:42,850 Alright so coming back to Android Studio, let's start by right clicking the package name - New Kotlin 22 00:01:42,850 --> 00:01:48,490 File/Class, Kind Class, and the name for the new class, we're going to go with BaseActivity. 23 00:01:48,850 --> 00:01:50,620 Now although this is going to be an activity, 24 00:01:54,180 --> 00:01:59,400 we're not going to be using the wizard, and that's because we don't want a layout created for it and we 25 00:01:59,400 --> 00:02:01,460 don't want it adding to the manifest. 26 00:02:01,530 --> 00:02:06,730 So the name as you can see is BaseActivity, and it'll be a class which I've selected, and I'm going to click OK. 27 00:02:07,230 --> 00:02:10,229 So this is for our activity base class. 28 00:02:10,289 --> 00:02:19,050 So for that reason I'm going to extend app compat activity, colon AppCompatActivity, and we also want 29 00:02:19,140 --> 00:02:27,580 a TAG as per normal, so private val TAG equals double quotes BaseActivity. 30 00:02:27,630 --> 00:02:32,850 Now I'm also going to define a couple of string constants that we'll use when transferring data between 31 00:02:32,850 --> 00:02:36,070 activities. Now when we launched new activities in the YouTube app, 32 00:02:36,270 --> 00:02:39,260 we didn't need to send any data between the activities. 33 00:02:39,540 --> 00:02:44,910 But in this app, when we want to display a photo in the photo details activity, we'll need to send 34 00:02:44,910 --> 00:02:50,010 the details of the photo to the activity that's launched, and you'll see how to do that and why we need 35 00:02:50,010 --> 00:02:56,530 these strings in a minute. So I'm going to start by typing internal const 36 00:02:56,640 --> 00:03:08,320 val flickr query is equal to flickr query in double quotes. Then the next one, internal const 37 00:03:08,950 --> 00:03:12,200 val photo underscore transfer 38 00:03:12,690 --> 00:03:16,190 equals double quotes photo underscore transfer. 39 00:03:18,570 --> 00:03:22,140 And actually want I should have done is define that outside of the class. 40 00:03:22,230 --> 00:03:23,330 So I'll go ahead and do that. 41 00:03:23,370 --> 00:03:28,820 I'll cut that out because it should have actually been outside, like so. 42 00:03:28,860 --> 00:03:34,110 Now I've made these constants internal because they don't have to be available outside this project. 43 00:03:34,120 --> 00:03:38,790 It's unlikely that we'd include this package in another, but it's a good habit to develop. 44 00:03:38,820 --> 00:03:42,690 Now we're only going to add one function to this base class. 45 00:03:42,690 --> 00:03:48,150 Now the function will be used to show the toolbar, and it'll also allow an activity to choose whether the 46 00:03:48,170 --> 00:03:51,380 toolbar should have the home button or not. 47 00:03:51,390 --> 00:03:56,720 Now main activity won't need a home button because it is the home screen, but the other activities will. 48 00:03:57,040 --> 00:04:02,880 So when we launch photo details activity from main activity for example, the home button will allow 49 00:04:02,880 --> 00:04:06,100 us to get back, or go back, to main activity. 50 00:04:06,410 --> 00:04:11,840 Now as you're typing this in, if you're prompted for the toolbar package to import, make sure you choose 51 00:04:11,850 --> 00:04:19,000 the support library, the Android dot support dot v 7 dot widget dot toolbar, and you should see that happens automatically 52 00:04:19,000 --> 00:04:22,970 as I'm typing. So I'm going to create that function. 53 00:04:23,100 --> 00:04:33,930 It's going to be internal, fun activateToolbar parentheses, and it's going to enableHome colon Boolean, and then we 54 00:04:35,420 --> 00:04:37,940 get a code block. And we'll start with a log, 55 00:04:37,980 --> 00:04:47,700 so Log.d parentheses TAG comma double quote dot activateToolbar. Then we're going to do val 56 00:04:47,880 --> 00:04:55,340 toolbar is equal to findViewById, and within the diamond operator we're going to do View, 57 00:04:56,270 --> 00:05:05,490 and within the parentheses it's going to be R dot id dot toolbar, closing parentheses as Toolbar with a capital T. And 58 00:05:05,490 --> 00:05:08,570 again this is where I said it's important to choose the correct import. 59 00:05:08,600 --> 00:05:11,080 So we want to choose the android dot support dot 60 00:05:11,120 --> 00:05:13,260 v 7 dot widget. 61 00:05:13,830 --> 00:05:21,110 OK, next we're going to type setSupportAction toolbar, toolbar, 62 00:05:21,450 --> 00:05:23,620 and then finally supportAction 63 00:05:23,630 --> 00:05:31,410 Bar questionmark dot set Display Home as up enabled, enableHome. 64 00:05:32,580 --> 00:05:33,920 So that's actually the code. 65 00:05:34,170 --> 00:05:39,960 That's our base activity created, and actually I should have fixed that up as well, added a parentheses, or add a parentheses 66 00:05:39,960 --> 00:05:40,780 at the end of that 67 00:05:40,920 --> 00:05:47,580 to make that line valid, because we're obviously extending AppCompatActivity. So again that's all there 68 00:05:47,580 --> 00:05:52,770 is to base activity. It's created. The other activities in the app will extend this class instead of 69 00:05:52,980 --> 00:05:59,100 AppCompatActivity, so that they can use this activate toolbar function. And all the function does is inflate 70 00:05:59,100 --> 00:06:05,600 the toolbar from the toolbar dot XML file, then use the setSupportActionBar function with the inflated 71 00:06:05,610 --> 00:06:09,500 toolbar, to put the toolbar in place at the top of the screen. 72 00:06:09,570 --> 00:06:13,580 Now the action bar will automatically add the home button if we tell it to. 73 00:06:13,950 --> 00:06:18,950 And you probably guessed that we do that by getting a reference to the new action bar, and calling setDisplayHome 74 00:06:19,130 --> 00:06:26,120 AsUpEnabled to enable or disable the home button, depending on the parameter passed into the function. 75 00:06:26,130 --> 00:06:31,890 Now there's one other important change we need to make. Kotlin defaults to not allowing classes to be 76 00:06:31,890 --> 00:06:33,090 subclassed. 77 00:06:33,090 --> 00:06:35,540 Now we do want to extend base activity. 78 00:06:35,550 --> 00:06:37,680 That's the reason we're creating it after all. 79 00:06:37,860 --> 00:06:41,260 So to allow it to be extended, we have to go back and mark it as open. 80 00:06:41,280 --> 00:06:45,990 So we're going to do that on line 15, open to make it 81 00:06:46,110 --> 00:06:52,710 now able to be subclassed. Now if I just hover over that we're getting a warning over here, about 82 00:06:52,710 --> 00:06:55,320 the class not being registered in the manifest. 83 00:06:55,350 --> 00:06:58,710 That's actually why the class name itself's highlighted. 84 00:06:58,710 --> 00:07:00,840 Now the light bulb lets us suppress that warning, 85 00:07:01,010 --> 00:07:02,940 and that would actually be worth doing here. 86 00:07:03,220 --> 00:07:06,050 Our app won't create instances of base activity, 87 00:07:06,450 --> 00:07:08,360 so there's actually no point in registering it. 88 00:07:08,490 --> 00:07:11,400 So for that reason I suggest you actually use the toolbar, 89 00:07:14,930 --> 00:07:21,710 and select this option, suppress registered, so I'm going to click on that, and you can see that that warning disappears. 90 00:07:21,720 --> 00:07:28,270 And we've got this annotation at suppressed lint for registered. Alright, so now that we've done that, let's actually 91 00:07:28,270 --> 00:07:32,550 change the three activity classes and also do a bit of tidying up. 92 00:07:32,710 --> 00:07:33,980 So I'm going to start from the bottom. 93 00:07:34,030 --> 00:07:36,250 So let's open search activity first. 94 00:07:37,880 --> 00:07:39,010 I want to change this 95 00:07:39,020 --> 00:07:41,630 so I'm going to open up the import just so you can see what's happening. 96 00:07:41,630 --> 00:07:47,100 We should find, as I start removing some of these things, that the imports get updated. 97 00:07:47,160 --> 00:07:55,220 So firstly for AppCompatActivity, we want to remove that and make that BaseActivity. 98 00:07:55,290 --> 00:08:00,800 Now I've seen that the import's updated automatically, and we also want to make sure now that we've got a 99 00:08:00,800 --> 00:08:03,030 tag, so let's add a tag as well. 100 00:08:03,080 --> 00:08:10,170 So we'll do a private val TAG equals SearchActivity. 101 00:08:10,250 --> 00:08:12,950 And if Android Studio didn't delete the imports for you, 102 00:08:13,200 --> 00:08:17,630 then you want to go ahead and do those manually, but again because I've configured Android Studio to 103 00:08:18,470 --> 00:08:21,890 automatically remove unused imports, it did that for me automatically. 104 00:08:21,890 --> 00:08:27,250 Now in the onCreate method, we want to use the ActivateToolbar function to set the toolbar. 105 00:08:27,390 --> 00:08:32,809 So we want to comment out this setSupportAction toolbar line, and what we can actually do is just actually remove 106 00:08:32,809 --> 00:08:34,390 both of those lines. 107 00:08:34,940 --> 00:08:41,179 And instead what we want to do is call our function activateToolbar, 108 00:08:41,610 --> 00:08:43,440 and we want to pass true to this case, 109 00:08:43,490 --> 00:08:49,560 in this case, because we do want the home button to appear on the search activity, and we want to do a Log.d 110 00:08:49,570 --> 00:08:58,910 parentheses TAG comma double quotes, and it's going to be dot onCreate ends, and I didn't put one at the 111 00:08:58,910 --> 00:09:01,200 start so let's do that as well. 112 00:09:01,220 --> 00:09:03,890 First line there, starts. 113 00:09:07,390 --> 00:09:11,710 Now make sure that you do keep an eye on the imports and delete any unused ones that may get left 114 00:09:11,710 --> 00:09:12,130 behind, 115 00:09:12,130 --> 00:09:17,550 again, if you haven't configured your version of Android Studio to automatically reorganise imports. 116 00:09:18,030 --> 00:09:23,270 Alright so that's search activity. We need to do the same thing for photo details activity, so 117 00:09:23,290 --> 00:09:24,500 let's bring that on the screen. 118 00:09:28,070 --> 00:09:35,720 So firstly I'm going to change AppCompatActivity again to be BaseActivity, and I'm going to take the opportunity 119 00:09:35,720 --> 00:09:42,230 to clean the fab code up, because we're not going to be calling that anymore. I'm going to delete 120 00:09:42,230 --> 00:09:44,480 that, and also delete this set 121 00:09:44,470 --> 00:09:46,880 Action, setSupportAction toolbar. 122 00:09:47,220 --> 00:09:54,010 And instead what we're going to do is call the activateToolbar, passing true because again for photo details 123 00:09:54,290 --> 00:10:01,070 activity, we do want the home button to be an option so that we can go back to the main activity. So again 124 00:10:01,100 --> 00:10:03,080 tidy up any imports if necessary. 125 00:10:03,130 --> 00:10:06,740 So finally now, main activity, let's open that up. 126 00:10:06,930 --> 00:10:09,550 So we're going to start by replacing AppCompatActivity again, 127 00:10:11,600 --> 00:10:19,890 BaseActivity. We want our super and our set content view but we don't want the setSupportActionBar line there, I'm 128 00:10:19,910 --> 00:10:26,800 going to delete that. Instead what I'm going to do on that line and is do an activateToolbar. 129 00:10:26,960 --> 00:10:30,430 This time we want to pass false, 130 00:10:30,680 --> 00:10:34,760 and that's because we don't want the home button to appear, because we're already on the home page which 131 00:10:34,760 --> 00:10:38,600 is main activity. Now check the imports here as well. 132 00:10:38,610 --> 00:10:42,100 Now the synthetic import of activity underscore main isn't needed, 133 00:10:42,320 --> 00:10:45,380 now that we're not accessing the toolbar directly. 134 00:10:45,570 --> 00:10:51,420 At the moment here you can see we've just got content underscore main still showing. Android Studio won't always delete 135 00:10:51,440 --> 00:10:53,900 the import though, so keep that in mind. 136 00:10:54,170 --> 00:10:56,070 OK so that's the the housekeeping out of the way. 137 00:10:56,220 --> 00:11:01,630 It's time now to make our onItemClick methods, functions do something useful. At the moment 138 00:11:01,630 --> 00:11:04,690 if you recall, they're only just displaying Toast messages. 139 00:11:04,760 --> 00:11:10,640 So what we want is for a tap, or a long press of an item in the recycler view to launch the photo 140 00:11:10,630 --> 00:11:15,040 details activity, providing it with the details of the photograph to display. 141 00:11:15,530 --> 00:11:18,580 So we have to decide which of the two events to use, 142 00:11:18,860 --> 00:11:24,830 and for most applications, the onItemClick function's probably the one that users would expect. 143 00:11:24,830 --> 00:11:28,320 In other words they just tap the photo and it opens full screen for viewing. 144 00:11:28,410 --> 00:11:32,540 Now there are some apps where you might decide to use the long press instead. 145 00:11:32,570 --> 00:11:38,210 For example, a sat nav application aimed at motorcyclists. If you consider the use case for an app like 146 00:11:38,210 --> 00:11:45,260 that, the phone or tablet would probably be inside a waterproof case, fixed to the handlebars, and the rider 147 00:11:45,320 --> 00:11:47,150 would often be wearing thick gloves. 148 00:11:47,420 --> 00:11:48,970 So in a situation like that, 149 00:11:49,100 --> 00:11:52,640 the long press is a better option to make the app more usable, 150 00:11:52,850 --> 00:11:55,580 otherwise it's far too easy for the rider to trigger a 151 00:11:55,590 --> 00:11:56,920 tap when they didn't intend to. 152 00:11:57,410 --> 00:12:03,690 So the conditions that the app will be used in, will help to decide which touch event we should be using. 153 00:12:03,710 --> 00:12:07,330 So I'm going to use the long press, just so that we could be sure it works 154 00:12:07,340 --> 00:12:12,700 after our discussion in the previous video. And if you remember the long press function in the recyclerItem 155 00:12:12,770 --> 00:12:16,410 ClickListener, didn't return a true or false value. 156 00:12:16,430 --> 00:12:20,960 So this is actually a good opportunity to make sure it still works despite that. 157 00:12:21,410 --> 00:12:25,340 So we're going to modify the onItemLongClick function. 158 00:12:25,340 --> 00:12:29,810 So I'm just going to come down here, and you can see that we've got the code here. What I'm going to do 159 00:12:29,810 --> 00:12:34,750 first is get rid of the Toast message now because we're going to be using that anymore. 160 00:12:35,560 --> 00:12:43,010 And the code we want to introduce here is val photo equals flickrRecyclerViewAdaper 161 00:12:43,550 --> 00:12:53,180 dot getPhoto parentheses position. I accidentally added two parentheses there, it should just be the one. Then on 162 00:12:53,190 --> 00:12:53,680 the next line, 163 00:12:53,690 --> 00:13:07,400 if parentheses photo is not equal to null, code block, then we're going to do val intent equals Intent parentheses this comma, 164 00:13:08,220 --> 00:13:13,980 and it's going to be PhotoDetailsctivity colon colon class.java. 165 00:13:17,040 --> 00:13:27,400 On the next line we're going to put intent dot putExtra, putExtra parentheses, and it's going to be photo 166 00:13:27,400 --> 00:13:31,770 underscore transfer comma photo. 167 00:13:31,810 --> 00:13:33,010 Then we're going to startActivity 168 00:13:35,820 --> 00:13:37,700 intent. 169 00:13:37,870 --> 00:13:45,700 Now we had to write the getPhoto function if you recall, that we're calling here on line 47, to return the nullable 170 00:13:45,700 --> 00:13:49,590 photo type, which means we need this null check which we're doing on line 48. 171 00:13:49,820 --> 00:13:54,970 And next we use an intent to launch the PhotoDetailsActivity. 172 00:13:54,970 --> 00:14:01,090 Now this is exactly the same as we did in the YouTube app, when we launched the standalone activity from a button 173 00:14:01,090 --> 00:14:02,360 on the main screen. 174 00:14:02,500 --> 00:14:08,170 So we create a new intent, then we use this as a context, this is the code on line 49. 175 00:14:08,830 --> 00:14:14,890 That's fine because this class extends BaseActivity, which itself extends AppCompatActivity, 176 00:14:15,100 --> 00:14:21,490 so our main activity class is an activity, and can be used whenever a context is required. 177 00:14:21,490 --> 00:14:24,760 Now the second parameter is the activity class that we want to launch. 178 00:14:25,030 --> 00:14:31,420 And remember that we put colon colon class dot Java after the class name, to create a class literal 179 00:14:31,630 --> 00:14:34,870 that we can use to pass the class as a parameter. 180 00:14:34,870 --> 00:14:39,970 Now the next thing we need to do before starting the activity, is provide the photo details to the 181 00:14:39,970 --> 00:14:40,750 intent. 182 00:14:41,020 --> 00:14:46,720 Now we didn't have any information to pass to the new activity in the YouTube app, but here we have to tell the 183 00:14:46,720 --> 00:14:53,110 PhotoDetailsActivity which photo it should display, and we can do that by using the putExtra function 184 00:14:53,350 --> 00:14:54,690 to add data to the intent. 185 00:14:54,700 --> 00:15:00,790 You can see we're doing that on line 50. Now putExtra can be used to add simple data such as integers and 186 00:15:00,790 --> 00:15:07,570 strings, but you can also send more complex objects, even photo objects. And it actually works very similar 187 00:15:07,570 --> 00:15:11,280 to the bundles that we used when saving the activity state, 188 00:15:11,390 --> 00:15:16,170 for when it's restored after an orientation change. So you need to provide a key and a value, 189 00:15:16,390 --> 00:15:21,070 then whatever needs to access the data, uses the key to retrieve it. 190 00:15:21,340 --> 00:15:26,990 So that's why we defined the photo underscore transfer constant in the base activity class. 191 00:15:27,010 --> 00:15:31,640 So both main activity and photo details activity need to use the same key, 192 00:15:31,780 --> 00:15:38,080 so we use a constant to make sure we don't type the key differently in one class. But we have got this 193 00:15:38,080 --> 00:15:40,450 error though as you can see again on line 50. 194 00:15:40,630 --> 00:15:44,710 Now I said that putExtra can be used to add more complex objects to the intent, 195 00:15:44,860 --> 00:15:49,060 but there is a condition that the object must meet for it all to work, 196 00:15:49,060 --> 00:15:51,350 and that object has to be serializable. 197 00:15:51,460 --> 00:15:56,290 So I'm going to stop the video here, and in the next video we're going to look at the serializable interface, 198 00:15:56,620 --> 00:15:57,950 and how to implement it. 199 00:15:58,270 --> 00:15:59,450 So I'll see you in the next video.