Setting up a minimal, command-line Android emulator on Linux

Android has all kinds of nice development tools, but sometimes you just want to run an apk and don’t need all the surrounding tooling. In my case, I have already have my Chromium setup, which can produce binaries for several platforms including Android.

I usually test on a physical device, a smartphone, but I would like to try a device with a tablet form factor and I don’t have one at hand.

Chromium smartphone and tablet screenshots

Chromium provides different user experiences for smartphones and tablets.

I’ve set up the most stripped-down environment possible to run an Android emulator using the same tools provided by the platform. Notice these are generic instructions, not tied to Chromium tooling, despite I’m doing this mainly to run Chromium.

The first step is to download the latest version of the command line tools, instead of Android Studio, from the Android developer website. We will extract the tools to the location ~/Android/Sdk, in the path where they like to find themselves:

mkdir -p ~/Android/Sdk/cmdline-tools
unzip -d ~/Android/Sdk/cmdline-tools ~/Downloads/commandlinetools-linux-10406996_latest.zip 
mv ~/Android/Sdk/cmdline-tools/cmdline-tools/ ~/Android/Sdk/cmdline-tools/latest

Now we have the tools installed in ~/Android/Sdk/cmdline-tools/latest, we need to add their binaries to the path. We will also add other paths for tools we are about to install, with the command:

export PATH=~/Android/Sdk/cmdline-tools/latest/bin:~/Android/Sdk/emulator:~/Android/Sdk/platform-tools:$PATH

Run the command above for a one-time use, or add it to ~/.bashrc or your preferred shell configuration for future use.

You will also need to setup another envvar:

export ANDROID_HOME=~/Android/Sdk

Again, run it once for a one-time use or add it to your shell configuration in ~/.bashrc or equivalent.

Now we use the tool sdkmanager, which came with Android’s command-line tools, to install the rest of the software we need. All of this will be installed inside your $ANDROID_HOME, so in ~/Android/Sdk.

sdkmanager "emulator" "platform-tools" "platforms;android-29" "system-images;android-29;google_apis;x86_64"

In the command above I have installed the emulator, platform tools (adb and friends), and system libraries and a system image for Android 10 (API level 29). You may check what other things are available with sdkmanager --list, you will find multiple variants of the Android platform and system images.

Finally, we have to setup a virtual machine, called AVD (“Android Virtual Device”) in Android jargon. We do that with:

avdmanager -v create avd -n Android_API_29_Google -k "system-images;android-29;google_apis;x86_64" -d pixel_c

Here I have created a virtual device called “Android_API_29_Google” with the system image I had downloaded, and the form factor and screen size of a Pixel C, which is a tablet. You may get a list of all the devices that can be simulated with avdmanager list device.

Now we can already start an emulator running the AVD with the name we have just chosen, with:

emulator -avd Android_API_29_Google

The files and configuration for this AVD are stored in ~/.android/avd/Android_API_29_Google.avd, or a sibling directory if you used a different name. Configuration is in the config.ini file, you can set here many options you would normally configure from Android Studio, and even more. I recommend to change at least this value, for your convenience:

hw.keyboard = yes

Otherwise you will find yourself typing URLs with a mouse on a virtual keyboard… Not fun.

Finally, your software will be the up-to-date after a fresh install but, when new versions are released, you will be able to install them with:

sdkmanager --update

Make sure to run this every now and then!

At this point, your setup should be ready, and all the tools you need are in the PATH. Feel free to reuse the commands above to create any number of virtual devices with different Android system images, different form factors… Happy hacking!

An update on LibreOffice for Android

Summer time is for hacking! I booked some time in the last weeks to carry on with the work on the Android port we started earlier this year at Igalia, merging the pending bits in master, completing features and adding some polish. Now that everything is upstream, let me share it with you!

Merge ownCloud document provider

I had used the ownCloud Android library to speed up the implementation of the ownCloud document provider. Unfortunately, it included some libraries as dependencies in binary form and that was not acceptable to integrate the library in LibreOffice.

My solution was creating a Github fork of the library and replace the binary dependencies with the corresponding sources. Now a tarball containing the sources in that repository is downloaded and compiled by LibreOffice build system when asked for the Android target.

