1 00:00:04,710 --> 00:00:06,870 Alright, now just as we have to define 2 00:00:06,870 --> 00:00:08,400 our activities in the Android manifest 3 00:00:08,400 --> 00:00:10,920 so that Android knows how to launch the 4 00:00:10,920 --> 00:00:12,750 activity, we also have to define our 5 00:00:12,750 --> 00:00:15,359 ContentProvider in that manifest as 6 00:00:15,359 --> 00:00:17,400 well. So we can do that by adding a 7 00:00:17,400 --> 00:00:19,820 provider tag, so let's open up our 8 00:00:19,820 --> 00:00:23,340 manifest file, AndroidManifest.xml. I'm 9 00:00:23,340 --> 00:00:25,080 going to come down here, after the 10 00:00:25,080 --> 00:00:27,420 closing of the activity - closing activity 11 00:00:27,420 --> 00:00:29,760 tag - before the closing application tag, 12 00:00:29,760 --> 00:00:31,710 and add our provider. So I'm going to type 13 00:00:31,710 --> 00:00:34,199 provider, noting that Android Studio has 14 00:00:34,199 --> 00:00:35,730 filled out some information for us. 15 00:00:35,730 --> 00:00:37,800 So the name we're going to go with is the 16 00:00:37,800 --> 00:00:39,600 name of our package first, so it's 17 00:00:39,600 --> 00:00:43,980 learnprogramming.academy dot a task 18 00:00:43,980 --> 00:00:46,620 timer dot, this is for the authorities, 19 00:00:46,620 --> 00:00:49,920 it's going to be dot provider. That's the 20 00:00:49,920 --> 00:00:51,480 name we've assigned for our ContentProvider. 21 00:00:51,480 --> 00:00:54,030 Then the actual name will be the full 22 00:00:54,030 --> 00:00:55,950 package and class name of our App 23 00:00:55,950 --> 00:00:57,120 Provider. So it's going to be learn 24 00:00:57,120 --> 00:01:00,540 programming.academy.task 25 00:01:00,540 --> 00:01:02,070 timer.AppProvider. You can see 26 00:01:02,070 --> 00:01:03,390 that Android Studio's helpfully filled 27 00:01:03,390 --> 00:01:05,579 that in for us. Then we also want to add an 28 00:01:05,579 --> 00:01:09,690 android colon exported equals double 29 00:01:09,690 --> 00:01:12,750 quotes false. then we'll close off the 30 00:01:12,750 --> 00:01:15,450 provider tag. Alright, so as you saw the 31 00:01:15,450 --> 00:01:18,090 name attribute is the full name of our 32 00:01:18,090 --> 00:01:20,250 ContentProvider class. So we're using 33 00:01:20,250 --> 00:01:21,659 the package name followed by the name of 34 00:01:21,659 --> 00:01:22,320 the class. 35 00:01:22,320 --> 00:01:23,610 Now obviously you'll replace that 36 00:01:23,610 --> 00:01:25,110 package name with whatever is 37 00:01:25,110 --> 00:01:27,330 appropriate to your application, if you 38 00:01:27,330 --> 00:01:28,799 haven't followed the same conventions I 39 00:01:28,799 --> 00:01:30,720 have. And obviously you can see the package 40 00:01:30,720 --> 00:01:32,430 at the top on line 3 if you need to 41 00:01:32,430 --> 00:01:33,960 refer to what it is - how it's been set up 42 00:01:33,960 --> 00:01:36,240 for you. Now the authorities attribute, 43 00:01:36,240 --> 00:01:38,369 ideally, we'd want to be able to use the 44 00:01:38,369 --> 00:01:40,470 constant CONTENT_AUTHORITY 45 00:01:40,470 --> 00:01:41,790 from the AppProvider class for this 46 00:01:41,790 --> 00:01:43,979 attribute. But I haven't, as of the time 47 00:01:43,979 --> 00:01:45,479 of recording this video, found a way to 48 00:01:45,479 --> 00:01:47,400 use that constant. Now the final 49 00:01:47,400 --> 00:01:49,860 attribute is to, or is used rather, to 50 00:01:49,860 --> 00:01:52,619 specify whether the ContentProvider can 51 00:01:52,619 --> 00:01:54,360 be accessed by other applications - that's 52 00:01:54,360 --> 00:01:56,700 the exported equals. Now we can change 53 00:01:56,700 --> 00:01:58,829 that later, but for now I'm keeping it 54 00:01:58,829 --> 00:02:00,930 private by setting that to false. The 55 00:02:00,930 --> 00:02:02,009 main reason for doing that is we don't 56 00:02:02,009 --> 00:02:03,659 have to worry about permissions just yet. 57 00:02:03,659 --> 00:02:05,700 Now there is a description that you can 58 00:02:05,700 --> 00:02:08,878 refer to for more information, and I'll 59 00:02:08,878 --> 00:02:13,799 just quickly open up that link. They're 60 00:02:13,799 --> 00:02:15,389 definitely worth reading through this 61 00:02:15,389 --> 00:02:17,579 page, so that you're familiar with all the 62 00:02:17,579 --> 00:02:18,370 other options 63 00:02:18,370 --> 00:02:20,739 you can use when you come to create your 64 00:02:20,739 --> 00:02:23,920 own providers. Alright, that's the only 65 00:02:23,920 --> 00:02:25,720 change we needed to make to the manifest. 66 00:02:25,720 --> 00:02:31,209 So going back to our MainActivity, and 67 00:02:31,209 --> 00:02:34,540 specifically the onCreate function, I'm 68 00:02:34,540 --> 00:02:36,370 going to remove the test code that uses 69 00:02:36,370 --> 00:02:38,950 the AppDatabase, then replace it with 70 00:02:38,950 --> 00:02:41,470 code to query the database using the 71 00:02:41,470 --> 00:02:43,420 ContentProvider. Now the code will be 72 00:02:43,420 --> 00:02:45,250 very similar to what we used in our 73 00:02:45,250 --> 00:02:48,220 ContentResolver example previously. And 74 00:02:48,220 --> 00:02:50,440 keep in mind we don't need to get our 75 00:02:50,440 --> 00:02:52,420 own instance to that database, and we 76 00:02:52,420 --> 00:02:53,950 call the ContentResolvers query 77 00:02:53,950 --> 00:02:55,840 function instead of the database 78 00:02:55,840 --> 00:02:57,400 function. Let's go ahead and make those 79 00:02:57,400 --> 00:02:59,620 changes. So first thing, I'm going to delete 80 00:02:59,620 --> 00:03:02,859 these two entries here now, and instead 81 00:03:02,859 --> 00:03:04,450 what we're going to do, instead of a db 82 00:03:04,450 --> 00:03:06,819 dot rawQuery I'm going to change that to 83 00:03:06,819 --> 00:03:11,440 contentResolver dot. And it won't be raw 84 00:03:11,440 --> 00:03:13,569 query, it'll be query - that's the name 85 00:03:13,569 --> 00:03:17,230 of the function we created. Now the 86 00:03:17,230 --> 00:03:19,900 contentResolver needs a URI as its 87 00:03:19,900 --> 00:03:22,660 first argument to its query function, and 88 00:03:22,660 --> 00:03:23,889 we're going to get all the records, which 89 00:03:23,889 --> 00:03:25,690 means we can pass null for the remaining 90 00:03:25,690 --> 00:03:27,579 four arguments. Let's go ahead and make 91 00:03:27,579 --> 00:03:30,549 those changes. So I'm going to delete the 92 00:03:30,549 --> 00:03:32,980 rest of that line, and we'll start with 93 00:03:32,980 --> 00:03:37,209 passing it TasksContract.CONTENT 94 00:03:37,209 --> 00:03:39,579 _URI comma. Then I'm just 95 00:03:39,579 --> 00:03:41,169 going to add the four arguments null; 96 00:03:41,169 --> 00:03:45,489 projection, selection, selectionArgs and 97 00:03:45,489 --> 00:03:48,730 sortOrder, all as null, because again 98 00:03:48,730 --> 00:03:50,410 we're actually going to get all the 99 00:03:50,410 --> 00:03:52,060 records at this point in time. So that 100 00:03:52,060 --> 00:03:53,739 should be enough now for us to run the 101 00:03:53,739 --> 00:03:56,139 app, and confirm that it'll actually work 102 00:03:56,139 --> 00:03:57,340 and we should be able to retrieve the 103 00:03:57,340 --> 00:04:02,620 data. So let me just start the emulator. Okay 104 00:04:02,620 --> 00:04:08,250 we'll give it a run, then I'll open the logcat, 105 00:04:08,250 --> 00:04:10,299 and I'm just going to type forward slash 106 00:04:10,299 --> 00:04:13,720 main so we'll only get the information 107 00:04:13,720 --> 00:04:15,760 relating to our app - MainActivity in this 108 00:04:15,760 --> 00:04:17,410 case. And you can see there what's 109 00:04:17,410 --> 00:04:19,839 happened. We can see the three rows that 110 00:04:19,839 --> 00:04:21,668 we added from the terminal in a previous 111 00:04:21,668 --> 00:04:23,470 video, when we were checking that the 112 00:04:23,470 --> 00:04:26,020 database was created properly. Now if you 113 00:04:26,020 --> 00:04:28,120 were testing on a physical device rather 114 00:04:28,120 --> 00:04:29,919 than an emulator, you won't have been 115 00:04:29,919 --> 00:04:31,110 able to run the SQL 116 00:04:31,110 --> 00:04:33,570 insert commands in the earlier video. So in 117 00:04:33,570 --> 00:04:34,860 that case you'll see the number of rows 118 00:04:34,860 --> 00:04:36,990 showing zero. That's fine though because 119 00:04:36,990 --> 00:04:38,550 it's correctly reporting that there's no 120 00:04:38,550 --> 00:04:40,560 rows in your table, but the query is 121 00:04:40,560 --> 00:04:42,660 still working fine. And if you do get any 122 00:04:42,660 --> 00:04:44,160 other errors then you've got a problem, 123 00:04:44,160 --> 00:04:47,190 but zero rows is perfectly okay if you 124 00:04:47,190 --> 00:04:48,330 weren't able to add any for whatever 125 00:04:48,330 --> 00:04:50,370 reason. We'll be actually writing the code 126 00:04:50,370 --> 00:04:53,520 to insert records soon, and then you'll 127 00:04:53,520 --> 00:04:54,750 be able to see everything working 128 00:04:54,750 --> 00:04:56,940 fine on a physical device. But that's 129 00:04:56,940 --> 00:04:58,770 good. At this point in time the Content 130 00:04:58,770 --> 00:05:00,600 Provider appears to be working. 131 00:05:00,600 --> 00:05:02,670 Now this isn't how we'll be using it to 132 00:05:02,670 --> 00:05:05,310 execute queries, by the way. Our query was 133 00:05:05,310 --> 00:05:07,500 executed on the UI thread which isn't 134 00:05:07,500 --> 00:05:09,390 good, but at least we've tested the 135 00:05:09,390 --> 00:05:11,580 Content Provider, and we know now that it 136 00:05:11,580 --> 00:05:13,340 can successfully query the database. 137 00:05:13,340 --> 00:05:15,450 There's a couple more things we can try 138 00:05:15,450 --> 00:05:17,340 before we move on to adding the 139 00:05:17,340 --> 00:05:19,770 insert, update and delete functions. At 140 00:05:19,770 --> 00:05:22,350 the moment we're returning all records 141 00:05:22,350 --> 00:05:24,660 from the column. Now we can change that 142 00:05:24,660 --> 00:05:27,270 by specifying a value for the projection 143 00:05:27,270 --> 00:05:29,250 argument. And we can also change the 144 00:05:29,250 --> 00:05:31,830 ordering to sort on the sortOrder 145 00:05:31,830 --> 00:05:34,170 column, and what I'll also do while we're 146 00:05:34,170 --> 00:05:35,760 making these changes is we'll delete the 147 00:05:35,760 --> 00:05:37,830 floating action button code, because you 148 00:05:37,830 --> 00:05:39,330 won't be using that in this app. So I'll 149 00:05:39,330 --> 00:05:41,970 do that first - get rid of this code down 150 00:05:41,970 --> 00:05:44,730 here, this fab code for the floating 151 00:05:44,730 --> 00:05:46,650 action button, and let's go ahead now and 152 00:05:46,650 --> 00:05:49,350 change those arguments. So what I'm going 153 00:05:49,350 --> 00:05:50,910 to do above the definition for the 154 00:05:50,910 --> 00:05:52,950 cursor, I'm going to create a projection 155 00:05:52,950 --> 00:05:58,320 and a sort column: val projection equals, 156 00:05:58,320 --> 00:06:01,910 it's going to be arrayOf parentheses 157 00:06:01,910 --> 00:06:07,140 TasksContract.Columns.TASK_NAME. 158 00:06:07,140 --> 00:06:08,730 that's our first field we want returned, 159 00:06:08,730 --> 00:06:10,920 comma. Then our second one we want is 160 00:06:10,920 --> 00:06:13,620 the sortOrder, so TasksContract dot 161 00:06:13,620 --> 00:06:17,700 Columns.TASK_SORT 162 00:06:17,700 --> 00:06:20,580 _ORDER. That's our projection, 163 00:06:20,580 --> 00:06:21,720 and then for our sortColumn, 164 00:06:21,720 --> 00:06:26,070 val sortColumn is equal to - we're just 165 00:06:26,070 --> 00:06:28,860 going to specify the actual 166 00:06:28,860 --> 00:06:32,040 column - TasksContract.Columns, because 167 00:06:32,040 --> 00:06:34,620 it's a single one, dot TASK_ 168 00:06:34,620 --> 00:06:36,600 SORT_ORDER. Then we'll make a 169 00:06:36,600 --> 00:06:38,640 change here to the code - instead of 170 00:06:38,640 --> 00:06:40,470 passing null for the projection, we'll 171 00:06:40,470 --> 00:06:41,790 actually pass the projection we've 172 00:06:41,790 --> 00:06:45,000 just defined on line 19. And 173 00:06:45,000 --> 00:06:48,420 then for the sortOrder, oops projection, and for 174 00:06:48,420 --> 00:06:50,430 sortOrder we're going to use the 175 00:06:50,430 --> 00:06:53,640 definition on line 20 for that. And what 176 00:06:53,640 --> 00:06:55,230 we'll also do is make some changes here 177 00:06:55,230 --> 00:06:56,930 to the output, because we're no longer 178 00:06:56,930 --> 00:07:00,030 getting the ID. We haven't specified that 179 00:07:00,030 --> 00:07:01,710 in our projection so I'm going to 180 00:07:01,710 --> 00:07:03,660 comment that out. So name in fact will be 181 00:07:03,660 --> 00:07:05,970 the first result returned, and we'll set 182 00:07:05,970 --> 00:07:07,580 the column index to 0 for that. 183 00:07:07,580 --> 00:07:09,900 Description, we're also not getting any 184 00:07:09,900 --> 00:07:12,090 more - I'm going to comment that out - and 185 00:07:12,090 --> 00:07:14,550 as a result sortOrder is now the second 186 00:07:14,550 --> 00:07:17,669 column index, so that'll be number 1. Let's 187 00:07:17,669 --> 00:07:19,200 also take the opportunity to clean up 188 00:07:19,200 --> 00:07:21,630 the result. We're going to delete the ID 189 00:07:21,630 --> 00:07:23,700 component first because that's no longer 190 00:07:23,700 --> 00:07:25,680 being included, and likewise for 191 00:07:25,680 --> 00:07:27,740 description, let's delete that as well, 192 00:07:27,740 --> 00:07:31,169 leaving only name and sortOrder. So we'll 193 00:07:31,169 --> 00:07:36,960 run this app again now. Let's see what we 194 00:07:36,960 --> 00:07:40,710 get returned. This time you can see down 195 00:07:40,710 --> 00:07:42,750 the bottom, it's correctly showing two 196 00:07:42,750 --> 00:07:45,300 entries there; tasktimer - you can see 197 00:07:45,300 --> 00:07:46,890 there, even though the log has made that 198 00:07:46,890 --> 00:07:49,200 a little bit interesting to read, you can 199 00:07:49,200 --> 00:07:50,520 see that we've got our three entries still 200 00:07:50,520 --> 00:07:52,440 but now it's showing only the name and 201 00:07:52,440 --> 00:07:54,720 the sort order, noting also that it's 202 00:07:54,720 --> 00:07:56,370 actually sorted in the correct order. Null 203 00:07:56,370 --> 00:07:58,740 is the first sortOrder 0, and then 204 00:07:58,740 --> 00:08:02,669 2. And again, confirming that Kotlin is 205 00:08:02,669 --> 00:08:05,460 appearing before Android in the results 206 00:08:05,460 --> 00:08:08,310 that were returned. And if we go back to our 207 00:08:08,310 --> 00:08:10,410 query method - let's go back to that 208 00:08:10,410 --> 00:08:13,770 in our AppProvider - the other thing we 209 00:08:13,770 --> 00:08:16,460 could do which we haven't really done, 210 00:08:16,460 --> 00:08:18,690 just go up to the top and check that out. 211 00:08:18,690 --> 00:08:20,520 Actually we have got that outputted there - 212 00:08:20,520 --> 00:08:22,979 we've got query match is as well as query 213 00:08:22,979 --> 00:08:24,510 called with. So let's just, I didn't 214 00:08:24,510 --> 00:08:25,710 actually see that in the output, let's go 215 00:08:25,710 --> 00:08:27,990 back and check that out again. And I think 216 00:08:27,990 --> 00:08:28,979 that's not appearing because I've got 217 00:08:28,979 --> 00:08:31,219 the slash main there so let's delete that, 218 00:08:31,219 --> 00:08:34,320 and we can see it now a bit more clearly. That's 219 00:08:34,320 --> 00:08:35,370 what I was looking for then, because I 220 00:08:35,370 --> 00:08:38,460 had slash main I couldn't see it. That query 221 00:08:38,460 --> 00:08:40,469 was called initially, as you can see 222 00:08:40,469 --> 00:08:42,150 there, with content colon forward slash 223 00:08:42,150 --> 00:08:43,469 forward slash learnprogramming dot 224 00:08:43,469 --> 00:08:45,900 academy.tasktimer.provider slash 225 00:08:45,900 --> 00:08:48,480 tasks, and it's showing it there that the 226 00:08:48,480 --> 00:08:50,790 query match was 100, which is correct in 227 00:08:50,790 --> 00:08:53,010 this case. And then obviously we're using 228 00:08:53,010 --> 00:08:56,550 a code there, queryBuilder, to create a 229 00:08:56,550 --> 00:08:58,970 sqlite queryBuilder instance. 230 00:08:58,970 --> 00:09:01,170 Alright so that's working fine. So 231 00:09:01,170 --> 00:09:02,820 going back to our code now in Main 232 00:09:02,820 --> 00:09:04,500 Activity again. At the moment we're 233 00:09:04,500 --> 00:09:06,810 returning all the records, but the 234 00:09:06,810 --> 00:09:08,459 ContentResolver can also be used to 235 00:09:08,459 --> 00:09:10,589 return specific rows from the database, 236 00:09:10,589 --> 00:09:13,589 by passing a slightly different URI. And 237 00:09:13,589 --> 00:09:15,839 again, the one we can see at the moment is 238 00:09:15,839 --> 00:09:18,930 clearly being sent there, slash tasks, 239 00:09:18,930 --> 00:09:21,450 which is all entries, or all rows I 240 00:09:21,450 --> 00:09:23,160 should say, in the database. But by 241 00:09:23,160 --> 00:09:25,380 changing that slightly, changing our 242 00:09:25,380 --> 00:09:27,480 contentResolver.query line, we can 243 00:09:27,480 --> 00:09:29,970 get a specific record. So let's go ahead 244 00:09:29,970 --> 00:09:31,830 and do that. So if I change this first 245 00:09:31,830 --> 00:09:34,770 line here - TasksContract.CONTENT 246 00:09:34,770 --> 00:09:36,779 _URI - I'm going to change 247 00:09:36,779 --> 00:09:40,649 that to TasksContract. This time 248 00:09:40,649 --> 00:09:43,560 it's going to be dot buildUriFrom 249 00:09:43,560 --> 00:09:47,220 Id and then parentheses, 2. We'll pass 250 00:09:47,220 --> 00:09:48,540 out comma so the rest of the parameters 251 00:09:48,540 --> 00:09:50,100 are accurate. So that's now going to 252 00:09:50,100 --> 00:09:52,230 return just a single entry. Now you 253 00:09:52,230 --> 00:09:54,209 wouldn't normally hard code the ID of a 254 00:09:54,209 --> 00:09:55,950 row in your code, but we're only 255 00:09:55,950 --> 00:09:58,380 testing that the provider works here. So 256 00:09:58,380 --> 00:09:59,910 I've changed that first parameter, as you 257 00:09:59,910 --> 00:10:01,560 saw there, and what I probably should have 258 00:10:01,560 --> 00:10:03,089 done is commented out the original line 259 00:10:03,089 --> 00:10:04,500 because we'll need to go back to that later, 260 00:10:04,500 --> 00:10:07,200 but we'll do that at another time. We'll 261 00:10:07,200 --> 00:10:09,720 just have to retype it in again. And as I 262 00:10:09,720 --> 00:10:11,130 said you wouldn't normally hard code the 263 00:10:11,130 --> 00:10:13,529 ID like this. We're just testing, though, 264 00:10:13,529 --> 00:10:15,600 that we can select a single row. 265 00:10:15,600 --> 00:10:17,910 So what we're going to do now is just 266 00:10:17,910 --> 00:10:19,910 stop it, then we'll actually run it again, 267 00:10:19,910 --> 00:10:23,610 and let's see what happens. So the log 268 00:10:23,610 --> 00:10:25,110 cat should show the URI with the 269 00:10:25,110 --> 00:10:27,420 ID appended to it now. And you can see here 270 00:10:27,420 --> 00:10:30,270 that it is correctly showing that. You 271 00:10:30,270 --> 00:10:31,500 can see it's learnprogramming dot 272 00:10:31,500 --> 00:10:33,300 academy.tasktimer.provider 273 00:10:33,300 --> 00:10:34,980 slash tasks. This time it's got the ID 274 00:10:34,980 --> 00:10:38,130 that we specified, slash 2 on the end of it. 275 00:10:38,130 --> 00:10:39,810 But that's interesting though, because if 276 00:10:39,810 --> 00:10:41,160 we have a look at the actual output here, 277 00:10:41,160 --> 00:10:45,120 we've got no records showing, and also 278 00:10:45,120 --> 00:10:47,339 looking up on the line above that - query 279 00:10:47,339 --> 00:10:50,820 rows in returned cursor equals 0. So 280 00:10:50,820 --> 00:10:52,410 there's no record between the two rows 281 00:10:52,410 --> 00:10:54,000 of asterisks, confirming that we 282 00:10:54,000 --> 00:10:57,060 haven't actually got any data back. So 283 00:10:57,060 --> 00:10:58,950 why is this happening? Well again, going 284 00:10:58,950 --> 00:11:02,220 back to our AppProvider code, we're using 285 00:11:02,220 --> 00:11:04,560 the appendWhereEscapeString function. 286 00:11:04,560 --> 00:11:06,329 What I'm going to do is just change that 287 00:11:06,329 --> 00:11:09,329 back. I'm going to delete the EscapeString, so 288 00:11:09,329 --> 00:11:10,529 that we're going back and using just the 289 00:11:10,529 --> 00:11:12,180 appendWhere function. 290 00:11:12,180 --> 00:11:14,010 And let's stop and run it again and see what 291 00:11:14,010 --> 00:11:21,240 happens, and you can see in that case, in 292 00:11:21,240 --> 00:11:23,370 fact it has worked - same content 293 00:11:23,370 --> 00:11:25,890 provider output as you can see there. We've 294 00:11:25,890 --> 00:11:27,930 got our slash 2 again. This time we've 295 00:11:27,930 --> 00:11:30,000 got records, or rows rather, in returned 296 00:11:30,000 --> 00:11:32,190 cursor equals 1, and in between the 297 00:11:32,190 --> 00:11:33,390 asterisks there we've got this 298 00:11:33,390 --> 00:11:36,930 reading data Name Android N sort order 299 00:11:36,930 --> 00:11:39,260 2, which is clearly the entry for the 300 00:11:39,260 --> 00:11:43,350 row with ID 2. So are you interested at 301 00:11:43,350 --> 00:11:44,760 this point in knowing why this is not 302 00:11:44,760 --> 00:11:46,650 working? Well let's move on to the next 303 00:11:46,650 --> 00:11:48,330 video where I'm going to show you what's 304 00:11:48,330 --> 00:11:50,940 gone wrong, but more importantly, how to 305 00:11:50,940 --> 00:11:55,040 fix it? So I'll see you in the next video.