1 00:00:03,740 --> 00:00:09,800 G'day everyone, welcome back. Okay we've got the layout for our custom dialog. 2 00:00:09,800 --> 00:00:15,980 In this video, we'll see how to display it when the user taps the about TaskTimer item, 3 00:00:15,980 --> 00:00:21,470 that's in the menu. MainActivity is responsible for responding to menu items 4 00:00:21,470 --> 00:00:23,919 and we'll be adding the code in there. 5 00:00:23,920 --> 00:00:27,660 We're going to create a showAboutDialog function, 6 00:00:27,660 --> 00:00:31,720 and call that from our onOptionsItemSelected function. 7 00:00:31,720 --> 00:00:36,940 We'll start with a field to store a reference to our dialog, which I'll add after the 8 00:00:36,949 --> 00:00:40,960 mTwoPane variable. 9 00:00:53,719 --> 00:00:59,690 If we're going to display the dialogue in a showAboutDialog function, you 10 00:00:59,690 --> 00:01:04,580 may be wondering why we need to store a reference to it in a field, instead of 11 00:01:04,580 --> 00:01:09,770 using a local variable. I've sort of given that away with the comment. Okay, 12 00:01:09,770 --> 00:01:15,820 we'll call the function in onOptionsItemSelected, 13 00:01:19,920 --> 00:01:25,430 and we'll add the function immediately below. 14 00:01:28,580 --> 00:01:34,200 This is pretty straightforward, after what we've done so far with the dialogues. 15 00:01:34,200 --> 00:01:38,100 The difference when using a custom dialogue is that we have to 16 00:01:38,100 --> 00:01:44,780 inflate the XML layout. That creates the view that the dialogue will be displaying. 17 00:01:44,780 --> 00:01:49,980 Next we create an alert dialog.builder object to do all the work 18 00:01:49,990 --> 00:01:55,780 of building our dialog for us. As I said, I'll come back to why we're using a 19 00:01:55,780 --> 00:02:01,160 field aboutDialog, instead of just a local variable. 20 00:02:01,160 --> 00:02:02,319 The setView function 21 00:02:02,320 --> 00:02:08,240 uses our inflated view as the dialogue's contents, but you probably worked that one out. 22 00:02:08,240 --> 00:02:12,720 Then we call the builder's create function to create the dialogue, and 23 00:02:12,720 --> 00:02:18,560 store a reference to it in our aboutDialog field. Dialogues normally cancel 24 00:02:18,580 --> 00:02:23,799 when you tap away from them on the screen. That's the default behavior. 25 00:02:23,800 --> 00:02:31,820 It's possible to change that by passing false to the dialogue's setCanceledOnTouchOutside function. 26 00:02:31,820 --> 00:02:34,660 If you want to prevent your dialogue from being cancelled when 27 00:02:34,660 --> 00:02:40,820 the user taps away from it, that's how you do it. Just pass false to this function. 28 00:02:40,820 --> 00:02:45,549 Remember they can still use the back button, though. Our dialogue won't have 29 00:02:45,549 --> 00:02:50,560 any buttons, which is why I've explicitly passed true to that function. 30 00:02:50,560 --> 00:02:56,079 Without buttons, the user won't have any obvious way to dismiss the dialogue, if the 31 00:02:56,079 --> 00:03:01,480 default behavior changes. They'll try tapping away from it and won't know that 32 00:03:01,480 --> 00:03:06,430 they have to use the back button. I prefer not to rely on default behavior, 33 00:03:06,430 --> 00:03:12,160 if it's crucial to the correct functioning of the app. Okay, next we set 34 00:03:12,160 --> 00:03:18,160 the title and the icon for the dialogue. Calling the builder's setTitle and setIcon 35 00:03:18,160 --> 00:03:23,079 function is a correct way to do this, but that comment sort of gives the 36 00:03:23,079 --> 00:03:29,590 game away. What we're doing here won't work. I'll explain why and put it right 37 00:03:29,590 --> 00:03:34,120 once we've seen that it's not working. The last bit of code displays the 38 00:03:34,120 --> 00:03:38,950 version information. We'll see where the version number comes from after seeing 39 00:03:38,950 --> 00:03:44,170 the dialogue working. Notice that we have to use findViewById 40 00:03:44,170 --> 00:03:50,050 here. we can't use a synthetic import because we have to look for the IDs in 41 00:03:50,050 --> 00:03:55,150 message view. Watch out for that, because your code will compile quite happily if 42 00:03:55,150 --> 00:04:00,880 you do use a synthetic import. It'll compile but it won't run, and you'll get 43 00:04:00,880 --> 00:04:05,830 an error because the TextView will be null. I'll say that again because 44 00:04:05,830 --> 00:04:12,010 it can be a tricky bug to find. If you try to use synthetic imports to refer to 45 00:04:12,010 --> 00:04:17,779 views in a dialog, your code will compile but it will crash when you run it. 46 00:04:17,779 --> 00:04:23,361 If you want to see that happening, change line 124. 47 00:04:23,361 --> 00:04:29,220 Copy and paste, comment out the first line 48 00:04:29,220 --> 00:04:37,140 and change aboutVersion to about_version. Android Studio adds a 49 00:04:37,140 --> 00:04:44,580 synthetic import of kotlinx.android.synthetic.main.about.* 50 00:04:44,580 --> 00:04:48,970 to the imports, and the code looks fine. I'll swap the comments around 51 00:04:48,970 --> 00:04:54,120 and leave the incorrect line in the code. 52 00:04:57,780 --> 00:05:03,790 The function finishes by showing the dialogue. We're getting a warning from 53 00:05:03,790 --> 00:05:10,100 Android Studio about passing null as the route view. We've discussed that before. 54 00:05:10,100 --> 00:05:14,760 It's not normally a good idea, because Android won't be able to apply styles 55 00:05:14,770 --> 00:05:21,070 and so forth to the layout. There are two exceptions, and we saw the first one when 56 00:05:21,070 --> 00:05:25,120 the layout we were creating was the root view, so there was no root to pass 57 00:05:25,120 --> 00:05:30,040 into the inflate function. The other exception to the rule is here, when 58 00:05:30,040 --> 00:05:34,690 creating a dialogue. Dialogues aren't part of the underlying activity. They 59 00:05:34,690 --> 00:05:39,730 display on top of it and are styled separately. Once again, there is no root 60 00:05:39,730 --> 00:05:42,380 view that we could sensibly use. 61 00:05:42,380 --> 00:05:44,560 Those are the only times when you should pass 62 00:05:44,560 --> 00:05:49,600 null as the root view, when inflating a layout. In all other cases, you'll have a 63 00:05:49,600 --> 00:05:54,340 view that you can use as the root and you shouldn't pass null. I want to show 64 00:05:54,340 --> 00:05:57,010 you how to disable these lint warnings when you're 65 00:05:57,010 --> 00:06:01,889 absolutely sure that you know what you're doing, and want to ignore them. 66 00:06:01,889 --> 00:06:08,130 I want to be completely clear here. This isn't a way to deal with these warnings. 67 00:06:08,130 --> 00:06:13,690 Most of the time, you should fix the problem that's causing them. It's very rarely 68 00:06:13,690 --> 00:06:17,800 that you should do what I'm about to show you. The warnings are there for a 69 00:06:17,800 --> 00:06:22,300 reason, and just disabling them for a quiet life is not an approach you should 70 00:06:22,300 --> 00:06:28,090 consider. Very occasionally, such as our call to the inflate function, the 71 00:06:28,090 --> 00:06:34,000 warnings aren't relevant. It's okay to disable the warning in cases like this. 72 00:06:34,000 --> 00:06:38,500 Click on the null argument in the call to inflate, and then use the light bulb 73 00:06:38,500 --> 00:06:43,210 to get suggestions for how to respond to the warning. The option I want to use 74 00:06:43,210 --> 00:06:48,820 here is the suppress option. That adds an annotation to this bit of code, to 75 00:06:48,820 --> 00:06:54,160 suppress the warning in this place only. Anywhere else that you try to pass null 76 00:06:54,160 --> 00:06:58,960 as the root view when inflating a layout, you'll still get the warning. 77 00:06:58,960 --> 00:07:06,060 Choose the suppress option, and the annotation's added to the code. That removes the 78 00:07:06,060 --> 00:07:10,540 warning from the function without disabling it anywhere else. 79 00:07:10,540 --> 00:07:15,360 It's a useful way to prevent the warnings from appearing, when you've made a deliberate 80 00:07:15,360 --> 00:07:19,880 decision to ignore them. That decision should be an informed one, though. 81 00:07:19,880 --> 00:07:23,979 Don't just go disabling warnings when you really should fix them. If you're 82 00:07:23,979 --> 00:07:28,660 wondering why you can't just ignore them, you can get Android Studio to stop on 83 00:07:28,660 --> 00:07:33,060 each warning, when you check your code into a version control system such as 84 00:07:33,060 --> 00:07:37,419 git, for example. That's a neat feature, as it helps you avoid checking in poor 85 00:07:37,419 --> 00:07:42,130 quality code for your colleagues to sneer at. But if it stops on warnings 86 00:07:42,130 --> 00:07:46,750 that you know you want to ignore, that'd get annoying. Suppressing the 87 00:07:46,750 --> 00:07:52,000 warnings gives you a way to cope with that situation. Our dialogue code's now complete, 88 00:07:52,000 --> 00:07:57,400 so run the app 89 00:07:57,400 --> 00:08:07,260 and use the About TaskTimer option in the menu to see it in action. 90 00:08:07,260 --> 00:08:19,020 In landscape mode, we use the information icon on the toolbar to display the About dialog. 91 00:08:19,020 --> 00:08:22,200 Hopefully, you're not feeling too underwhelmed right now, 92 00:08:22,210 --> 00:08:27,400 because I did warn you that the title and icon wouldn't appear. Everything else 93 00:08:27,400 --> 00:08:32,500 looks fine, though, so we've just got to fix those two items. Before going back to 94 00:08:32,500 --> 00:08:38,440 our code, tap away from the dialog and it should disappear. The reason we haven't 95 00:08:38,440 --> 00:08:43,779 got the title and icon, is to do with where in the code we're calling the 96 00:08:43,779 --> 00:08:50,110 setTitle and setIcon functions. If you call them after creating the dialogue, they 97 00:08:50,110 --> 00:08:55,660 don't work. You don't get an error, they just don't do anything. We need to call 98 00:08:55,660 --> 00:09:01,750 those two functions, before calling the builder.create function. I'll copy those 99 00:09:01,750 --> 00:09:10,300 two lines to the clipboard, and paste them in before the call to setView and create. 100 00:09:10,300 --> 00:09:15,160 I'll comment out the original calls. That way you'll still have them if you 101 00:09:15,160 --> 00:09:23,340 download this source code. I'll remove this comment and we'll run the app again. 102 00:09:23,340 --> 00:09:28,660 If we now check, we should get the complete dialog appearing. 103 00:09:28,660 --> 00:09:35,589 I'll dismiss this dialog, rotate to landscape, and check again. We're going to rotate 104 00:09:35,589 --> 00:09:45,320 back into portrait, but first, make sure the log cat's visible. 105 00:09:45,320 --> 00:09:51,700 In fact, delete the contents so we can see anything that's new. When I rotate the emulator with the 106 00:09:51,700 --> 00:09:57,600 dialog showing, we get an error. 107 00:09:57,600 --> 00:10:01,920 You'll probably have to scroll up to see it 108 00:10:03,480 --> 00:10:09,030 because it doesn't crash the app. Our app doesn't crash, but obviously, something's 109 00:10:09,030 --> 00:10:12,940 not quite right and Android's logging an error. 110 00:10:12,940 --> 00:10:14,520 The error is: 111 00:10:14,520 --> 00:10:21,500 activity learnprogramming.academy.tasktimer MainActivity has leaked a window, 112 00:10:21,500 --> 00:10:27,810 that was originally added here. So, we've got a memory leak because we're not 113 00:10:27,810 --> 00:10:32,850 dismissing our dialogue. A memory leak happens when the Java garbage collection 114 00:10:32,850 --> 00:10:38,870 mechanism can't reclaim the memory and other resources, that an object uses. 115 00:10:38,870 --> 00:10:44,300 A dialog that isn't dismissed is a good example of how to leak memory. 116 00:10:44,300 --> 00:10:50,100 We should dismiss the dialog when our activity is destroyed by Android. A good place to do 117 00:10:50,100 --> 00:10:54,930 that is in the onStop function. If you're not sure why that's a suitable 118 00:10:54,930 --> 00:11:00,360 place, refer back to the activity lifecycle video in section 4. 119 00:11:00,360 --> 00:11:05,010 We've already got onStop at the end of the class. We added it just to see it being 120 00:11:05,010 --> 00:11:09,980 called in the logcat, but now we've got a use for it. 121 00:11:13,500 --> 00:11:18,780 We check that the dialog's showing, using a safe call in case it's null, 122 00:11:18,780 --> 00:11:23,520 and then dismiss it if it is showing. That's why aboutDialog had to be a 123 00:11:23,530 --> 00:11:29,380 field rather than a local variable. We need a reference to the dialogue so that 124 00:11:29,380 --> 00:11:32,500 we can dismiss it here, in onStop. 125 00:11:32,500 --> 00:11:35,900 Okay, now we shouldn't see that error in the logcat 126 00:11:35,900 --> 00:11:40,440 when the dialogue's showing and we rotate the device. 127 00:11:50,170 --> 00:11:54,560 There's another couple of things I want to talk about before we leave dialogues 128 00:11:54,560 --> 00:11:59,329 and move on. In the next video, we'll have a look at where the version number came 129 00:11:59,329 --> 00:12:04,579 from in our dialogue. After that, we'll test those links we added in the dialog, 130 00:12:04,580 --> 00:12:09,000 and see how to make them work on older Android versions as well. 131 00:12:09,000 --> 00:12:12,040 See you in the next one.