Affichage d'un document Office sous Android

33
Visualiser un document dans une appli Android by Johann Hilbold & Alain Boudard @OxianeIT 1

description

Présentation réalisée à l'occasion de Devoxx France 2012. Auteurs : Johann Hilbold et Alain Boudard

Transcript of Affichage d'un document Office sous Android

Page 1: Affichage d'un document Office sous Android

Visualiser un document dans une appli Android

by Johann Hilbold & Alain Boudard

@OxianeIT

1

Page 2: Affichage d'un document Office sous Android

Abstract

• Ce que je veux faire: afficher un document Office ou PDF dans une application Android

• Comment pallier à l’absence de gestion native des documents Office et PDF?

• Présentation des différentes solutions• Un retour sur expérience d’une aventure folle

Portabilité de Java vers Android Utilisation du code natif “quand y’a pas le choix!”

2

Page 3: Affichage d'un document Office sous Android

Johann Hilbold & Alain Boudard

Développeur Android Développeur Swing à ses débutsOxiane

3

Développeur Android WebdesignerOxiane Studio

Page 4: Affichage d'un document Office sous Android

Plan

4

• Introduction• Contraintes• Solutions• Conclusion

Page 5: Affichage d'un document Office sous Android

Contraintes• Ne pas sortir de l’application pour ouvrir un document (!)• Ne pas sortir le document du SI (= pas de google docs)• Afficher des documents Office (Office 2003, 2007) et PDF

5

Page 6: Affichage d'un document Office sous Android

Solutions• Afficher un document Office « binaire » dans une app• Afficher un document Office « xml » dans une app• Afficher un PDF dans une app

6

Page 7: Affichage d'un document Office sous Android

Afficher un fichier Office « binaire » - 1

C’est l’exemple le plus simple:

Il suffit d’utiliser la librairie Apache POI.

3 jars à télécharger:• poi-scratchpad-3.8.jar, • poi-3.8.jar • et commons-codec.jar

Copier des fichiers de test dans le répertoire Assets

7

Page 8: Affichage d'un document Office sous Android

Afficher un fichier Office « binaire » - 2

