SENG 440: Lecture 15 – Camera
Dear students,
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:
- Read the Sensors Overview.
- On a quarter sheet of paper to be turned in at the beginning of the next class, write down three questions or observations inspired by the reading. Is there a sensor you wish we had available?
- Share a plan for your term 2 project on Slack, complete with sketches or wireframes of the user interface. Be sure to declare ownership of tasks. Don’t let collaborative development run afoul by not communicating.
Okay, let’s get going.
Activity
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:
- Properties for the current
year
and the selected, 1-basedmonth
andday
. All areInt
s. - A property
photoDirectory
that yields aFile
pointing to a public directory on external storage namedbacklog
. 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
- A model for a day’s photo with the constructor
Photo(val file: File)
. - An adapter to show a list of photos with the constructor
PhotoAdapter(photos: List<Photo>)
. - A
RecyclerView
namedphotoList
.
Also, you can and should make use of other teams’ methods.
Exercises
I recommend writing the code outside of Android Studio.
- Write method
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 photosbacklog/2019/04_29.jpg
andbacklog/2018/04_29.jpg
. Keep this short with higher-order functions and lambdas. No loops are necessary. (< 10 lines of Kotlin) - Write method
dayFile
, which accepts a year, a month, and a day, each anInt
, and does two things: it returns aFile
pointing 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) - Declare a
FileProvider
for the app, which we need because we want the camera activity to save our photo to our photo directory. Historically, we just sent afile:///
-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 thebacklog
directory in external storage. (< 10 lines of unwrapped XML) - Write method
dayUri
, which accepts a year, a month, and a day, each anInt
, and does two things: it returns aUri
pointing 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.fromFile
does not create shareable URIs. Use aFileProvider
to generate the URI. (< 5 lines of Kotlin) - Write method
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. UseREQUEST_CAMERA
for the request code. (< 10 lines of Kotlin) - Write method
takePictureFromGallery
, which fires off an intent for choosing an image from the gallery. UseREQUEST_GALLERY
for the request code. (< 10 lines of Kotlin) - Write method
copyUriToUri
, which accepts a sourceUri
and a destinationUri
. It copies the content of the sourceUri
to the destinationUri
. UseContentResolver
to deal with theUri
s. Kotlinisms likeuse
and itsjava.io
extension functions can make this short. (< 5 lines of Kotlin) - Override method
onActivityResult
. If the camera activity succeeds, reload the current day’s photos. If the gallery activity succeeds, copy theUri
it returns to the current day’sUri
and 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.