Implement save to cloud

While I was working on the Document Providers infrastructure, Miklos and Tomaž were implementing document edition on the LibreOffice port. Now there is some basic support to edit and save documents, I must enable the save operation in the Document Providers framework and in particular in the ownCloud test implementation.

The ownCloud save operation itself was easy, but knowing whether the local save operation had finished or not proved to be a bit more challenging. LibreOfficeKit API does not yet provide a way to set a callback on the save operation so we can check its status and do something after it has finished. As a workaround, I periodically check the modification date field in the file to know if LibreOffice core is already done with it.

This is meant to be a temporary solution, but at least, we can provide some feedback on the save operation now.

Feedback on save

A hamburger in my LibreOffice

Without any indication, the drawer containing the list of document providers was completely hidden from users; only a handful of people I showed the app to, besides myself, were aware of it. For that reason, I enabled the application home button, the one on the left of the action bar, to become a drawer indicator, the famous “hamburger” icon found in many Android apps.

It took me some time to figure out the way to put everything together so the button changes its icon and behavior according to the context. When the user is on the root folder, the “hamburger” will indicate the presence of a drawer menu with the additional locations implemented by the available document providers. When browsing some directory, it will become a back arrow to browse the parent directory. Finally, when the drawer is open it will also become an arrow to close the drawer.

Drawer indicator

Smart behavior for system back key

Tapping the system back key when browsing some directory and see the app close was a bit frustrating, because most users expect it to go back to the previous location, the parent directory. I’ve overridden the default behavior to make it smarter and context-aware. When browsing some directory, it will open the parent directory; when the drawer is open, it will close it; when browsing the root directory, it will ask for an additional tap for confirmation to exit the application.

Exit confirmation

All these patches are in master branch and will be part of the next major release. Impatient people will also find them in the daily builds from tomorrow onward.

Creating new document providers in LibreOffice for Android

We recently completed our tasks for The Document Foundation regarding the Android document browser; nonetheless, we had a pending topic regarding the documentation of our work: write and publish a guide to extend the cloud storage integration. This blog post covers how to integrate new cloud solutions using the framework for cloud storage we have implemented.

Writing a document provider

Document Provider class diagram

We have called “document providers” to the set of classes that implement support for some storage solution. Document providers will consist of two classes implementing the IDocumentProvider and IFile interfaces. Both contain extensive in-code documentation of the operations to help anybody implementing them.

The IDocumentProvider interface provides some general operations about the provider, addressed to provide a starting point for the service. getRootDirectory() provides a pointer to the root of the service, while createFromUri() is required to restore the state of the document browser.

The IFile interface is an abstraction of the java File class, with many similar operations. Those operations will be used by the document browser to print information about the files, browse the directories and open the final documents.

Once those classes have been implemented, the new provider must be linked with the rest of the application by making some modifications to DocumentProviderFactory class. Touching the initialize() method to add a new instance of the provider to the providers[] array should be enough:

    // initialize document providers list
    instance.providers = new IDocumentProvider[3];
    instance.providers[0] = new LocalDocumentsDirectoryProvider();
    instance.providers[1] = new LocalDocumentsProvider();
    instance.providers[2] = new OwnCloudProvider(context);

At this point, your provider should appear in the drawer that pops-up with a swipe gesture from the left of the screen.

LibreOffice for Android, provider selection

You are encouraged to create the classes for your document provider in a separate package inside org.libreoffice.storage. Your operations may throw a RuntimeException in case of error, it will be captured by the UI activity and the message inside the exception will be shown, so make sure that you are internationalizing the strings using the standard Android API. You can always take a look to the existing providers and use them as an example, specially OwnCloudProvider which is the most complex one but still quite manageable.

Making use of application settings

If you are implementing a generic provider for some cloud service, it is quite likely that you need some input from the user like a login name or a password. For that reason we have added an activity for configuration related with document providers.

To add your settings in that screen, modify the file res/xml/documentprovider_preferences.xml and add a new PreferenceCategory that contain your own ones. The android:key attribute will allow you to use the preference from your code; you may want to add that preference string as a constant in DocumentProviderSettingsActivity.

