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.

