Проблема обслуживания приложения шагомер Xamarin Forms переднего плана на Android Oreo 8.1 и Android пирог 9,0 - PullRequest
1 голос
/ 29 марта 2019

Я использую службу переднего плана, если версия ОС> = 8.0. На Android 8.1 и 9.0, когда мое приложение шагомер открыто или на заднем плане оно считает шаги, но не когда приложение закрыто. Также после закрытия приложения значок уведомления исчезает с экрана телефона. Протестировано на Oreo 8.0 с использованием службы переднего плана, работает нормально и подсчитывает шаги, когда приложение закрыто. Проверено также на андроиде Nougat 7.0 с фоновым сервисом и работающим без проблем.

BootReceiver.cs

[BroadcastReceiver(Enabled = true, Exported = true, DirectBootAware = true)]
    [IntentFilter(new string[] { Intent.ActionBootCompleted, Intent.ActionLockedBootCompleted, "android.intent.action.QUICKBOOT_POWERON", "com.htc.intent.action.QUICKBOOT_POWERON" })]
    public class BootReceiver : BroadcastReceiver
    {
        public override void OnReceive(Context context, Intent intent)
        {
             var stepServiceIntent = new Intent(context, typeof(StepService));
            if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O)
            {
                context.StartForegroundService(stepServiceIntent);
            }
            else
            {
                context.StartService(stepServiceIntent);
            }
        }
    }

StepServiceBinder.cs

public class StepServiceBinder : Binder
    {
        StepService stepService;
        public StepServiceBinder(StepService service)
        {
            this.stepService = service;
        }

        public StepService StepService
        {
            get { return stepService; }
        }
    }

StepServiceConnection.cs

 public class StepServiceConnection : Java.Lang.Object, IServiceConnection
    {
        MainActivity activity;

        public StepServiceConnection(MainActivity activity)
        {
            this.activity = activity;
        }

        public void OnServiceConnected(ComponentName name, IBinder service)
        {
            var serviceBinder = service as StepServiceBinder;
            if (serviceBinder != null)
            {
                activity.Binder = serviceBinder;
                activity.IsBound = true;
            }
        }

        public void OnServiceDisconnected(ComponentName name)
        {
            activity.IsBound = false;
        }
    }

* Manifest.xml *

<manifest android:versionName="1.1" package="com.PedometerApp" android:installLocation="internalOnly" android:versionCode="11">
    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="27" />
    <application android:label="My Pedometer" android:icon="@drawable/logo">    </application>
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
</manifest>

StepService.cs

