Salut,
venant du monde Windows, je suis assez nouveau en Android et j'ai de gros problèmes de synchronisation dans mon projet Android actuel.
Solution recherchée : synchroniser les Intent d'installation et de désinstallation de package afin qu'ils se déroulent l'un après l'autre.

Le but du projet est d'installer automatiquement les dernières versions des programmes Android disponibles dans l'entreprise.
Pour cela, le programme récupère via service web une liste de programmes, le chemin de leur APK (nouvelle version coté serveur) ainsi que leur numéro de version (coté serveur) et un booleen précisant s'il doit s'exécuter automatiquement après l'installation, le tout sous la forme d'un DataSet.
Une fois le DataSet récupéré, pour chaque programme listé je compare le numéro de version locale du programme déjà installé (s'il est déjà installé) au numéro de version coté serveur à (potentiellement) installer.
Ensuite, pour chaque programme à installer (car version locale inférieure à la version serveur) je télécharge les APK un par un sur la tablette.

Jusque là tout va bien, c'est maintenant que ca se corse :

Pour chaque programme je dois :
- Regarder s'il existe en local
- si non je dois lancer l'installation
- si oui je dois le désinstaller
- s'il a bien été désinstallé, je dois installer la nouvelle version
- s'il n'a pas été désinstallé (annulation...) je dois le désinstaller...
- s'il a bien été installé je dois le lancer s'il est marqué en "auto"

J'ai fait des fonctions (qui fonctionnent !!) pour chacune de ces étapes;
Le problème c'est que j'ai vraiment du mal à comprendre le fonctionnement d'Android au niveau des threads UI et des synchronisations des fonctions.
Du coup, si j'ai 3 programmes à installer, les 3 désinstallations se lancent en même temps, les installations aussi et le programme veut m'exécuter en auto un package qui est en cours de désinstall ou d'install...

Voici les fonctions d'installation, de désinstallation, d'execution et de présence des packages :
(j'utilise Xamarin pour pouvoir bosser en c#, mais ca devrait être lisible pour les adeptes de java)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
 
        public static async Task<bool> Installation_Package(string package, Activity activity)
        {
            Intent intent = new Intent(Android.Content.Intent.ActionInstallPackage);
            intent.SetDataAndType(Android.Net.Uri.FromFile(new Java.IO.File(package)), "application/vnd.android.package-archive");
            intent.SetFlags(ActivityFlags.NewTask);
            intent.PutExtra(Intent.ExtraReturnResult, true);
 
            activity.StartActivityForResult(intent, 2); //2 pour installation           
 
            return true;
        }
 
        public static async Task<bool> DesInstallation_Package(string package, Activity activity)
        {
            Intent intent = new Intent(Android.Content.Intent.ActionUninstallPackage, Android.Net.Uri.FromParts("package", activity.ApplicationContext.PackageManager.GetPackageArchiveInfo(Android.Net.Uri.FromFile(new Java.IO.File(package)).Path, 0).PackageName, null));
            intent.PutExtra("android.intent.extra.UNINSTALL_ALL_USERS", true);
            intent.PutExtra(Intent.ExtraReturnResult, true);
 
            activity.StartActivityForResult(intent, 1); //1 pour desinstallation           
 
            return true;
        }
 
        public static async Task<bool> EstLancee_Package(string package, Activity activity)
        {
            ActivityManager activityManager = (ActivityManager)activity.ApplicationContext.GetSystemService(Context.ActivityService);
            IList<ActivityManager.RunningAppProcessInfo> procInfos = activityManager.RunningAppProcesses;
            if (procInfos != null)
            {
                foreach (ActivityManager.RunningAppProcessInfo processInfo in procInfos)
                {
                    if (processInfo.ProcessName.Equals(package))
                    {
                        return true;
                    }
                }
            }
 
            return false;
        }
 
        public static async Task<bool> EstInstalle_Package(string package, Activity activity)
        {
            try
            {
                PackageInfo info = activity.ApplicationContext.PackageManager.GetPackageInfo(package, 0);
                return info != null;
            }
            catch
            {
                return false;
            }
        }
 
        public static async Task<bool> Execute_Package(string package, Activity activity)
        {
            Intent launchIntent = activity.ApplicationContext.PackageManager.GetLaunchIntentForPackage(package);
            if (launchIntent != null)
            {
                activity.StartActivity(launchIntent);
            }
 
            return true;
        }
Indépendamment elles fonctionnent toutes.

Pour les appeler depuis mon activity principale j'utilise ces deux fonctions :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
 
        private async Task<bool> MiseAJour_Programmes()
        {
            bool termine = false;
 
            foreach (DataRow dr_Programme in _dt_Programmes.Rows)
            {
                if ((bool)dr_Programme["MAJ"])
                {
                    termine = await MiseAJour_Programme(dr_Programme);
                }
            }
 
            return true;
        }
 
        private async Task<bool> MiseAJour_Programme(DataRow dr_Programme)
        {
            string nom_Package = dr_Programme["Nom_Package"].ToString();
            string nom_Programme = dr_Programme["Nom"].ToString();
            string chemin_Ecriture_Package = Chemin_Ecriture_Package(nom_Package);
            bool termine;
 
            if (await Outils_Android_Packages.Packages.EstInstalle_Package(nom_Package, this))
            {
                termine = await Outils_Android_Packages.Packages.DesInstallation_Package(chemin_Ecriture_Package, this);
            }
            else
            {
                termine = await Outils_Android_Packages.Packages.Installation_Package(chemin_Ecriture_Package, this);
            }
 
            return true;
        }
Après de multiples recherches j'ai vu qu'il fallait utiliser StartActivityForResult et récupérer le résultat des Intent dans la fonction suivante :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
 
        protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
        {
            switch (requestCode)
            {
                    case 1://après la désinstallation
                              REINSTALLER LE PACKAGE QUE JE VIENS DE DESINSTALLER
                        break;
                    case 2://après l'installation
                              POTENTIELLEMENT EXECUTER LE PROGRAMME NOUVELLEMENT INSTALLE
                        break;
            }
        }
quand j'installe ou désinstalle un package, l'activity lance bien le "OnActivityResult" et le requestCode correspond bien à ce que j'ai lancé. Par contre le data est toujours "null".
De plus, après la désinstallation il faut le réinstaller, or, comme plusieurs désinstalles se lancent en même temps je ne sais pas laquelle a fini et du coup laquelle réinstaller (j'espérais récupérer cette info via l'Intent 'data' mais il est null...).

Du coup j'ai deux problèmes :
- la fonction 'MiseAJour_Programmes()' me lance toutes les install/desinstall à la suite sans attendre alors que j'aimerais pouvoir les exécuter les unes après les autres.
- la fonction 'OnActivityResult(...)' ne me donne pas d'info sur 'qui vient de finir son taf' et du coup m'empêche de réinstaller/executer le package.

Si vous voyer trainer plein de 'await', 'async', 'Task' etc... inutiles, c'est bien parce que j'ai TOUT tenté ^^ et qu'aucun tutoriel ne m'a permis de comprendre la subtilité (soit trop vagues, soit qu'un concept prérequis important m'échappe)

Voila, donc si quelqu'un à une (ou deux) solutions j'en serais très content.
S'il y a une méthode alternative aussi
Et si vous connaissez un tutoriel (en français de préférence) suffisamment explicite sur les await/async/task/asynctask et tout ça ce serait farpait.

Eternellement Merci,
TheMacleod.