LibreOffice hackfest in Madrid

Earlier this month, I attended a LibreOffice hackfest that has been held in Madrid. My goal for this hackfest was to work on the accessibility of the suite, in particular with the problem exposed in bugs 35652 and 91739, which together prevent the “say all” feature in ATs from working properly with LibreOffice.

The root of the problem is that LibreOffice only exposes a subset of the document through the accessibility APIs; in particular, only the visible parts of the document. This makes sense from the performance point of view, specially for huge documents, but poses a challenge for ATs.

To workaround this behavior, LibreOffice provides some means to navigate to off-screen elements through the flows-to relation. The last paragraph in the visible area has a relation of this kind with the next paragraph even if the latter is not on screen. Unfortunately, the associated accesibility tree does not reflect a proper parent-child structure when these relations are used: objects which should be present are absent, because they actually are never added to the tree. Additionally, when the user changes the visible area of the document, the accessible tree is rebuilt to contain only the visible contents of the document and it leaves orphaned objects left and right.

The proposal from my colleage Joanie, maintainer of Orca, is creating a new set of top-level children for the accessible document object to represent the pages in the document. These would be exposed through the accessibility APIs at any time, regardless of the visibility of said pages, and the actual contents would be lazily loaded when required by ATs. This should expose information to ATs properly in a tree structure and keep most of the document unloaded until really needed. Besides, the problems that appear when navigating flows-to relations or changing the visible area should be addressed to keep the accessible tree clean and up-to-date.

Sadly, I could make no significant advances in the implementation of the above solution. First, the notion of accessible pages doesn’t even exist in Writer, it should be implemented completely from scratch. Besides, the “only visible objects exist” logic is deeply rooted in the code and it would need much more work than three days of hackfest. Still, the analysis work has been very valuable to see where the problems are and pave the way to fix them.

Finally, I’ve also been helping a new contributor to land his first patches in LibreOffice. We started with a cleanup of ATK roles, one of the first tasks I touched when working on accessibility in LibreOffice which was still ongoing, and now it’s close to be finished. We also tried to debug a docx import problem regarding the flip property and instead found a regression in image flip for certain types of bitmaps. The work resulted in another patch for the latter problem which is already merged.

This has been a great occassion to hang out with the community, help newcomers and get some hacking done. I’m very thankful to Igalia for having sponsored my trip and stay in Madrid. See you in the next event!

Igalia & LibreOffice

Updated LibreOffice workshop at A Coruña University

I’ve been invited to repeat the workshop about LibreOffice I conducted last year at the University of A Coruña. Contents are largely the same but I’ve done some updates, hence this short entry to share the new slides:

It was a great session, with many interesting questions from the students. Thanks to Juan José Sánchez for the invitation! I hope to be able to repeat next year 🙂

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.

Speaking at Protocols Plugfest 2015

In a couple of hours, I will go to the airport and head for Zaragoza, where I’m taking part of the Protocols Plugfest 2015 happening this week.

Protocols Plugfest 2015 logo

Igalia will contribute a speech about LibreOffice interoperability with ECM solutions, specially SharePoint, through the CMIS protocol.

See you there!

EDIT: The plugfest is over! Thanks to everyone attending. By the way, I couldn’t help doing some changes to the slides the night before the talk; I’ve updated the link above.

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.