[Service(Enabled = true)]
    [IntentFilter(new String[] { "com.PedometerApp.StepService" })]
    public class StepService :  Service, ISensorEventListener, INotifyPropertyChanged
    {
        private SensorManager sManager;
        private bool isRunning;
        private long stepsToday = 0;
        public bool WarningState
        {
            get;
            set;
        }

        public long StepsToday
        {
            get { return stepsToday; }
            set
            {
                if (stepsToday == value)
                    return;

                stepsToday = value;
                OnPropertyChanged("StepsToday");
                Settings.CurrentDaySteps = value;
                MessagingCenter.Send<object, long>(this, "Steps", Settings.CurrentDaySteps);
            }
        }

        public const string PRIMARY_NOTIF_CHANNEL = "exampleChannel";
        public const int SERVICE_RUNNING_NOTIFICATION_ID = 10000;
        public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId)
        {
            RegisterForService();
            var warning = false;
            if (intent != null)
                warning = intent.GetBooleanExtra("warning", false);
            Startup();
            return StartCommandResult.Sticky;
        }

        private void RegisterForService()
        {
            if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
            {
                var channel = new NotificationChannel(PRIMARY_NOTIF_CHANNEL, "Pedometer Service Channel", NotificationImportance.Low)
                {
                    Description = "Foreground Service Channel"
                };

                var notificationManager = (NotificationManager)GetSystemService(NotificationService);
                notificationManager.CreateNotificationChannel(channel);

                var notification = new Notification.Builder(this, PRIMARY_NOTIF_CHANNEL)
                .SetContentTitle("Service")
                .SetContentText("Running")
                .SetSmallIcon(Resource.Drawable.ic_stat_name)
                .SetContentIntent(BuildIntentToShowMainActivity())
                .SetOngoing(true)
                .Build();
                StartForeground(SERVICE_RUNNING_NOTIFICATION_ID, notification);
            }
            else
            {
                BuildIntentToShowMainActivity();
            }
        }

        PendingIntent BuildIntentToShowMainActivity()
        {
            var alarmManager = ((AlarmManager)ApplicationContext.GetSystemService(AlarmService));
            var intent2 = new Intent(this, typeof(StepService));
            intent2.PutExtra("warning", WarningState);
            var stepIntent = PendingIntent.GetService(ApplicationContext, 200, intent2, PendingIntentFlags.UpdateCurrent);
            alarmManager.Set(AlarmType.Rtc, Java.Lang.JavaSystem
              .CurrentTimeMillis() + 1000 * 60 * 60, stepIntent);

            return stepIntent;
        }



        public override void OnTaskRemoved(Intent rootIntent)
        {
            base.OnTaskRemoved(rootIntent);

            UnregisterListeners();
            var intent = new Intent(this, typeof(StepService));
            intent.PutExtra("warning", WarningState);
            ((AlarmManager)GetSystemService(AlarmService)).Set(AlarmType.Rtc, Java.Lang.JavaSystem
                .CurrentTimeMillis() + 500,
                PendingIntent.GetService(this, 201, intent, 0));
        }

        private void Startup(bool warning = false)
        {
            CrunchDates(true);

            if (!isRunning)
            {
                RegisterListeners();
                WarningState = warning;
            }

            isRunning = true;
        }

        public override void OnDestroy()
        {
            base.OnDestroy();
            UnregisterListeners();
            isRunning = false;
            CrunchDates();
        }

        void RegisterListeners()
        {
            sManager = GetSystemService(SensorService) as SensorManager;
            sManager.RegisterListener(this, sManager.GetDefaultSensor(SensorType.StepCounter), SensorDelay.Ui);
        }


        void UnregisterListeners()
        {
            if (!isRunning)
                return; 
            try
            {
                var sensorManager = (SensorManager)GetSystemService(Context.SensorService);
                sensorManager.UnregisterListener(this);

                isRunning = false;
            }
            catch (Exception ex)
            {

            }
        }

        StepServiceBinder binder;
        public override Android.OS.IBinder OnBind(Android.Content.Intent intent)
        {
            binder = new StepServiceBinder(this);
            return binder;
        }

        public void OnAccuracyChanged(Sensor sensor, SensorStatus accuracy)
        {
            //do nothing here
        }

        public void AddSteps(long count)
        {
            if (lastSteps == 0)
            {
                lastSteps = count;
            }


            newSteps = count - lastSteps;


            if (newSteps < 0)
                newSteps = 1;
            else if (newSteps > 100)
                newSteps = 1;


            lastSteps = count;
            CrunchDates();

            Settings.TotalSteps += newSteps;

            StepsToday = Settings.TotalSteps - Settings.StepsBeforeToday;
        }

        long newSteps = 0;
        long lastSteps = 0;
        public void OnSensorChanged(SensorEvent e)
        {
            if (lastSteps < 0)
                lastSteps = 0;
            var count = (long)e.Values[0];
            WarningState = false;
            AddSteps(count);
        }

        private void CrunchDates(bool startup = false)
        {
            if (!Utils.IsSameDay)
            {

                var yesterday = Settings.CurrentDay;
                var dayEntry = StepEntryManager.GetStepEntry(yesterday);
                if (dayEntry == null || dayEntry.Date.DayOfYear != yesterday.DayOfYear)
                {
                    dayEntry = new StepEntry();
                }

                dayEntry.Date = yesterday;
                dayEntry.Steps = Settings.CurrentDaySteps;

                Settings.CurrentDay = DateTime.Today;
                Settings.CurrentDaySteps = 0;
                Settings.StepsBeforeToday = Settings.TotalSteps;
                StepsToday = 0;
                try
                {
                    StepEntryManager.SaveStepEntry(dayEntry);
                }
                catch (Exception ex)
                {
                    Console.WriteLine("Error {0}", ex.Message);
                }

            }

            else if (startup)
            {
                StepsToday = Settings.TotalSteps - Settings.StepsBeforeToday;
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void OnPropertyChanged(string name)
        {
            if (PropertyChanged == null)
                return;

            PropertyChanged(this, new PropertyChangedEventArgs(name));
        }

    }

MainActivity.cs

[Activity(Label = "My Pedometer", Icon = "@mipmap/icon", Theme = "@style/MainTheme", LaunchMode = LaunchMode.SingleTask, MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        public bool IsBound { get; set; }
        private StepServiceBinder binder;
        private bool registered;
        private Handler handler;
        private bool firstRun = true;
        private StepServiceConnection serviceConnection;
        public StepServiceBinder Binder
        {
            get { return binder; }
            set
            {
                binder = value;
                if (binder == null)
                    return;

                HandlePropertyChanged(null, new System.ComponentModel.PropertyChangedEventArgs("StepsToday"));

                if (registered)
                    binder.StepService.PropertyChanged -= HandlePropertyChanged;

                binder.StepService.PropertyChanged += HandlePropertyChanged;
                registered = true;
            }
        }

        protected override void OnCreate(Bundle savedInstanceState)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(savedInstanceState);
            Xamarin.Forms.Forms.Init(this, savedInstanceState);
            StartStepService();        
            handler = new Handler();
            handler.PostDelayed(() => UpdateUI(), 500);
           LoadApplication(new App());

        }

      private void StartStepService()
        {
            try
            {
                var service = new Intent(this, typeof(StepService));

                if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O)
                {
                    StartForegroundService(service);
                }
                else
                {
                   StartService(service);
                }

            }
            catch (Exception ex)
            {               
                Console.WriteLine("Exception {0}.", ex.Message);
            }
        }

        protected override void OnStop()
        {
            base.OnStop();
            if (IsBound)
            {
                UnbindService(serviceConnection);
                IsBound = false;
            }
        }

        protected override void OnDestroy()
        {
            base.OnDestroy();
            if (IsBound)
            {
                UnbindService(serviceConnection);
                IsBound = false;
            }
        }

        protected override void OnStart()
        {
            base.OnStart();

            if (!firstRun)
                StartStepService();

            if (IsBound)
                return;

            var serviceIntent = new Intent(this, typeof(StepService));
            serviceConnection = new StepServiceConnection(this);
            BindService(serviceIntent, serviceConnection, Bind.AutoCreate);
        }

        protected override void OnPause()
        {
            base.OnPause();
            if (registered && binder != null)
            {
                binder.StepService.PropertyChanged -= HandlePropertyChanged;
                registered = false;
            }
        }

        protected override void OnResume()
        {
            base.OnResume();
            if (!firstRun)
            {
                if (handler == null)
                    handler = new Handler();
                handler.PostDelayed(() => UpdateUI(), 500);
            }

            firstRun = false;

            if (!registered && binder != null)
            {
                binder.StepService.PropertyChanged += HandlePropertyChanged;
                registered = true;
            }
        }

        void HandlePropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (e.PropertyName != "StepsToday")
                return;
            UpdateUI();
        }

        private void UpdateUI()
        {
            RunOnUiThread(() =>
            {
                long steps = 0;
                var showWaring = false;
                if (Binder == null)
                {
                    if (Utils.IsSameDay)
                        steps = Settings.CurrentDaySteps;
                }
                else
                {
                    steps = Binder.StepService.StepsToday;
                    showWaring = binder.StepService.WarningState;
                }

                Settings.CurrentDaySteps = steps;
            });
        }
    }
...