1 00:00:04,700 --> 00:00:07,040 Alright so we're talking about the Uri 2 00:00:07,040 --> 00:00:09,530 Matcher in the last video, and there's 3 00:00:09,530 --> 00:00:11,960 the link to that UriMatcher class in 4 00:00:11,960 --> 00:00:13,820 the description under Content URI 5 00:00:13,820 --> 00:00:16,640 patterns, on the documentation page for 6 00:00:16,640 --> 00:00:18,529 creating Content Providers. So let's go 7 00:00:18,529 --> 00:00:19,970 and click on that and open it in a new 8 00:00:19,970 --> 00:00:23,869 tab. Let's have a look at this Uri 9 00:00:23,869 --> 00:00:26,990 Matcher class. So this class maps 10 00:00:26,990 --> 00:00:29,779 different content URIs to integer 11 00:00:29,779 --> 00:00:32,210 values, that we can use to decide what 12 00:00:32,210 --> 00:00:35,170 code to execute for various URIs. 13 00:00:35,170 --> 00:00:36,979 And you can see down here that it's 14 00:00:36,979 --> 00:00:40,309 described as a utility class to aid in 15 00:00:40,309 --> 00:00:42,799 matching URIs in content providers. 16 00:00:42,799 --> 00:00:46,339 And you can see there that the example 17 00:00:46,339 --> 00:00:48,170 defines a number of constant int values; 18 00:00:48,170 --> 00:00:50,720 PEOPLE is 1, PEOPLE_ID is 19 00:00:50,720 --> 00:00:53,930 2 and so forth. Most of Google's 20 00:00:53,930 --> 00:00:56,510 example code is still in Java, but it 21 00:00:56,510 --> 00:00:58,010 should be easy to see what's going on 22 00:00:58,010 --> 00:01:00,320 here. If the URI just referred to the 23 00:01:00,320 --> 00:01:02,629 PEOPLE table, the UriMatcher would 24 00:01:02,629 --> 00:01:05,600 return 1. If the URI also had an ID on 25 00:01:05,600 --> 00:01:08,420 the end, then Matcher would return 2. Now 26 00:01:08,420 --> 00:01:10,520 we can't do that magically, so further 27 00:01:10,520 --> 00:01:12,469 down in the example code, as we scroll down 28 00:01:12,469 --> 00:01:14,899 there, there's a lot of calls to the add 29 00:01:14,899 --> 00:01:18,049 URI method. These associate the various 30 00:01:18,049 --> 00:01:20,359 URI patterns with the values 31 00:01:20,359 --> 00:01:23,090 that we want the Matcher to return. For 32 00:01:23,090 --> 00:01:25,640 the people table without an ID, the Matcher 33 00:01:25,640 --> 00:01:27,530 is told to return the value of the 34 00:01:27,530 --> 00:01:30,380 people constant, which is 1. But if an 35 00:01:30,380 --> 00:01:32,840 ID is provided, then this code tells it to 36 00:01:32,840 --> 00:01:35,420 return PEOPLE_ID, which is two. 37 00:01:35,420 --> 00:01:37,549 So I'll talk about that hash in a minute, 38 00:01:37,549 --> 00:01:40,159 but it's just a wild card that'll match 39 00:01:40,159 --> 00:01:42,289 any number. And this example goes a 40 00:01:42,289 --> 00:01:44,539 bit further, and allows all the phone 41 00:01:44,539 --> 00:01:47,119 numbers for a specified person to be 42 00:01:47,119 --> 00:01:49,039 returned - that's PEOPLE_PHONES - 43 00:01:49,039 --> 00:01:51,679 or a specific phone number for that 44 00:01:51,679 --> 00:01:53,689 person - PEOPLE_PHONES 45 00:01:53,689 --> 00:01:56,509 _ID. So you might be asking why 46 00:01:56,509 --> 00:01:59,450 do we need an integer value for each URI? 47 00:01:59,450 --> 00:02:01,609 Well if we didn't use the UriMatcher, 48 00:02:01,609 --> 00:02:03,109 then it would have to do things like 49 00:02:03,109 --> 00:02:05,899 counting how many segments the URI path 50 00:02:05,899 --> 00:02:09,258 contains, then checking the value of each 51 00:02:09,258 --> 00:02:11,470 segment to decide what action to take. 52 00:02:11,470 --> 00:02:13,670 Now a bit further down the page - if I 53 00:02:13,670 --> 00:02:16,150 scroll down - you can see over here that 54 00:02:16,150 --> 00:02:18,440 Google have provided an example 55 00:02:18,440 --> 00:02:20,150 of how we can use a switch statement 56 00:02:20,150 --> 00:02:22,720 with the values returned by the Matcher, 57 00:02:22,720 --> 00:02:25,580 compared to an alternative if we don't 58 00:02:25,580 --> 00:02:28,640 use the Matcher. I can't quite get that 59 00:02:28,640 --> 00:02:30,380 all on the screen but you can see the 60 00:02:30,380 --> 00:02:33,350 gist of it there, and Java uses switch in 61 00:02:33,350 --> 00:02:35,360 case instead of Kotlons when, and the 62 00:02:35,360 --> 00:02:37,340 arrow, the right arrow. But hopefully, you 63 00:02:37,340 --> 00:02:39,140 get the general idea with his code even 64 00:02:39,140 --> 00:02:41,090 if you don't know Java. Now don't actually 65 00:02:41,090 --> 00:02:42,590 worry about these return values at 66 00:02:42,590 --> 00:02:44,690 the moment - we're coming back to those 67 00:02:44,690 --> 00:02:45,230 in a minute. 68 00:02:45,230 --> 00:02:47,810 The important thing here, is that the 69 00:02:47,810 --> 00:02:50,240 code that doesn't use the UriMatcher 70 00:02:50,240 --> 00:02:52,460 is a lot more complicated. So there's a 71 00:02:52,460 --> 00:02:54,340 UriMatcher code, and if I scroll down 72 00:02:54,340 --> 00:02:57,230 you can see there. I think clearly you'd 73 00:02:57,230 --> 00:02:58,610 agree that looks a lot more 74 00:02:58,610 --> 00:03:01,160 complicated, and that's because it has to 75 00:03:01,160 --> 00:03:03,230 check the number of segments, then check 76 00:03:03,230 --> 00:03:05,690 specific segments against a table name. 77 00:03:05,690 --> 00:03:07,550 So it's just more complicated. The Uri 78 00:03:07,550 --> 00:03:10,610 Matcher is making our lives easier by 79 00:03:10,610 --> 00:03:12,470 taking care of the matching, and just 80 00:03:12,470 --> 00:03:14,960 giving us an integer value to use in the 81 00:03:14,960 --> 00:03:17,150 switch statement, and obviously in the 82 00:03:17,150 --> 00:03:19,430 when statement if we're talking Kotlin. So 83 00:03:19,430 --> 00:03:20,870 now that we've been through that we can 84 00:03:20,870 --> 00:03:23,360 add the code for the buildUriMatcher 85 00:03:23,360 --> 00:03:25,489 function now, or rather add the rest of 86 00:03:25,489 --> 00:03:26,030 the code. 87 00:03:26,030 --> 00:03:28,040 Now Google have done it slightly 88 00:03:28,040 --> 00:03:30,200 differently here in the code just above 89 00:03:30,200 --> 00:03:33,070 this. I'll scroll up and have a look there, 90 00:03:33,070 --> 00:03:36,230 and you can see here they've added a static 91 00:03:36,230 --> 00:03:38,600 initializer block to call the addURI 92 00:03:38,600 --> 00:03:40,700 method. Tt'll be interesting to see 93 00:03:40,700 --> 00:03:42,170 what they suggest when they produce a 94 00:03:42,170 --> 00:03:44,450 Kotlin version of this example, but I 95 00:03:44,450 --> 00:03:45,890 suspect they'll do the same as what 96 00:03:45,890 --> 00:03:49,030 we're about to do. So going back to our code, 97 00:03:49,030 --> 00:03:51,980 so our lazy delegate that we defined on 98 00:03:51,980 --> 00:03:55,250 line 34, make sure that the UriMatcher 99 00:03:55,250 --> 00:03:57,260 instance is created when we need it, and 100 00:03:57,260 --> 00:03:59,690 lazy also ensures that there's only one 101 00:03:59,690 --> 00:04:01,970 instance created. Now if you read that 102 00:04:01,970 --> 00:04:04,160 article by Christophe Beyls, you'll 103 00:04:04,160 --> 00:04:06,350 see that he based the singleton holder 104 00:04:06,350 --> 00:04:09,830 class on the Kotlin lazy code. Alright so 105 00:04:09,830 --> 00:04:11,060 let's write a bit of code here now and 106 00:04:11,060 --> 00:04:13,160 expand on the buildUriMatcher 107 00:04:13,160 --> 00:04:15,050 method. So at the moment we've got, just 108 00:04:15,050 --> 00:04:17,060 pretty well an empty bit of code there - 109 00:04:17,060 --> 00:04:19,579 I'm going to start. Now firstly we'll 110 00:04:19,579 --> 00:04:21,560 look at the first URI we want to 111 00:04:21,560 --> 00:04:25,910 match, and it'll be e.g. content colon 112 00:04:25,910 --> 00:04:28,010 //learn 113 00:04:28,010 --> 00:04:31,249 programming.academy 114 00:04:31,249 --> 00:04:37,039 dot tasktimer.provider/Tasks 115 00:04:37,039 --> 00:04:39,319 with a capital T. So to add that to the 116 00:04:39,319 --> 00:04:40,339 Matcher we're going to type matcher 117 00:04:40,339 --> 00:04:43,519 dot addURI, and then in 118 00:04:43,519 --> 00:04:45,469 parentheses we'll start with our CONTENT 119 00:04:45,469 --> 00:04:48,199 _AUTHORITY comma. Then it'll be 120 00:04:48,199 --> 00:04:52,159 tasksContract dot tables TABLE 121 00:04:52,159 --> 00:04:57,379 _NAME comma, then TASKS. Then the 122 00:04:57,379 --> 00:04:58,549 next thing we want to match, if I just 123 00:04:58,549 --> 00:05:02,539 copy that comment. This time it'll be for 124 00:05:02,539 --> 00:05:05,269 a specific task, /8 125 00:05:05,269 --> 00:05:07,189 for argument's sake, and that would be 126 00:05:07,189 --> 00:05:12,289 mature.addURI, and in parentheses 127 00:05:12,289 --> 00:05:15,259 CONTENT_AUTHORITY comma. This time 128 00:05:15,259 --> 00:05:16,429 it's going to be, in double quotes, dollar 129 00:05:16,429 --> 00:05:19,579 sign left and right curly braces Tasks 130 00:05:19,579 --> 00:05:23,179 Contract.TABLE_NAME, then right, 131 00:05:23,179 --> 00:05:25,219 closing off the curly brace, the right 132 00:05:25,219 --> 00:05:27,799 curly brace there, forward slash and the 133 00:05:27,799 --> 00:05:30,229 hash for the wild card comma. This time 134 00:05:30,229 --> 00:05:34,369 it's going to be TASKS_ID. Then 135 00:05:34,369 --> 00:05:37,189 what I'm going to do is take a copy of 136 00:05:37,189 --> 00:05:42,409 these. We're going to change this - instead 137 00:05:42,409 --> 00:05:44,449 of TasksContract here we're going to go 138 00:05:44,449 --> 00:05:46,549 with, in this next part, 139 00:05:46,549 --> 00:05:51,739 TimingsContract - then that'll be TIMINGS 140 00:05:51,739 --> 00:05:52,459 on the end there. 141 00:05:52,459 --> 00:05:55,789 And don't worry about these errors, I'll 142 00:05:55,789 --> 00:05:58,869 talk about those shortly. And in the 143 00:05:58,869 --> 00:06:02,959 string it's going to be TimingsContract 144 00:06:02,959 --> 00:06:06,079 TABLE_NAME, and instead of TASKS_ID it's 145 00:06:06,079 --> 00:06:10,639 going to be TIMINGS_ID. And 146 00:06:10,639 --> 00:06:13,699 we'll add one more group here, and this 147 00:06:13,699 --> 00:06:16,419 will be for the DurationsContract. 148 00:06:16,419 --> 00:06:21,429 So I'll come over to there, type Durations, , 149 149 00:06:21,429 --> 00:06:23,599 Durations contract, and then on the end 150 00:06:23,599 --> 00:06:25,059 instead of TIMINGS it's going to be 151 00:06:25,059 --> 00:06:29,119 TASK_DURATIONS. And on the end 152 00:06:29,119 --> 00:06:30,409 here I'll just make that change - that's 153 00:06:30,409 --> 00:06:33,369 TASK_DURATIONS_ID. 154 00:06:33,369 --> 00:06:37,309 And then the actual string - instead of 155 00:06:37,309 --> 00:06:39,019 TimingsCcontract - is going to be 156 00:06:39,019 --> 00:06:42,970 DurationsContract. 157 00:06:42,970 --> 00:06:45,910 Okay, and again don't worry about the errors. 158 00:06:45,910 --> 00:06:48,340 I've included URIs for the timings 159 00:06:48,340 --> 00:06:50,470 and durations table that we'll be using 160 00:06:50,470 --> 00:06:52,630 later, because without them it's less 161 00:06:52,630 --> 00:06:55,450 obvious what this function's doing. Al 162 00:06:55,450 --> 00:06:57,070 right so when the function starts, it 163 00:06:57,070 --> 00:06:59,800 creates a new UriMatcher object and we 164 00:06:59,800 --> 00:07:02,620 can see that on line 38. The constructors 165 00:07:02,620 --> 00:07:05,200 parameter is a value to return if the 166 00:07:05,200 --> 00:07:07,720 route URI is matched. Now we don't 167 00:07:07,720 --> 00:07:10,030 want to re-match the route. A table name must be 168 00:07:10,030 --> 00:07:12,010 specified in the URL. Well that's why 169 00:07:12,010 --> 00:07:14,530 we're specifying NO_MATCH as 170 00:07:14,530 --> 00:07:18,190 the argument on line 38. Alright, so then 171 00:07:18,190 --> 00:07:20,050 we're adding the URIs that we want 172 00:07:20,050 --> 00:07:23,080 to match. So if the Tasks table is 173 00:07:23,080 --> 00:07:25,600 specified without an ID, the matcher will 174 00:07:25,600 --> 00:07:28,300 return 100, the value of our Tasks 175 00:07:28,300 --> 00:07:31,300 constant - that's the code on line 41. Now 176 00:07:31,300 --> 00:07:33,760 if an ID is also provided it'll return 177 00:07:33,760 --> 00:07:37,240 101 - the TASKS_ID constant, and 178 00:07:37,240 --> 00:07:40,180 that's the code on line 44. And for the 179 00:07:40,180 --> 00:07:44,350 Timings table we'll get 200 and 201, which 180 00:07:44,350 --> 00:07:46,180 is the value of TIMINGS and TIMINGS 181 00:07:46,180 --> 00:07:47,620 _ID - the constants that we've 182 00:07:47,620 --> 00:07:49,600 defined. Now we can scroll up and see 183 00:07:49,600 --> 00:07:52,150 those values again, the ones that we've 184 00:07:52,150 --> 00:07:53,590 defined up here earlier in the file, at 185 00:07:53,590 --> 00:07:57,190 the top of the file. Now just to be clear 186 00:07:57,190 --> 00:07:59,610 here, these numbers are totally arbitrary. 187 00:07:59,610 --> 00:08:01,810 We could have just set our constants to 188 00:08:01,810 --> 00:08:04,360 1, 2, 3 etc, which is what that Google 189 00:08:04,360 --> 00:08:06,550 example we just looked at, does. Now 190 00:08:06,550 --> 00:08:08,230 obviously we shouldn't use negative 1 191 00:08:08,230 --> 00:08:10,090 because that's the value of the Uri 192 00:08:10,090 --> 00:08:12,430 Matcher.NO_MATCH constant. And 193 00:08:12,430 --> 00:08:15,180 if you're wondering you can see that - 194 00:08:15,180 --> 00:08:17,530 I'll hover over it and click - you can see 195 00:08:17,530 --> 00:08:19,030 there that no matter is set defined to 196 00:08:19,030 --> 00:08:21,520 be negative 1. And if you haven't already 197 00:08:21,520 --> 00:08:23,290 done so, what I suggest you do is click 198 00:08:23,290 --> 00:08:25,030 on the documentation for one of these 199 00:08:25,030 --> 00:08:26,860 links. So I'm going to do the one for the 200 00:08:26,860 --> 00:08:29,500 first addURI, and it gives us some 201 00:08:29,500 --> 00:08:31,630 information. So you can see that the 202 00:08:31,630 --> 00:08:33,700 first parameter here's the authority, and 203 00:08:33,700 --> 00:08:35,830 the second one is the path that we want 204 00:08:35,830 --> 00:08:37,960 to match. So the path that we want to match 205 00:08:37,960 --> 00:08:40,450 can contain wild cards, either a hash 206 00:08:40,450 --> 00:08:43,360 or an asterisk. In this example we used 207 00:08:43,360 --> 00:08:45,280 a hash because we want to match any 208 00:08:45,280 --> 00:08:47,920 numeric ID. If you want to match any 209 00:08:47,920 --> 00:08:50,170 text, then you could use an asterisk 210 00:08:50,170 --> 00:08:51,880 which will match numbers as well as text. 211 00:08:51,880 --> 00:08:54,670 Now the use of the hash here is maybe 212 00:08:54,670 --> 00:08:56,850 unfortunate, as I mentioned in the 213 00:08:56,850 --> 00:08:58,139 previous video when were looking at 214 00:08:58,139 --> 00:09:01,050 URIs, because the hash character is used 215 00:09:01,050 --> 00:09:03,480 to separate a URI fragment in the 216 00:09:03,480 --> 00:09:05,790 specification that we looked at. So here 217 00:09:05,790 --> 00:09:07,500 though, the hash is something that the 218 00:09:07,500 --> 00:09:10,709 uriMatcher class interprets to mean any 219 00:09:10,709 --> 00:09:12,930 numeric value. And it's not used in the 220 00:09:12,930 --> 00:09:14,699 same way as the hash that separates a 221 00:09:14,699 --> 00:09:16,709 fragment from the rest of the URI that, 222 00:09:16,709 --> 00:09:20,160 again, we saw in the specification 223 00:09:20,160 --> 00:09:22,380 relating to URIs. Now there's no 224 00:09:22,380 --> 00:09:23,910 confusion as far as the class is 225 00:09:23,910 --> 00:09:25,860 concerned, because this second parameter 226 00:09:25,860 --> 00:09:29,190 is only matching the path part of the 227 00:09:29,190 --> 00:09:32,100 URI. So the third parameter is the code 228 00:09:32,100 --> 00:09:34,350 that we want the matcher to return, if it 229 00:09:34,350 --> 00:09:36,870 matches the URI. So go back to our code 230 00:09:36,870 --> 00:09:40,589 here; if the URI just refers to the Tasks 231 00:09:40,589 --> 00:09:43,199 table then the matcher would return 100, 232 00:09:43,199 --> 00:09:45,420 and if there's an ID on the end of the 233 00:09:45,420 --> 00:09:45,750 URI 234 00:09:45,750 --> 00:09:48,870 it'll return 101. And I've also included 235 00:09:48,870 --> 00:09:50,970 the code to match the timings and 236 00:09:50,970 --> 00:09:53,730 duration URIs, just so you can see 237 00:09:53,730 --> 00:09:55,230 how we get different values returned 238 00:09:55,230 --> 00:09:57,089 when dealing with the different 239 00:09:57,089 --> 00:09:59,040 URIs. Now at this point we haven't 240 00:09:59,040 --> 00:10:00,839 created the TimingsContract and 241 00:10:00,839 --> 00:10:03,449 DurationContract classes, so I'm going 242 00:10:03,449 --> 00:10:04,649 to comment out those lines just to 243 00:10:04,649 --> 00:10:07,019 remove the errors, but we'll uncomment 244 00:10:07,019 --> 00:10:08,819 this code once the classes have been 245 00:10:08,819 --> 00:10:13,920 written in later videos. So hopefully now, 246 00:10:13,920 --> 00:10:15,990 the reason for these constants that we 247 00:10:15,990 --> 00:10:17,939 defined at the top of the file should 248 00:10:17,939 --> 00:10:19,980 now be apparent. They're the values that 249 00:10:19,980 --> 00:10:22,410 the uriMatcher will be returning for us 250 00:10:22,410 --> 00:10:24,689 to ultimately match the URIs against. 251 00:10:24,689 --> 00:10:26,519 Alright so let's scroll down and look at the 252 00:10:26,519 --> 00:10:28,980 code, start implementing some code now. 253 00:10:28,980 --> 00:10:31,139 And we'll start by getting the onCreate 254 00:10:31,139 --> 00:10:33,389 method out of the way, then we can see 255 00:10:33,389 --> 00:10:36,240 how to use this your uriMatcher. Now 256 00:10:36,240 --> 00:10:38,430 although it's not important, I prefer to 257 00:10:38,430 --> 00:10:40,439 arrange these functions in a 258 00:10:40,439 --> 00:10:43,410 more logical order. So onCreate, to mind, 259 00:10:43,410 --> 00:10:46,139 should come first, and I expect to find 260 00:10:46,139 --> 00:10:48,569 it to the top of the class code. So I'm 261 00:10:48,569 --> 00:10:49,529 just going to move these functions 262 00:10:49,529 --> 00:10:50,939 around a little bit, so start with 263 00:10:50,939 --> 00:10:53,850 onCreate. So I'm just going to put that 264 00:10:53,850 --> 00:10:56,639 right at the top here, and the next one 265 00:10:56,639 --> 00:11:01,230 I'd expect to see there is getType - grab 266 00:11:01,230 --> 00:11:03,300 that as well - 267 00:11:03,300 --> 00:11:07,090 and put that below the onCreate. And the 268 00:11:07,090 --> 00:11:09,550 next one would be query, then we're going 269 00:11:09,550 --> 00:11:13,420 to move that. And then the other three; 270 00:11:13,420 --> 00:11:16,420 insert, update and delete, are fine in 271 00:11:16,420 --> 00:11:17,920 that order. And again, that's more a 272 00:11:17,920 --> 00:11:20,110 personal preference thing, but generally 273 00:11:20,110 --> 00:11:21,550 I would expect to be using things in 274 00:11:21,550 --> 00:11:23,590 that order, and therefore seeing the 275 00:11:23,590 --> 00:11:25,720 functions defined in that order as well. 276 00:11:25,720 --> 00:11:27,970 And what I'll also do is take the 277 00:11:27,970 --> 00:11:30,520 opportunity to remove the nullable type 278 00:11:30,520 --> 00:11:33,040 marker from all the Uri parameters. So let's 279 00:11:33,040 --> 00:11:39,820 go ahead and do that. Now these functions 280 00:11:39,820 --> 00:11:41,320 won't be called with a null Uri. 281 00:11:41,320 --> 00:11:44,230 If they are, something's gone wrong, 282 00:11:44,230 --> 00:11:45,940 and I'd much rather that was picked up 283 00:11:45,940 --> 00:11:47,890 by the compiler rather than having the 284 00:11:47,890 --> 00:11:50,230 app crash. And the same's actually true 285 00:11:50,230 --> 00:11:53,080 of the content value parameters - these 286 00:11:53,080 --> 00:11:55,570 ones here from the insert and update 287 00:11:55,570 --> 00:11:57,130 functions. There's really no point 288 00:11:57,130 --> 00:11:58,810 passing null for the values to insert, 289 00:11:58,810 --> 00:12:00,580 for example. It's just not going to work. 290 00:12:00,580 --> 00:12:02,980 So I'm going to remove the question mark 291 00:12:02,980 --> 00:12:03,370 there - 292 00:12:03,370 --> 00:12:05,860 the nullable type marker - from both the 293 00:12:05,860 --> 00:12:08,080 insert method for ContentValues, as well 294 00:12:08,080 --> 00:12:11,200 as the ContentValues for update. Alright, 295 00:12:11,200 --> 00:12:12,550 so getting back to our onCreate 296 00:12:12,550 --> 00:12:14,890 function, it's very simple - very, very 297 00:12:14,890 --> 00:12:16,990 simple. I'm not going to do anything in 298 00:12:16,990 --> 00:12:18,730 fact, in the code, but I'll start by 299 00:12:18,730 --> 00:12:22,450 deleting this TODO. Now we could create 300 00:12:22,450 --> 00:12:25,150 our database class in here, but as we've 301 00:12:25,150 --> 00:12:27,610 implemented it as a singleton, we'll just 302 00:12:27,610 --> 00:12:29,650 use the getInstance function whenever 303 00:12:29,650 --> 00:12:32,530 we need a database instance. But this on 304 00:12:32,530 --> 00:12:34,510 Create function has to be implemented 305 00:12:34,510 --> 00:12:37,270 though, and should return true. And just 306 00:12:37,270 --> 00:12:39,100 out of interest, it's worth demonstrating 307 00:12:39,100 --> 00:12:41,230 that we can't create instances of App 308 00:12:41,230 --> 00:12:43,630 Database, so I'll try and do that now. But 309 00:12:43,630 --> 00:12:45,540 I'll start with a log, Log.d 310 00:12:45,540 --> 00:12:50,740 parentheses TAG comma onCreate colon 311 00:12:50,740 --> 00:12:54,130 starts, and just to be consistent I'm going 312 00:12:54,130 --> 00:12:55,450 to add a colon up here - buildUri 313 00:12:55,450 --> 00:12:59,590 Matcher: starts as well, for the 314 00:12:59,590 --> 00:13:02,440 log. Alright, so I'm trying to create an 315 00:13:02,440 --> 00:13:05,230 instance of our AppDatabase; val app 316 00:13:05,230 --> 00:13:10,030 Database equals AppDatabase, and in 317 00:13:10,030 --> 00:13:14,170 parentheses context, and if I hover over 318 00:13:14,170 --> 00:13:15,730 it you can see, you "Cannot access init: 319 00:13:15,730 --> 00:13:16,390 it's 320 00:13:16,390 --> 00:13:18,490 private in AppDatabase". So that's 321 00:13:18,490 --> 00:13:20,410 actually what we want here: any attempt 322 00:13:20,410 --> 00:13:22,690 to create an AppDatabase instance will 323 00:13:22,690 --> 00:13:24,820 fail, and that's because we have to use 324 00:13:24,820 --> 00:13:27,040 the getInstance function. So I'm going 325 00:13:27,040 --> 00:13:28,510 to delete that line now just to remove 326 00:13:28,510 --> 00:13:34,920 that error, but what I will do is return true. 327 00:13:34,920 --> 00:13:36,760 Alright, so I mentioned that on 328 00:13:36,760 --> 00:13:39,640 Create's easy - it certainly was. I've added logging 329 00:13:39,640 --> 00:13:41,020 so that we can see the flow events in 330 00:13:41,020 --> 00:13:43,060 the logcat. But let's get back to using 331 00:13:43,060 --> 00:13:45,970 the uriMatcher - this time in the query 332 00:13:45,970 --> 00:13:49,540 function, which is the third implemented 333 00:13:49,540 --> 00:13:51,340 function. And I'm going to get back to 334 00:13:51,340 --> 00:13:54,040 getType, the getType function later. So 335 00:13:54,040 --> 00:13:55,960 the first parameter in the query, the 336 00:13:55,960 --> 00:13:58,210 query function here - and I'll delete the TO 337 00:13:58,210 --> 00:14:01,780 DO - so again, the first parameter is the 338 00:14:01,780 --> 00:14:04,000 URI. So we're going to start by 339 00:14:04,000 --> 00:14:06,070 using our uriMatcher to work out what 340 00:14:06,070 --> 00:14:08,290 kind of URI we've been given here. 341 00:14:08,290 --> 00:14:09,870 So I'm going to type Log.d 342 00:14:09,870 --> 00:14:13,090 parentheses TAG comma, as a comment 343 00:14:13,090 --> 00:14:17,470 query colon called with uri dollar 344 00:14:17,470 --> 00:14:19,690 uri, so I can see in logcat what it 345 00:14:19,690 --> 00:14:21,850 looks like. But the actual code will 346 00:14:21,850 --> 00:14:25,420 be val match equals uriMatcher dot 347 00:14:25,420 --> 00:14:28,270 match, in the parentheses, uri. 348 00:14:28,270 --> 00:14:30,160 Then we'll do a log for that as well; Log 349 00:14:30,160 --> 00:14:33,580 .d parentheses TAG comma, and it'll 350 00:14:33,580 --> 00:14:35,220 be query again : 351 00:14:35,220 --> 00:14:41,170 match is $match. Alright, at this 352 00:14:41,170 --> 00:14:43,030 point now we can use the value of match 353 00:14:43,030 --> 00:14:45,850 to decide which URI was passed into 354 00:14:45,850 --> 00:14:48,370 this query method, so that we know which 355 00:14:48,370 --> 00:14:51,010 table we should be using, for example. So 356 00:14:51,010 --> 00:14:52,450 the next thing we want to do, is use a 357 00:14:52,450 --> 00:14:55,030 queryBuilder to build the query that 358 00:14:55,030 --> 00:14:58,150 will be executed against the database. So 359 00:14:58,150 --> 00:15:00,280 to do that I'm going to type val query 360 00:15:00,280 --> 00:15:05,190 Builder colon and it's going to be SQLite 361 00:15:05,190 --> 00:15:09,820 QueryBuilder equals, and it's going to be 362 00:15:09,820 --> 00:15:13,180 SQLiteQueryBuilder. Now to get the 363 00:15:13,180 --> 00:15:14,770 documentation for the SQLiteQuery 364 00:15:14,770 --> 00:15:15,280 Builder, 365 00:15:15,280 --> 00:15:17,050 make sure you click on the type over here 366 00:15:17,050 --> 00:15:21,130 and not the constructor call. And you can 367 00:15:21,130 --> 00:15:22,750 see here that the documentation tells us 368 00:15:22,750 --> 00:15:25,630 that this is a convenience class that 369 00:15:25,630 --> 00:15:28,450 helps build SQL queries, to be sent to 370 00:15:28,450 --> 00:15:30,040 SQLite database 371 00:15:30,040 --> 00:15:32,949 subjects. So that's pretty useful. It's 372 00:15:32,949 --> 00:15:35,170 always easier to use a builtin object 373 00:15:35,170 --> 00:15:36,699 for building things like URIs or 374 00:15:36,699 --> 00:15:39,190 queries. The alternative is parsing and 375 00:15:39,190 --> 00:15:41,470 concatenating strings, which while not 376 00:15:41,470 --> 00:15:43,690 being terribly difficult, can quickly get 377 00:15:43,690 --> 00:15:46,329 messy. Alright to see how we use the 378 00:15:46,329 --> 00:15:48,040 SQLiteQueryBuilder, I'm going to 379 00:15:48,040 --> 00:15:49,480 paste in the rest of the code for this 380 00:15:49,480 --> 00:15:51,490 function, so that we can see what it does 381 00:15:51,490 --> 00:15:53,860 while I explain it. So let me just grab 382 00:15:53,860 --> 00:15:55,990 that code, and we'll actually get some 383 00:15:55,990 --> 00:15:57,910 errors here, and I'll deal with them once 384 00:15:57,910 --> 00:15:59,440 we've talked about what the code's doing. 385 00:15:59,440 --> 00:16:02,649 So I'm going to put this below the query 386 00:16:02,649 --> 00:16:05,470 Builder variable, the variable definition 387 00:16:05,470 --> 00:16:07,180 there. Now I'm also going to take the 388 00:16:07,180 --> 00:16:09,430 opportunity up here, to remove the 389 00:16:09,430 --> 00:16:13,990 redundant type declaration. I only added 390 00:16:13,990 --> 00:16:15,850 that so we had something to click on to 391 00:16:15,850 --> 00:16:18,040 see the documentation. So I mentioned 392 00:16:18,040 --> 00:16:19,569 we're going to get some errors. They're 393 00:16:19,569 --> 00:16:22,149 there mainly because, mostly because this 394 00:16:22,149 --> 00:16:23,470 includes the code for querying the 395 00:16:23,470 --> 00:16:25,750 timings and durations tables that we 396 00:16:25,750 --> 00:16:27,970 haven't created Contract classes for. And 397 00:16:27,970 --> 00:16:29,769 by the way, if you want to grab the code 398 00:16:29,769 --> 00:16:31,269 for this that I've just, the code for 399 00:16:31,269 --> 00:16:33,069 what I've just pasted in, it is in the 400 00:16:33,069 --> 00:16:34,870 resources section for this video if you 401 00:16:34,870 --> 00:16:36,209 want to go ahead and type that in. 402 00:16:36,209 --> 00:16:38,860 So again, I've added the code here for 403 00:16:38,860 --> 00:16:40,990 the timings and duration tables, because 404 00:16:40,990 --> 00:16:43,029 if there was only one table, it may not 405 00:16:43,029 --> 00:16:44,529 have been as obvious why we need to 406 00:16:44,529 --> 00:16:46,480 bother with the URI in the first 407 00:16:46,480 --> 00:16:48,550 place. I'm going to comment out the 408 00:16:48,550 --> 00:16:50,440 timings and durations code in a minute, 409 00:16:50,440 --> 00:16:52,510 but seeing all the code does make it 410 00:16:52,510 --> 00:16:54,550 easier, hopefully, to see why a Uri 411 00:16:54,550 --> 00:16:57,430 Matcher class is so useful. Alright so 412 00:16:57,430 --> 00:16:59,889 we're using a when here, a when statement to 413 00:16:59,889 --> 00:17:01,350 choose different blocks of code, 414 00:17:01,350 --> 00:17:04,000 depending on the result of matching the 415 00:17:04,000 --> 00:17:06,699 URI. If the URI just contained a 416 00:17:06,699 --> 00:17:08,770 table name, the basic query's pretty 417 00:17:08,770 --> 00:17:11,079 simple. It's just a SQL select with a 418 00:17:11,079 --> 00:17:13,150 TABLE_NAME. You can see that there - the 419 00:17:13,150 --> 00:17:16,329 code on line 74. And same for timings, 420 00:17:16,329 --> 00:17:21,549 line 82, and durations, line 90. So 421 00:17:21,549 --> 00:17:23,290 basically, when the URI matches TASKS, 422 00:17:23,290 --> 00:17:26,740 TIMINGS or TASKS_DURATIONS, we'll just 423 00:17:26,740 --> 00:17:29,320 call the setTables method to tell the 424 00:17:29,320 --> 00:17:31,240 queryBuilder which table we want to 425 00:17:31,240 --> 00:17:34,570 query. So the URI matches TASKS, TIMINGS 426 00:17:34,570 --> 00:17:36,700 or TASKS_DURATIONS. We're just 427 00:17:36,700 --> 00:17:39,370 using, we're just setting the tables 428 00:17:39,370 --> 00:17:42,520 property here, to tell the queryBuilder 429 00:17:42,520 --> 00:17:43,940 which table we want to use. 430 00:17:43,940 --> 00:17:45,620 And if we check the documentation there 431 00:17:45,620 --> 00:17:47,840 for that, you'll see that we can also 432 00:17:47,840 --> 00:17:50,870 specify more than one table and we can 433 00:17:50,870 --> 00:17:53,450 perform a join. So that's certainly one 434 00:17:53,450 --> 00:17:55,880 way of doing things, but another approach 435 00:17:55,880 --> 00:17:58,220 is to perform the join in the database 436 00:17:58,220 --> 00:18:01,010 using a view. So using a view made 437 00:18:01,010 --> 00:18:03,230 life easier in a SQL tutorial 438 00:18:03,230 --> 00:18:05,210 earlier in this course, and it also 439 00:18:05,210 --> 00:18:07,670 simplifies the code here in the provider. 440 00:18:07,670 --> 00:18:09,410 Now you can do it either way of course, 441 00:18:09,410 --> 00:18:11,570 but there's really no reason why the 442 00:18:11,570 --> 00:18:13,310 content provider should care about how 443 00:18:13,310 --> 00:18:16,240 the data's organized in the database. 444 00:18:16,240 --> 00:18:18,320 Implementation details like that really 445 00:18:18,320 --> 00:18:20,540 belong in the database, so the less our 446 00:18:20,540 --> 00:18:22,250 code relies on knowing about the 447 00:18:22,250 --> 00:18:24,500 implementation, the easier it is to 448 00:18:24,500 --> 00:18:27,260 change things in the future. This code 449 00:18:27,260 --> 00:18:29,240 though, as it stands, does have a 450 00:18:29,240 --> 00:18:31,700 potentially serious flaw though. Using 451 00:18:31,700 --> 00:18:34,010 queryBuilder.appendWhere, like 452 00:18:34,010 --> 00:18:35,980 we're using here, for example, on line 79, 453 00:18:35,980 --> 00:18:39,320 may introduce the potential for SQL 454 00:18:39,320 --> 00:18:41,330 injection attacks. In fact, if you've 455 00:18:41,330 --> 00:18:42,800 uploaded code like this to the Google 456 00:18:42,800 --> 00:18:44,960 Play Store, you'll get a warning and be 457 00:18:44,960 --> 00:18:47,180 told to fix it. So rather than using 458 00:18:47,180 --> 00:18:50,300 appendWhere, we'll use, the suggestion is to use 459 00:18:50,300 --> 00:18:52,130 appendWhereEscapeString, so I'm going 460 00:18:52,130 --> 00:18:55,790 to change that. And I'll take the 461 00:18:55,790 --> 00:19:03,260 opportunity to do that for all three. So 462 00:19:03,260 --> 00:19:05,630 let's call the, call up the documentation 463 00:19:05,630 --> 00:19:08,720 for that function. So the relevant part, and 464 00:19:08,720 --> 00:19:10,340 the difference from the appendWhere 465 00:19:10,340 --> 00:19:12,170 method that we're using, is this final 466 00:19:12,170 --> 00:19:14,210 sentence of the documentation, "it will be 467 00:19:14,210 --> 00:19:16,130 escaped to avoid SQL injection 468 00:19:16,130 --> 00:19:19,400 attacks". Now at the moment the lint 469 00:19:19,400 --> 00:19:20,810 checker doesn't pick up on this, and 470 00:19:20,810 --> 00:19:23,150 allows calls to appendWhere to get 471 00:19:23,150 --> 00:19:25,370 through with no warning. Until that 472 00:19:25,370 --> 00:19:27,710 changes be vigilant, and use the append 473 00:19:27,710 --> 00:19:30,050 WhereEscapeString function instead of 474 00:19:30,050 --> 00:19:32,510 appendWhere. Alright, so moving on 475 00:19:32,510 --> 00:19:36,230 now. If an ID is included in the URI, 476 00:19:36,230 --> 00:19:38,570 then the matcher will return TASK 477 00:19:38,570 --> 00:19:42,140 _ID, TIMINGS_ID or TASK 478 00:19:42,140 --> 00:19:45,140 _DURATIONS_ID. So in 479 00:19:45,140 --> 00:19:47,600 that case, we call the getId method of 480 00:19:47,600 --> 00:19:49,520 our Contracts class, and there's an example there 481 00:19:49,520 --> 00:19:52,400 on line 78, and that'll extract the ID 482 00:19:52,400 --> 00:19:55,700 for the URI, for us. Now this is another 483 00:19:55,700 --> 00:19:57,460 example of delegating responsibility 484 00:19:57,460 --> 00:20:00,520 where it belongs. Our Contract 485 00:20:00,520 --> 00:20:02,289 classes have intimate knowledge of the 486 00:20:02,289 --> 00:20:04,779 URIs, so it should be down to them to 487 00:20:04,779 --> 00:20:06,880 pass information out of the URIs. 488 00:20:06,880 --> 00:20:09,070 There's no need for the content provider 489 00:20:09,070 --> 00:20:11,140 to know that the ID appears in the 490 00:20:11,140 --> 00:20:13,210 second segment of the path - it just needs 491 00:20:13,210 --> 00:20:16,360 to get the ID from the URI. Now 492 00:20:16,360 --> 00:20:18,130 we'll add that function to the 493 00:20:18,130 --> 00:20:20,860 Contract class in the next video. Now if 494 00:20:20,860 --> 00:20:22,779 there's an ID included in the URI, we'll 495 00:20:22,779 --> 00:20:25,570 call the queryBuilders appendWhere 496 00:20:25,570 --> 00:20:28,240 EscapeString function, and this adds a 497 00:20:28,240 --> 00:20:30,700 where clause to the query, and here the 498 00:20:30,700 --> 00:20:32,440 where clause will just be _ID 499 00:20:32,440 --> 00:20:34,809 equals whatever the actual ID value was. 500 00:20:34,809 --> 00:20:36,789 Alright, so I'm going to take this opportunity 501 00:20:36,789 --> 00:20:39,010 just to comment out the code now, for the 502 00:20:39,010 --> 00:20:45,700 tables that we haven't created yet. And 503 00:20:45,700 --> 00:20:47,140 in the next video we'll start work on 504 00:20:47,140 --> 00:20:50,020 getting that getId function in a Task 505 00:20:50,020 --> 00:20:52,179 contract class written, and make a few 506 00:20:52,179 --> 00:20:54,070 other changes. So I'll see you in the 507 00:20:54,070 --> 00:20:56,490 next video.