public void onCreate(Bundle savedInstanceState) {      super.onCreate(savedInstanceState);  //copier les fichiers de test depuis mon répertoire assets vers la SDCard      copyAssetToSDCard("test.doc");      copyAssetToSDCard("testxls.xls");    //lancer la conversion avec les jars d'apache POI(source, dest)      WordToHtmlConverter.main(new String[]{"/mnt/sdcard/test.doc", "/mnt/sdcard/test.html"});      ExcelToHtmlConverter.main(new String[]{"/mnt/sdcard/testxls.xls", "/mnt/sdcard/testxls.html"});  }  

8

Page 9: Affichage d'un document Office sous Android

Afficher un fichier Office « binaire » - 3

private final int BUF_SIZE = 8192;  private File copyAssetToSDCard(String assetName) {         File copiedFileStoragePath = new File("/mnt/sdcard/"+assetName);         BufferedInputStream bis = null;         OutputStream dexWriter = null;         try {             bis = new BufferedInputStream(getAssets().open(assetName));             dexWriter = new BufferedOutputStream(new FileOutputStream(copiedFileStoragePath));             byte[] buf = new byte[BUF_SIZE];             int len;             while((len = bis.read(buf, 0, BUF_SIZE)) > 0) {                 dexWriter.write(buf, 0, len);             }             dexWriter.close();             bis.close();           } catch (Exception e) {e.printStackTrace();         return null;}         Log.d("OfficeCopy", "copied "+assetName+" to /sdcard");         return copiedFileStoragePath; }

9

Page 10: Affichage d'un document Office sous Android

Afficher un fichier Office « binaire » - 4

Et voila!

Maintenant, on peut afficher le résultat dans une webview

WebView v = (WebView) findViewById(R.id.ma_web_view);  v.loadUrl("file://mnt/sdcard/test.html"); 

10

DEMO

Page 11: Affichage d'un document Office sous Android

Afficher un document Office « xml » - 1

Apache POI offre des services de lecture/écriture de fichiers Open XML

Mais c’est plus compliqué!

Si on essaye la méthode précédente (documents binaires)…

org.apache.poi.poifs.filesystem.OfficeXmlFileException: The supplied data appears to be in the Office 2007+ XML. You are calling the part of POI that deals with OLE2 Office Documents. You need to call a different part of POI to process this data (eg XSSF instead of HSSF)

11

Page 12: Affichage d'un document Office sous Android

Afficher un document Office « xml » - 2

Utilisation d’Apache POI – XSSF

Le plan:

Utiliser le même raisonnement que pour les documents « binaires »: • télécharger les jars pour les documents Open XML

http://poi.apache.org/overview.html#components OK!• Les ajouter au build path OK!• Créer une petite classe pour appeler les bonnes méthodes

org.apache.poi.ss.examples.html.ToHtml.main(String[] files) OK!

• Lancer le projet et prier! NOK!

12

Page 13: Affichage d'un document Office sous Android

Afficher un document Office « xml » - 3

Que s’est-il passé?Avant même de pouvoir installer l’APK sur le device,

[Dex Loader] Unable to execute dex: null[myProject] Conversion to Dalvik format failed: Unable to execute dex: null

Lors de la génération du fichier APK, tous les fichiers .class sont transformés en un unique fichier .dex (Dalvik EXecutable).

Cette transformation est limitée à 64k références de méthodes (!)Nos jars (de 24Mo) pèsent plus de 64k méthodes!

13

Page 14: Affichage d'un document Office sous Android

Afficher un document Office « xml » - 4

La solution du blog Android Developper

Il « suffit » de découper nos classes en plusieurs fichiers .jar

Puis de lancer la dx.bat sur chacun des jars

Il convient d’utiliser le script Ant fourni dans le SDKandroid-sdk\tools\ant\build.xml

Et de le modifier de la sorte:<dex-helper-mod input-dir="${basedir}/libsToSpecialDEX/1"output-dex-file="${out.absolute.dir}/onary_dex_dir/classes.dex"/>

<jar destfile="${asset.absolute.dir}/onary_dex.jar" basedir="${out.absolute.dir}/onary_dex_dir" includes="classes.dex" />

14

Page 15: Affichage d'un document Office sous Android

Afficher un document Office « xml » - 5

Si tout s’est bien passé… On peut maintenant charger les classes en mémoire à l’exécution

private Class loadClass(String className) {      new File("/sdcard/office/opti").mkdirs();      final File optimizedDexOutputPath = new File("/sdcard/office/opti");        DexClassLoader cl = new DexClassLoader("/sdcard/office/one.jar:/sdcard/office/two.jar", optimizedDexOutputPath.getAbsolutePath(),  

null,  

TestOfficeAndroidActivity.this.getClassLoader());      Class libProviderClazz = null;      try {            libProviderClazz = cl.loadClass(className);      }      catch(Exception e)      {e.printStackTrace();}      return libProviderClazz;  

}   15

Page 16: Affichage d'un document Office sous Android

Afficher un document Office « xml » - 6

Une fois la classe chargée en mémoire…

Class cl = loadClass( "org.apache.poi.ss.examples.html.ToHtml");  Class[] paramTypes = new Class[] { String[].class };Method main = cl.getMethod("main", paramTypes);main.invoke(o, (Object) new String[] {"/sdcard/office/SampleSS.xlsx", 

"/sdcard/office/SampleSS.html"}); 

Et c’est seulement à partir d’ici que ça devient vraiment intéressant!

W/System.err(370): Caused by: java.lang.RuntimeException: Installation Problem??? Couldn’t load messages: Can’t find resource for bundle ‘org.apache.xmlbeans.impl.regex.message_en_US’, key  »

16

Page 17: Affichage d'un document Office sous Android

Afficher un document Office « xml » - 7

Que peut bien vouloir dire cette erreur?Le fichier message.properties est introuvable !

Il n’est plus présent dans mon fichier dex! Mais présent dans XmlBeans-1.0.jar

La compilation en fichier APK, c’est un zip avec un fichier .dex pour les .class (et uniquement les .class!) tout le reste (images, xml de layout, fichiers raw) dans des dossiers /res/drawable, /res/layout, /raw…

Le fichier .properties n’est donc pas resté dans le .dex!

17

Page 18: Affichage d'un document Office sous Android

Afficher un document Office « xml » - 8

On peut tricher… XmlBeans est OpenSource!

public void setLocale(Locale locale) {try {//this.resources = ResourceBundle.getBundle("org.apache.xmlbeans.impl.regex.message", locale); this.resources = new ResourceBundle() {

@Overrideprotected Object handleGetObject(String key) {return res.get(key);}@Overridepublic Enumeration<String> getKeys() {return Collections.enumeration(res.keySet());}

}; } catch (MissingResourceException mre) {

throw new RuntimeException("Installation Problem??? Couldn't load messages: "+mre.getMessage());

}}static HashMap<String, String> res = new HashMap<String, String>();static{res.put("parser.parse.1", "Wrong character.");} 1

