none
Widget periodisch aktualisieren ab API 26 RRS feed

  • Frage

  • Hallo,

    Ich habe ein Widget, welches mit unten stehenden Code periodisch überprüft ob eine Datei auf OneDrive geändert wurde und diese ggf. herunterlädt um Daten daraus zur Anzeige zu bringen.

    Leider funktioniert dieser Code auf einem Handy ab API 26 (Android 8 (Oreo)), wegen den neuen Einschränkungen für Hintergrunddienste, nicht mehr.  Auf Handys mit API < 26 gibt es keine Probleme.

    Die Lösung soll der Einsatz von JobScheduler, JobService bzw. JobIntentService. Im Netz habe ich bisher ein paar Beispiele gefunden, die mir aber nicht geholfen haben, da sie einen Service in einer laufenden Activity starteten aber nicht zeigten wie ein Widget periodisch aktualisiert wird.

    Kann mir jemand ein paar Tipps geben wie ich den Code umstellen muss, damit er auch auf Handys mit API >= 26 läuft?

    Viele Grüße Klaus

    Hier der Code:

    using Android.App;
    using Android.Appwidget;
    using Android.Content;
    
    namespace Termin_Manager_mobil
    {
        [BroadcastReceiver(Label = "@string/widget_name")]
        [MetaData("android.appwidget.provider", Resource = "@xml/widget_term_provider")]
        [IntentFilter(new string[] { "android.appwidget.action.APPWIDGET_UPDATE" })]
        [IntentFilter(new[] { Intent.ActionBootCompleted })]
        public class WidgetTermProvider : AppWidgetProvider
        {
            public override void OnUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
            {
                base.OnUpdate(context, appWidgetManager, appWidgetIds);
                Intent intent = new Intent(context, typeof(WidgetTermUpdateService));
                context.StartService(intent);
            }
            public override void OnReceive(Context context, Intent intent)
            {
                if (intent.Action == null || intent.Action.Equals(Intent.ActionBootCompleted) || intent.Action.Equals(AppWidgetManager.ActionAppwidgetUpdate))
                {
                    Intent startIntent = new Intent(context, typeof(WidgetTermUpdateService));
                    context.StartService(startIntent);
                }
            }
    
        }
    }
    
    using Android.App;
    using Android.Appwidget;
    using Android.Content;
    using Android.OS;
    using Android.Widget;
    using LibOneDriveAnd;
    using Microsoft.Graph;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using TerminClass;
    
    namespace Termin_Manager_mobil
    {
        [Service]
        public class WidgetTermUpdateService : Service
        {
            private const int cTerminAnz = 5;
            private enum Filestatus { NoFile, FileAktuell, FileDownloaded, StartUpdate, Error };
            private static Filestatus fileStatus = Filestatus.NoFile;
            private static RemoteViews UpdateViews { get; set; } = null;
    
            public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
            {
                Context context = this;
    
                // Click registrieren
                UpdateViews = new RemoteViews(context.PackageName, Resource.Layout.WidgetTermine);
                Intent intentRunApp = new Intent(context, typeof(MainActivity));
                PendingIntent configPendingIntent = PendingIntent.GetActivity(context, 0, intentRunApp, 0);
                UpdateViews.SetOnClickPendingIntent(Resource.Id.widgetTerm, configPendingIntent);
                try
                {
                    // Termindatei prüfen und ggf.Termine einlesen und anzeigen:
                    SetAktuTime(Filestatus.StartUpdate);
                    LoadTermineAsync(context);
                }
                catch
                {
                    SetAktuTime(Filestatus.Error);
                }
                return StartCommandResult.NotSticky;
            }
    
            public override IBinder OnBind(Intent intent)
            {
                return null;
            }
    
            private static async void LoadTermineAsync(Context context)
            {
                ......
                TermineToViews(terminFn, fileStatus, context);
            }
    
            private static void TermineToViews(string terminFn, Filestatus state, Context context)
            {
                ........
                AppWidgetManager manager = AppWidgetManager.GetInstance(context);
                ComponentName thisWidget = new ComponentName(context, Java.Lang.Class.FromType(typeof(WidgetTermProvider)).Name);
                manager.UpdateAppWidget(thisWidget, UpdateViews);
    
                Intent intent = new Intent(context, typeof(WidgetTermUpdateService));
                context.StopService(intent);
            }
        }
    }
    Freitag, 11. Oktober 2019 13:37

