Dev Diary: Kindertron Flash Cards #2

Welcome back! It's been too long since the last Dev Diary, but I've been hard at work on large new feature! It's now wrapped up and shipped to the Apple App Store & Google Play Store last week, so it's live in the wild!

So what is this feature? Basically it lets users add new custom albums, using their own photos. This is great for adding family members, vacation memories, toys, etc. Whatever you want, really.

The Challenge

This proved a bit more complicated than expected, mainly because I didn't design for it upfront. I was hard-coding the built-in albums, without the expectation that users could add their own. I would need a user-modifiable way to store the list of albums/photos. Furthermore, I debated for a long time about whether users should be able to add their own photos to non-custom albums. Basically, could they extend the build-in albums (e.g. Animals) with other photos? For this, I concluded that while I wouldn't build this into the next version, I should design with it in mind. That way, I would avoid repeating this lengthy refactoring process if I ever wanted to add it.

App Preferences

The first order of business was to add some persistent user settings. This was pretty easy with the App Preferences cordova plugin. But I had to build out a whole new Settings page, and I spent some time adding toggle switches for previously hard-coded settings (like whether the user can click to advance the flashcards).

At the same time, I redesigned how the albums/photos are stored. I went with a hybrid model: there's a built-in Image Database which lists all the photos, captions, file names, etc. Then there is an Album List which is stored in the App Preferences, and references photos by key. This allows the user to customize the albums, add/remove/reorder photos, etc., without affecting the original data. And it lets me push out new photos or change existing photos without impacting their customization. I had to add a synchronization step, in case I want to push out an update with a new (or removed) photo.

Custom Albums

Finally, we get to the custom albums! I built out another page to list all the albums/photos, accessible from the Settings page. Then I had to figure out how to get photos from the user's phone.

The best option I could find was using the Camera plugin, configured to pull from the PHOTO_LIBRARY instead of the camera itself. Although this is where things got complicated.

My initial approach was to get a native URI and convert it to a file:/// URI, which I could directly add to an <img> tag to display. That was perfect, because I didn't have to store the images inside my app.

This approach needed some platform-specific code, since the native URIs are different and I couldn't find a consistent way to convert. But after a little time in the emulator, everything was working great.

That is, until I tried loading photos from Google Photos, which were not on my phone. That did not work, because I was using a device-local URI, and the photo was not on my device!

So, I started over again from scratch. The Camera plugin has another option, to get a copy of the selected image in a temporary photo. So my next approach was to get this image and copy it to the persistent app data folder. This also required platform-specific code: I used the File plugin on Android, which was pretty straightforward. But on iOS, it can't be used with the URIs returned from the Camera. So I used a combination of resolveLocalFilesystemUrl() and resolveDirectoryUrl() to get a FileEntry and DirectoryEntry, then used FileEntry.copyTo().

Once the photo is in the app data folder, everything else works basically the same. The upside is that it's a bit faster to display the photos, but the downside is that it needs to manually cleanup those file when removing photos/albums or resetting the entire Settings.

Finale

Eventually I got everything working the way I wanted on Android and iOS, so it was time to push out the release. I had done a ton of refactoring in the meantime, and added lots of new photos, so it was a long-overdue release!

Next up: adding sounds!