8

Page 19: Affichage d'un document Office sous Android

Afficher un document Office « xml » - 9

Cette fois-ci, ça va passer…Toujours pas!org.apache.xmlbeans.SchemaTypeLoaderException: XML-BEANS compiled schema: Could not locate compiled schema resource {…..}/index.xsb Les fichiers .xsb ne sont pas dans les .dex, mais cette fois, on sait pourquoi!

package org.apache.xmlbeans.impl.schema;public class ClassLoaderResourceLoader{public InputStream getResourceAsStream(String name) { try {

return new FileInputStream("/sdcard/office/xsb/"+name.substring(name.lastIndexOf("/")+1));

} catch (FileNotFoundException e) {e.printStackTrace();} //_classLoader.getResourceAsStream(resourceName);return null;

} 19

Page 20: Affichage d'un document Office sous Android

Afficher un document Office « xml » - 10

On tient le bout…

Maintenant qu’on accède à toutes les ressources du jar original…

ClassNotFoundException: java.awt.Color

POI est censé tourner sous Java 1.5 Android ne connait pas les packages java.awt (et quelques autres)

Une ultime manipulation: remplacer tous les java.awt par des and.awt dans les sources de POI

http://code.google.com/p/awt-android-compat/Attention, le résultat n’est pas parfait

pas de gestion des images (pour l’instant)20

DEMO

Page 21: Affichage d'un document Office sous Android

21

Afficher un document Office « xml » - 11

Une approche plus sexy:- Intégrer les ressources manquantes aux jars

- Création d’un jar simple- « Dexisation » du jar en un fichier classes.dex- Réimportation des ressources dans un nouveau jar créé à la main NOK!

Le dexloader n’a pas l’air de gérer les jars et les APK de la même façon- Packaging du classes.dex dans un APK « maison » OK!

- En réutilisant cette technique, on peut donc, en théorie, se passer des étapes précédentes

21

DEMO

Page 22: Affichage d'un document Office sous Android

Afficher un document Office « xml » - 12

Conclusion de la méthode POI

Le code complet est disponible ici:https://code.google.com/p/display-msoffice-docs-android-with-apache-poi/

Avantages:• Permet une conversion très complète (celle que permet le client POI)• Est très rapide (adaptée aux processeurs « lents » des mobiles

d’aujourd’hui)• Donne accès à tous les formats (doc/docx, ppt/pptx, xls/xlsx, et même

Visio .vsd ou Outlook msg)• Est chargeable dynamiquement en mémoire• Laisse la porte ouverte à un futur « mode édition »

22

Page 23: Affichage d'un document Office sous Android

Afficher un document Office « xml » - 13

Inconvénients:• Utilise énormément de place sur la SDcard (8Mo d’APK + 15Mo de

ressources)• Compliqué à mettre en œuvre• Pour la partie AWT, repose sur le package and.awt

Pas de gestion des java.awt.BufferedImage. Il faudrait les convertir en android.graphics.Bitmap!

23

Page 24: Affichage d'un document Office sous Android

Afficher un document Office « xml » - 14

L’alternative

La transformation XSL basée sur la solution de Julien Chablehttp

://openxmldeveloper.org/blog/b/openxmldeveloper/archive/2006/11/21/openxmlandjava.aspx

• Un procédé beaucoup plus simple, mais moins efficace.

<!-- Body and paragraphs --><xsl:template match="w:body">

<html><body>

<xsl:for-each select="w:p"><p>

<xsl:apply-templates select="w:pPr" /><xsl:apply-templates select="w:r" />

</p></xsl:for-each>

</body></html>

</xsl:template>24

Page 25: Affichage d'un document Office sous Android

Afficher un document Office « xml » - 15

Avantages:• D’une légèreté déconcertante: mon APK fait seulement 261k• D’une simplicité déconcertante: un seul jar à ajouter au Build Path et

c’est fini.• Facilement améliorable: il « suffit » d’ajouter les balises qu’on veut gérer

Inconvénients:• Ne gère que les OfficeOpen XML (donc pas les formats « binaires »)• Il faut trouver (ou écrire soi même?) un XSL vraiment complet pour un

résultat parfait. Les images ne sont pas du tout gérées Seuls quelques styles sont implémentés, pas de formule, tableaux,

wordart…

25

Page 26: Affichage d'un document Office sous Android

Afficher un document Office « xml » - 16

Structure d’un fichier docx

26

Page 27: Affichage d'un document Office sous Android

Afficher un document Office « xml » - 17

Utilisation des rID pour retrouver le style ou le nom de fichier pour une image avec le fichier document.xml.rels

w:drawing crée une balise <img>avec pour attribut id = rID

On extrait toutes les images de/word/media vers la SDCard

2ème parsing:scan de toutes les balises img et ajout de l’attribut grâce à la correspondance rID

27

Page 28: Affichage d'un document Office sous Android

Afficher un document Office « xml » - 18

Malgré cette technique pour afficher les images, cette solution reste imparfaite

• Les positions des éléments ne sont pas gérées• Les puces, formules, tableaux ne sont pas gérés

• Un exemple existant de traitement XSL de document duquel on pourrait s’inspirer: DocBook

Mon code est disponible icihttps

://code.google.com/p/display-msoffice-docs-android-without-apache-poi/

28

DEMO

Page 29: Affichage d'un document Office sous Android

Afficher un PDF -1Une bonne occasion de voir le NDK

• Sous windows, installer Cygwinhttp://mindtherobot.com/blog/452/android-beginners-ndk-setup-step-by-step/

(étapes 2 et 3)• Télécharger le projet APV

http://code.google.com/p/apv/

• Lancer la commande /script/build-native.sh du projet APV

• Si tout s’est bien passé, on peut lancer le projet APV sous Eclipse!

29

Page 30: Affichage d'un document Office sous Android

Afficher un PDF -2C’est très bien mais c’est pas ce qu’on veut!

On vient de faire l’équivalent d’installer l’appli « Acrobat Reader »

30

Fichier librairie qui permet de parser les PDFs

Page 31: Affichage d'un document Office sous Android

Afficher un PDF -2Customiser un peu…

Extraction de la View PagesView• Par défaut, apv offre une view qui fait de nombreux appels à une

activité.

Un composant personnalisé

<com.oxiane.DisplayPDF android:id="@+id/my_pdf_view" android:layout_width="fill_parent" android:layout_height="fill_parent" app:src="http://monpdf.com" />

31

Page 32: Affichage d'un document Office sous Android

Afficher un PDF -3Inconvénients de la méthode

• Pas de gestion des liens HTTP• Encore 1,7Mo à rajouter à l’APK!• Chargement se fait en rectangles

Avantages de la méthode

• Ça a le mérite de marcher!

32

Page 33: Affichage d'un document Office sous Android

Conclusion

• Une aventure pionnière!• Un aperçu des possibilités qu’offre l’open source Java dans Android• Des ouvertures pour arriver à des résultats plus professionnels

33