As I wrote in the previous post, I made a plugin for the Muzei Live Wallpaper of Roman Nurik, containing the portraits of Hans Christian Andersen, made by illustrators who will held an exhibition at the Andersen Festival.
You can find the plugin in the Play Store; it is an open source project (licensed under Apache v2) and you can find the source code in my github repository.
Please note that I don’t own the portraits, so you won’t find the assets (the images) with those illustrations in the repository: it won’t compile at first but I wrote a readme in the
assets folder with a list of the missing files.
In this post I will explain some aspects about writing a plugin for Muzei.
Building an offline Muzei Plugin
Muzei has a very nice API that let you write a plugin for it.
Between the API docs, the 500px example and the API reference, you’ve got everything you need to know to write a plugin.
In particular, reading the documentation of the
MuzeiArtSource is very helpful.
At its core, a plugin is an extension of the class
MuzeiArtSource, that override the
Calling the API method
publishArtWork() in your
onUpdate(), will tell Muzei from where to download the image to render as a live wallpaper.
My plugin, however, is offline: all the images are in the app, in the .apk.
I chose this design because the illustrations doesn’t change over time: so I think that forcing you to download them over and over again, would be a waste of bandwidth, in the long run. At the same time they are not so many, so there are no risks to create a too big .apk.
onUpdate() of my
AndersenFestivalSource does these six things:
- Update the index of the next portrait to render.
- Clear its private subdir.
- Copy the chosen portrait into its private directory.
- Grant a read permission to the inflated file’s URI, to an external subscriber.
- Signal Muzei to publish the portrait.
- Schedule the next update.
AndersenFestivalSource clears the private directory on every update, only one file is extracted from the .apk each time: I chose to do this, because I am forced to extract a copy of the file (to let it read by the Muzei app), so I didn’t want the private directory to be too big.
Copy the file is a blocking operation but we can safely do it in
onUpdate() because the method is called inside
MuzeiArtSource, as we can see in the source code; according to the Android documentation, the latter method is executed inside a worker thread.
Granting the read permission is a very important step, as my plugin is a separate Android app from the Muzei app. So Muzei must read a file of another app.
In Android, to securely share a file from an app to another app, we have to use the
FileProvider mechanism (in fact Artwork.Builder supports the Content URI).
FileProvider is nicely explained in the reference docs and there is also a nice article in the training section.
However, when I call
Context.grantUriPermission(), I have to know the package name that will receive the permission. For that purpose I override
onSubscriberRemoved() to store and delete the names accordingly.
Muzei is a really nice app, check out the other plugins too!