Publier 2 apps Android avec le même code mais un design différent

Android APK logo

Pendant que je développais des jeux pour Firezoo, j’ai fait face à un problème que beaucoup de développeur Android ont du avoir avant moi: Comment publier deux apps avec le même code mais un design (apparence) différent sans dupliquer le projet/code source ?

En effet, dans Eclipse, il n’est pas possible de sélectionner ce que vous desirez dans votre apk (ressources et assets spécifiques) quand vous exportez votre projet d’application Android. De plus, vous allez avoir besoin d’un nom différent pour votre application et votre package dans AndroidManifest.xml et même quelques modifications mineurs dans le code en fonction de l’apparence de l’app.

Je vais expliquer ci-dessous la solution que j’ai choisi lorsque j’ai développé ces deux apps, si vous avez des suggestions ou de meilleures idées, n’hesitez pas à me le faire savoir. Le code est libre d’être utilisé par tous.

Différences dans les fichiers

Le premier problème concernait les différences dans certains fichiers comme le AndroidManifest.xml, string.xml, fichiers java ou encore fichiers C++ si vous utilisez le NDK.

Puisque j’utilise Git comme gestionnaire de version, j’ai pensé à créer une branche diffèrente pour ma deuxième app (NewDesignApp) pendant que mon app originel (MainApp) resterait sur la branche master.

Les changements nécessaires pour la deuxième app seront fait sur la branche NewDesignApp, par exemple changer du code, des chaînes de caractères, ajouter des arguments de compilation au code C++, etc. Chaque fois que MainApp évoluera sur la branche master, elle sera mergé sur la branche NewDesignApp permettant à la deuxième app de profiter des améliorations et corrections de bugs que reçoit l’app originel. Mais attention, la branche NewDesignApp ne sera JAMAIS merger dans la branche master (MainApp), elle va vivre pour toujours toute seule et la branche master n’aura jamais connaissance de la deuxième app.

Lorsque vous êtes prêt à exporter une app ou l’autre, vous avez juste à changer de branche sur Git ($git checkout NewDesignApp), nettoyer le projet Eclipse (Project ? Clean) et exporter votre app dans le répertoire spécifique.

Repackager l’APK

Le deuxième problème apparaît durant l’export, il est impossible dans Eclipse de choisir les fichiers que l’on veut exporter dans l’APK de l’app, tous les fichiers présents dans les dossiers assets et res seront packagé. Il serait possible de régler ce problème avec les branches Git comme expliqué auparavant, mais il y a un autre problème sous-jacent à cela et qui est très important, c’est que le nom du package doit être différent pour chaque App (APK). Pour cela, vous devrez utiliser un script pour dépakager et repackager votre APK avec à l’interieur l’ensemble des noms des paquets renommés et contenant uniquement les fichiers voulus (la taille de l’apk à de l’importance en mobilité).

Le script ci-dessous est un Script Shell qui peut être utilisé sous Linux ou Mac dans un terminal, mais également sous Windows avec MinGW ou Cygwin.