At this point, you will be able to use the preferences in your DocumentProvider using the standard Android API. Take OwnCloudProvider as an example:

    public OwnCloudProvider(Context context) {
        ...
        // read preferences
        SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
        serverUrl = preferences.getString(
                DocumentProviderSettingsActivity.KEY_PREF_OWNCLOUD_SERVER, "");

Finally, we have added a way for providers to check if settings have changed; otherwise, the application should be restarted for the changes to take effect. Your provider must implement the OnSharedPreferenceChangeListener, which brings the onSharedPreferenceChanged() method. That method will be called whenever any preference is changed, you can just check the ones you are interested in using the key parameter, and make the changes required to the internal state of your provider.

Preference listener class diagram

Future

This effort has been one more step towards building a full featured version of LibreOffice for Android, and there are improvements we can, or even must do in the future:

  • With the progression of the work in the LibreOffice Android editor we will have to add a save operation to the document providers that takes care of uploading the new version of the file to the cloud.
  • It would be a good idea to implement an add-on mechanism to install the document providers. That way we would not add unnecessary weight to the main package, and plugins could be independently distributed.

That’s all for now; you can try the ownCloud provider building the feature/owncloud-provider-for-android branch yourself, or wait for it to be merged. We hope to see other members of the community taking advantage of this framework to provide new services soon.

New features in LibreOffice for Android document browser

The Document Foundation recently assigned one of the packages of the Android tender to Igalia; in particular, the one about cloud storage and email sharing. Our proposal comprised the following tasks:

  • Integrate the “share” feature of the Android framework to be able to send documents by email, bluetooth or any other means provided by the system.
  • Provide the means for the community to develop integration of cloud storage solutions.
  • Implement ownCloud integration as an example of how to integrate other cloud solutions.
  • Extensive documentation of the process to integrate more cloud solutions.

The work is completed and the patches available in the repository; most of them are already merged in master, while ownCloud support lives in a different branch for now.

Sharing documents

The Android-provided share feature allows to send a document not only through email but through bluetooth or any available methods, depending on the software installed in your device.

We have made this feature available to users through a context menu in the document browser, which pops up after a long press on a document.

Context menu in Android document browser

Share from the Android document browser

Support for cloud storage solutions

This task consisted of creating an interface to develop integration of any cloud storage solution. The first step was abstracting the code that made direct access to the file system, so it could be replaced by the different implementations of storage services, which from now on will be denominated document providers.

Afterwards, we created two document providers for local storage: one to access the internal storage of the device and another one to conveniently access the Documents directory inside the storage. These two simple providers served as a test for the UI to switch between both; we used the Android drawer widget, which pops-up with a swipe gesture from the left of the screen.

Side drawer in Android document browser

All the operations in the Android document browser were being performed in the same thread. Besides being suboptimal, the development framework actually forbids running network code in the main thread of the application. The next step for us was isolating the code that might need networking access when interacting with a cloud provider, and run it in separate threads.

ownCloud document provider

At that point, we had everything in place to write the code to access an ownCloud server. We did it with the help of an Android library provided by ownCloud developers.

There was still another task, though; any cloud service will likely need some configuration from the user for login credentials and so. We had to implement a preferences screen to enter these settings and do the proper wiring for the provider to be able to listen to any changes in them.

ownCloud settings screen

Documentation

To help other developers writing new document providers, we have tried to document the new code in detail, specially those interfaces that must be implemented to create new document providers. Besides, we will publish a document explaining how to extend the cloud storage integration here soon.

That’s all for now; to try the ownCloud provider you will have to build the feature/owncloud-provider-for-android branch yourself, while you will find the share feature in the packages already available in the Play Store or F-Droid. Hope you enjoy it!

LibreOffice for Android at FOSDEM

FOSDEM

Next week I’m flying to FOSDEM to let you know about our work in LibreOffice for Android, which has just been released in the Play Store. In my talk I will focus on the document browser, the new features we are currently working on and my own vision for the future of this component.

LibreOffice for Android, provider selection

You can read more about LibreOffice for Android in this post from The Document Foundation, and some gory technical details in Michael Meeks’ blog.

EDIT: Slides! Find them here.