Antworten

  • Hallo,

    ich habe jetzt eine Lösung für mein Problem doch noch hinbekommen. Sie funktioniert aber erst ab der API >=25. Bei kleineren APIs kommt leider beim Installieren eine Fehlermeldung beim Parsen der APK-Datei.  Ich werden deshalb mit 2 Versionen arbeiten müssen, die Alte und die Neue.  

    Falls es jemand interessiert, hier ist die Lösung:

    using Android.App;
    using Android.Appwidget;
    using Android.Content;
    
    namespace Termin_Manager_mobil
    {
        [BroadcastReceiver(Label = "@string/widget_name")]
        [MetaData("android.appwidget.provider", Resource = "@xml/widget_term_provider")]
        [IntentFilter(new string[] { "android.appwidget.action.APPWIDGET_UPDATE" })]
        [IntentFilter(new[] { Intent.ActionBootCompleted })]
        public class WidgetTermProvider : AppWidgetProvider
        {
            public override void OnUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
            {
                base.OnUpdate(context, appWidgetManager, appWidgetIds);
                StartService(context);
            }
            public override void OnReceive(Context context, Intent intent)
            {
                if (intent.Action == null || intent.Action.Equals(Intent.ActionBootCompleted) || intent.Action.Equals(AppWidgetManager.ActionAppwidgetUpdate))
                {
                    StartService(context);
                }
            }
    
            private void StartService(Context context)
            {
                Intent startIntent = new Intent(context, typeof(WidgetTermUpdateService));
                context.StartService(startIntent);
            }
    
        }
    }
    

    using Android.App.Job;
    using Android.Content;
    
    namespace Termin_Manager_mobil
    {
        public static class WidgetJobHelper
        {
            public static JobInfo.Builder CreateJobBuilderUsingJobId<T>(this Context context, int jobId, int waitMilliSec) where T : JobService
            {
                var javaClass = Java.Lang.Class.FromType(typeof(T));
                var componentName = new ComponentName(context, javaClass);
                return new JobInfo.Builder(jobId, componentName)
                                .SetPersisted(true)
                                .SetMinimumLatency(waitMilliSec)   
                                .SetOverrideDeadline(waitMilliSec * 3); 
            }
    
        }
    }

    using Android.App;
    using Android.App.Job;
    using Android.Appwidget;
    using Android.Content;
    using Android.OS;
    using Android.Widget;
    using LibOneDriveAnd;
    using Microsoft.Graph;
    using System;
    using System.Collections.Generic;
    using System.IO;
    using TerminClass;
    
    namespace Termin_Manager_mobil
    {
        [Service]
        public class WidgetTermUpdateService : Service
        {
            public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
            {
                var jobBuilder = WidgetJobHelper.CreateJobBuilderUsingJobId<WidgetTermUpdateJobService>(Application.Context, 1, 2*1000);
                var jobInfo = jobBuilder.Build();
                JobScheduler jobScheduler = (JobScheduler)GetSystemService(JobSchedulerService);
                jobScheduler.Schedule(jobInfo);
                jobBuilder.Dispose();
    
                return StartCommandResult.NotSticky;
            }
            public override IBinder OnBind(Intent intent)
            {
                return null;
            }
        }
    
        [Service(Name = "RaSoWa.Termin_Manager_mobil.WidgetTermUpdateJobService",
         Permission = "android.permission.BIND_JOB_SERVICE")]
        public class WidgetTermUpdateJobService : JobService
        {
            private const int cTerminAnz = 5;
            private static readonly int cWaitMilliSec = Application.Context.Resources.GetInteger(Resource.Integer.widget_waitMilliSec);
            private enum Filestatus { NoFile, FileAktuell, FileDownloaded, StartUpdate, Error };
            private static Filestatus fileStatus = Filestatus.NoFile;
            private static RemoteViews UpdateViews { get; set; } = null;
    
            public override bool OnStartJob(JobParameters jobParams)
            {
                Context context = this;
                if (UpdateViews == null)
                {   // Click registrieren:
                    UpdateViews = new RemoteViews(context.PackageName, Resource.Layout.WidgetTermine);
                    Intent intentRunApp = new Intent(context, typeof(MainActivity));
                    PendingIntent configPendingIntent = PendingIntent.GetActivity(context, 0, intentRunApp, 0);
                    UpdateViews.SetOnClickPendingIntent(Resource.Id.widgetTerm, configPendingIntent);
                }
                try
                {
                    // Termindatei prüfen und ggf.Termine einlesen und anzeigen:
                    SetAktuTime(Filestatus.StartUpdate);
                    LoadTermineAsync(context);
                }
                catch
                {
                    SetAktuTime(Filestatus.Error);
                }
                JobFinished(jobParams, false);
    
                var jobBuilder = WidgetJobHelper.CreateJobBuilderUsingJobId<WidgetTermUpdateJobService>(Application.Context, 1, cWaitMilliSec);
                var jobInfo = jobBuilder.Build();
                JobScheduler jobScheduler = (JobScheduler)GetSystemService(JobSchedulerService);
                int result = jobScheduler.Schedule(jobInfo);
                jobBuilder.Dispose();
                if (result == JobScheduler.ResultSuccess)
                    return true;   
                 else   
                    return false;  
            }
            public override bool OnStopJob(JobParameters jobParams)
            {
                return false;
            }
         
            private static async void LoadTermineAsync(Context context)
            {
               ……….
                    TermineToViews(terminFn, fileStatus, context);
            }
            private static void TermineToViews(string terminFn, Filestatus state, Context context)
               …….
                AppWidgetManager manager = AppWidgetManager.GetInstance(context);
                ComponentName thisWidget = new ComponentName(context, Java.Lang.Class.FromType(typeof(WidgetTermProvider)).Name);
                manager.UpdateAppWidget(thisWidget, UpdateViews);
    
                Intent intent = new Intent(context, typeof(WidgetTermUpdateJobService));
                context.StopService(intent);
            }
        }
    }
    

    Für Verbessungsvorschläge bin ich immer offen.

    Grüße Klaus.


    • Als Antwort markiert Klaus Raabe Mittwoch, 16. Oktober 2019 18:09
    Mittwoch, 16. Oktober 2019 16:18