We are back from a break. I hope you grew as a human being. I did, and I’m hoping to change things up a bit in this second half of the semester. The first half was largely me leading you on code adventures in lecture. I really don’t enjoy teaching in this tutorial style. You are not active participants, and we take on too much. Sometimes I wish I was a literature professor and knew how to carry on critical discussions.
Alas, this is a technical course. We must start with low-level mechanics. The one thing I can do to enrich the discussion is flip things around so you are more involved. So, for the second half of the semester, our lectures will have a different format. For the first fifteen minutes or so, you will team up in small groups. Each group will be given a problem to solve—most likely a method to implement. Then we will take combine and discuss your solutions to complete the overall task of the lecture.
Probably I will struggle to get the timing right. But let’s try.
The theme of term 1 was the Android software platform. The theme of term 2 is the Android physical platform. We start with the camera. Today we will continue our Backlog app that we started before term 1 ended. Recall that the purpose of the app is to prompt the user to take a photo memory each day of their life. When they open the app, they will see pictures for the current day from 2019, 2018, 2017, and so on.
We have two options for working with the camera. If our app needs to be directly involved in the picture-taking process, perhaps providing our own camera widgets atop the visual field, or visualizing a couch in an augmented reality interior decorating app, then we must create a custom view that displays the camera feed live. If we just need access to the taken photos, we can use an implicit intent to fire off an existing camera app. Today we just need access to the taken photos.
If we need to create our own camera activity, we have two APIs to choose from. An older, deprecated one that is well-documented. Or a newer poorly-documented one.
Before we dive into the exercises, here’s your TODO for next time:
Okay, let’s get going.
The user-interface and skeletal structure of the app has already been written. You will collaborate to complete this missing pieces to take pictures. Assume the following are already available in the activity:
yearand the selected, 1-based
day. All are
photoDirectorythat yields a
Filepointing to a public directory on external storage named
backlog. The directory might not exist yet. The directory, once it does exist, will have subdirectories for various years:
backlog/ 2019/ 04_30.jpg 04_29.jpg 04_28.jpg 2018/ 04_29.jpg 04_28.jpg
Photo(val file: File).
Also, you can and should make use of other teams’ methods.
I recommend writing the code outside of Android Studio.
loadDayPhotos, which sets the list’s adapter to hold the photos from all years that have a photo for the current month and day. If the current date is 29 April and the directory is as shown above, then after this method runs, the photo list will have a new adapter holding the photos
backlog/2018/04_29.jpg. Keep this short with higher-order functions and lambdas. No loops are necessary. (< 10 lines of Kotlin)
dayFile, which accepts a year, a month, and a day, each an
Int, and does two things: it returns a
Filepointing to the photo in the directory structure described above for the specified date, and it creates any missing parent directories that contain the file. (< 5 lines of Kotlin)
FileProviderfor the app, which we need because we want the camera activity to save our photo to our photo directory. Historically, we just sent a
file:///-prefixed URI as an extra to specify the write location. But recent versions of Android outlaw sending raw URIs because they are a security risk. Provide files in the
backlogdirectory in external storage. (< 10 lines of unwrapped XML)
dayUri, which accepts a year, a month, and a day, each an
Int, and does two things: it returns a
Uripointing to the photo in the directory structured described above for the specified date, and it creates any missing parent directories that contain the file. The URI that you create must be shareable with the camera activity.
Uri.fromFiledoes not create shareable URIs. Use a
FileProviderto generate the URI. (< 5 lines of Kotlin)
takePictureFromCamera, which fires off an intent for the capturing an image—but only if there is an activity that can handle it. Pass along the URI pointing to the current day’s photo location. Use
REQUEST_CAMERAfor the request code. (< 10 lines of Kotlin)
takePictureFromGallery, which fires off an intent for choosing an image from the gallery. Use
REQUEST_GALLERYfor the request code. (< 10 lines of Kotlin)
copyUriToUri, which accepts a source
Uriand a destination
Uri. It copies the content of the source
Urito the destination
ContentResolverto deal with the
Uris. Kotlinisms like
java.ioextension functions can make this short. (< 5 lines of Kotlin)
onActivityResult. If the camera activity succeeds, reload the current day’s photos. If the gallery activity succeeds, copy the
Uriit returns to the current day’s
Uriand reload the current day’s photos. Otherwise, defer to the superclass. (< 20 lines of Kotlin, many of which are curly braces)
Spend fifteen minutes on this. If you are able to share your code on Slack #general in a preformatted style, that’d be nice. Paper is okay too.