Outils nécessaires :

  • Java 1.7 (JRE 1.7) pour utiliser l’outil APK et Jarsigner
  • Le SDK de Android pour utiliser aapt et Zipalign.
  • ApkTool pour dépakager et repackager votre APK (https://code.google.com/p/android-apktool/)

L’exécutable aapt doit se trouver dans le PATH pour que ApkTool fonctionne correctement (e.g. sdk\build-tools\19.1.0). Vous pouvez également déplacer l’exécutable aapt dans votre dossier de repackaging.
Lors de l’éctriture de cet article, la version 2 de ApkTool n’était pas encore sortie officiellement, j’utilise la version bêta 9 (v2.0.0b9).

On Windows

Cette image montreà quoi peut ressembler votre dossier de repackaging sous Windows

Nous allons maintenant voir en détails ce que contient le script Repack.sh :

#!/bin/sh
# APK repacking script
OriginalAPK=$1.apk
FinalAPK=$2.apk
APKFolder=apk
Manifest=AndroidManifest.xml
InputFolder=input
OutputFolder=output
APKTool=apktool_2.0.0b9.jar

# Cleanup previous packaging
if [ -d "$APKFolder" ]; then
    echo Cleanup $APKFolder folder
    rm -r $APKFolder
fi

Les variables $1 et $2 font référence aux arguments sur la ligne de commande lors de l’exécution du script. Le script peut être lancé en ligne de commande comme ceci : $./Repack.sh NewDesignApp New\ Design\ App. La variable APKFolder est le répertoire où l’APK sera dépakager et repackager, il doit être supprimer s’il existe entre chaque lancement du script pour éviter une compilation incrémentale. InputFolder correspond au dossier où l’APK a été exporté à partir d’Eclipse, alors que OutputFolder est le répertoire où le nouveau APK sera déplacé à la fin de l’exécution du script. APKTool est le nom de la version courante de l’outil utilisé.

# Unpack selected apk from input folder
echo Start unpacking $OriginalAPK into $APKFolder folder
java -jar $APKTool d $InputFolder/$OriginalAPK -o $APKFolder
echo Finish unpacking

Ensuite viens le processus de dépackaging, rien de spécial à ce sujet.

# Change apk content depending on App type (MainApp, NewDesignApp, etc.)
if [ $1 = "MainApp" ]; then
    echo Rename package name in $Manifest
    sed -i -e 's/com.company.mainapp/com.company.newdesignapp/g' $APKFolder/$Manifest
    echo Rename package name in smali directory
    mv $APKFolder/smali/com/company/mainapp $APKFolder/smali/com/company/newdesignapp
    echo Rename package occurences in each files, can take some time...
    find $APKFolder/smali -type f -name *.smali -exec sed -i -e 's,company/mainapp,company/newdesignapp,g' {} \;
    echo Delete unnecessary assets
    AssetDir=MainApp
    rm -r $APKFolder/assets/$AssetDir
    rm $APKFolder/assets/api_key_MainApp.txt
elif [ $1 = "NewDesignApp" ]; then
    echo Delete unnecessary assets
    AssetDir=NewDesignApp
    rm -r $APKFolder/assets/$AssetDir
    rm $APKFolder/assets/api_key_NewDesignApp.txt
else
    echo "App not supported"
    exit 0
fi

Voilà la partie la plus importante du processus, il s’agit du renommage des fichiers et de leur contenu pour changer le nom du package Java. En effet, puisque le Play Store, mais également Java de manière générale, se base sur le nom du paquet pour identifier les apps, il n’est pas possible de publier deux applications avec le même nom de paquet, c’est pourquoi il est nécessaire de le renommé dans le Manifest ainsi que les fichiers smali.

Ensuite, vous avez la possibilité de supprimer des assets ou des ressources qui ne sont pas utilisés dans une version spécifique, ou même supprimé une clé d’API correspondant à une autre version de l’app. Vous avez juste besoin de supprimer les fichiers du répertoire apk avant de repackager.

# Repack apk and move it to output folder
echo Start repacking $OriginalAPK
java -jar $APKTool b $APKFolder
echo Finish repacking
echo Move final apk in $OutputFolder
if [ ! -d "$OutputFolder" ]; then
    echo Create $OutputFolder folder
    mkdir $OutputFolder
fi
mv $APKFolder/dist/$OriginalAPK $OutputFolder/$OriginalAPK

Maintenant commence le processus de repackaging et le fichier APK final est déplacé dans le répertoire de sortie.

# Sign and ZipAlign apk
echo Sign apk with keystore
jarsigner -sigalg MD5withRSA -digestalg SHA1 -keystore "androidkey" -storepass "mypassword" -keypass "mypassword" $OutputFolder/$OriginalAPK myandroidkey
echo Verify signing
jarsigner -verify -certs -keystore "androidkey" $OutputFolder/$OriginalAPK
if [ -f "$OutputFolder/$FinalAPK" ]; then
    echo Remove existing file : "$OutputFolder/$FinalAPK"
    rm "$OutputFolder/$FinalAPK"
fi
echo ZipAlign to a new apk : $FinalAPK
zipalign 4 "$OutputFolder/$OriginalAPK" "$OutputFolder/$FinalAPK"
rm "$OutputFolder/$OriginalAPK"

exit 0

Pour terminer le processus et obtenir un APK similaire à celui que l’on peut avoir lors de l’export avec Eclipse, vous aurez besoin de signer l’APK avec votre clé SHA1, vérifier la signature et utiliser l’outil zipalign sur l’APK.
« androidkey » est le nom de la clé du store (également nom du fichier), « myandroidkey » est le nom de la clé spécifique à cette app, groupe d’app. Il est recommandé par Google de signer l’ensemble des apps d’une même entreprise avec la même clé.

Vous devriez maintenant obtenir un APK prêt pour les tests sur votre appareil (en utilisant adb) et ensuite le publier sur votre magazin d’app préféré.