gdelataillade / alarm

A Flutter plugin to easily manage alarms on iOS and Android
https://pub.dev/packages/alarm
MIT License
132 stars 86 forks source link

v3.0.3 bugs - Android #133

Closed aykutuludag closed 10 months ago

aykutuludag commented 10 months ago

Alarm plugin version v3.0.3

Describe the bug Native crash in Android

To Reproduce Nothing. Plugin implemented regularly.

Expected behavior Working without crash.

Screenshots In the comments

aykutuludag commented 10 months ago

Hi again! Before releasing new version of my app with v3.0.3 I notice something on Google Play.

Ekran Resmi 2024-01-11 22 33 41

It basically suggest switch to inExactAlarm which I used in the past in Native Android (java). Literally no difference between inExact and exact in most situations. In this way, you can reduce permissions in the manifest too. Also it is battery friendly method comparing exactAlarm.

To get rid of errors/crash on runtime, I found temporary solution. It asks scheduleExactAlarm permission to user Android >=14. If user won't give permission, app will crash most probably either setting alarms or receiving alarm intent.

  // TEMPORARY
  if (Platform.isAndroid && await Permission.scheduleExactAlarm.status.isGranted == false) {
    await Permission.scheduleExactAlarm.request();
  }
  // TEMPORARY
aykutuludag commented 10 months ago

Some early bug reports dropped to Firebase Crashlytics. This error is about vibration in older android versions.

Fatal Exception: java.lang.NoClassDefFoundError: Failed resolution of: Landroid/os/VibrationEffect;
       at com.gdelataillade.alarm.services.VibrationService.startVibrating(VibrationService.java:1)
       at com.gdelataillade.alarm.alarm.AlarmService.onStartCommand(AlarmService.java)
       at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:3455)
       at android.app.ActivityThread.-wrap21(ActivityThread.java)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1651)
       at android.os.Handler.dispatchMessage(Handler.java:102)
       at android.os.Looper.loop(Looper.java:154)
       at android.app.ActivityThread.main(ActivityThread.java:6351)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:896)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:786)

Bug seems to be appear on Android 5, 6 and 7.

aykutuludag commented 10 months ago

Another bug only appear in Android >= 14.

java.lang.SecurityException: Starting FGS with type mediaPlayback

Fatal Exception: java.lang.RuntimeException: Unable to start service com.gdelataillade.alarm.alarm.AlarmService@efecfc5 with Intent { cmp=app.packagename/com.gdelataillade.alarm.alarm.AlarmService (has extras) }: java.lang.SecurityException: Starting FGS with type mediaPlayback callerApp=ProcessRecord{a25e887 31559:app.packagename/u0a351} targetSDK=34 requires permissions: all of the permissions allOf=true [android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK] 
       at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:5089)
       at android.app.ActivityThread.-$$Nest$mhandleServiceArgs()
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2458)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loopOnce(Looper.java:222)
       at android.os.Looper.loop(Looper.java:314)
       at android.app.ActivityThread.main(ActivityThread.java:8663)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:565)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)

My guess is adding following permission to manifest (addition to android.permission.FOREGROUND_SERVICE) may prevent this error.

<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK" />

aykutuludag commented 10 months ago

Appear in Android 12, 13 and 14.

android.app.ForegroundServiceStartNotAllowedException: Service.startForeground() not allowed due to mAllowStartForeground false

Fatal Exception: java.lang.RuntimeException: Unable to start service com.gdelataillade.alarm.alarm.AlarmService@a4b147 with Intent { cmp=app.packagename/com.gdelataillade.alarm.alarm.AlarmService (has extras) }: android.app.ForegroundServiceStartNotAllowedException: Service.startForeground() not allowed due to mAllowStartForeground false: service app.packagename/com.gdelataillade.alarm.alarm.AlarmService
       at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:5248)
       at android.app.ActivityThread.-$$Nest$mhandleServiceArgs()
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2444)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loopOnce(Looper.java:226)
       at android.os.Looper.loop(Looper.java:313)
       at android.app.ActivityThread.main(ActivityThread.java:8741)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)

This error may be related with previous one.

aykutuludag commented 10 months ago

In some phones, notification is sending I saw it but after a few seconds notification disappear. It is cancelling notification by itself i dont know why. I encountered more than one phones. Happened in Android 14 and Android 9 but possibilitiy to encounter other versions, too.

In the beginning of the notification it is clearly see that it opens turn up of phone's sound (soundbar shown) and sound starting with notification. When notification ends, it is turn down the volume (soundbar shown again) and notification will disappear.

My alarm code is below. This code is calling after Alarm.init. I want to send notification and stayed it there. In iOS, it's working notification not disappear until user clicked or dismissed it. In Android, I guess it's not working all the time or all devices/versions. I'm still investigating but I saw this happen with my own eyes that notification is disappearing after a few seconds.

Future<void> createAlarm(int alarmId, DateTime dateTime, String title, String body) async {
  final alarmSettings = AlarmSettings(
    id: alarmId,
    dateTime: dateTime,
    assetAudioPath: 'assets/notification.mp3',
    loopAudio: false,
    vibrate: true,
    volume: 1,
    notificationTitle: title,
    notificationBody: body,
    enableNotificationOnKill: false,
  );
  await Alarm.set(alarmSettings: alarmSettings);
}
gdelataillade commented 10 months ago

Hi @aykutuludag

Thanks again for this valuable feedback.

aykutuludag commented 10 months ago

Hi @gdelataillade

About your questions:

Extra: Also you are using gradle 7.x.x versions. Most plugins upgraded to gradle 8.x.x versions and transfer packagename variable in AndroidManifest.xml to gradle file. Normally it wont work, you cant run but I have a method to achieve that (https://stackoverflow.com/questions/76108428/how-do-i-fix-namespace-not-specified-error-in-android-studio/77738936#77738936) to handle your package because I'm using gradle 8.1.2. Not biggy, but my recommendation upgrade your gradle versions soon.

Extra2: Some developer may prefer (including me) to not provide assetAudioPath and prefer phone's default notification sound. Not a biggy but in future releases maybe you can include it. Have a nice day.

Best regards.

gdelataillade commented 10 months ago

@aykutuludag Understood. I'll let you know when I'll make a new release. Thank you.

aykutuludag commented 10 months ago

Owner

Thank you for your hard-work. I see that you released v3.0.4 but I desperately need last feature that is notifications should not auto dismissible/disappear. Currently user can't understand or see notifications. So I'm waiting v3.0.5 to release new version of my app and I will create bug file for that version, too.

Have a nice day.

aykutuludag commented 10 months ago

Same issue about notification auto-dismiss: https://github.com/gdelataillade/alarm/issues/121

aykutuludag commented 10 months ago

This issue is about setExactAlarm >>> https://github.com/gdelataillade/alarm/issues/102

aykutuludag commented 10 months ago

This is about my suggestion above named as Extra2 https://github.com/gdelataillade/alarm/issues/84

gdelataillade commented 10 months ago

Hi @aykutuludag

Extra 1: I'm having some trouble when upgrading to Gradle 8.x.x. Someone opened a PR #130 but I was not able to make alarm ring with this configuration. Also, when I increment Gradle version in my build.gradle after updating my JDK, I have build errors.

Extra 2: I added a comment in the issue to answer your request: https://github.com/gdelataillade/alarm/issues/84#issuecomment-1892429045

aykutuludag commented 10 months ago

Hi @aykutuludag

  • Do you experience significant battery drain ? Are you sure about inexactAlarm ? You used it before ? For long durations ?
  • Which one is the extra permission ?
  • I just release version 3.0.5 with the android notification that stays even if loopAudio is false. I did some research and it seems that there is no way to make persistent notifications on iOS.

Extra 1: I'm having some trouble when upgrading to Gradle 8.x.x. Someone opened a PR #130 but I was not able to make alarm ring with this configuration. Also, when I increment Gradle version in my build.gradle after updating my JDK, I have build errors.

Extra 2: I added a comment in the issue to answer your request: #84 (comment)

Extra1: I will check it when I have a free time. But in general, use AGP (https://developer.android.com/build/agp-upgrade-assistant) it fix many problems automatically. Extra2: I read your code especially Android side. You are playing sound in service, showing notification in service etc. However, notification has default sound. I am adding some code in your NotificationService.kt at below:

        notificationBuilder
            .setSmallIcon(iconResId)
            .setContentTitle(title)
            .setContentText(body)
            .setPriority(NotificationCompat.PRIORITY_MAX)
            .setCategory(NotificationCompat.CATEGORY_ALARM)
            .setAutoCancel(false)
            .setOngoing(true)
            .setContentIntent(notificationPendingIntent)
            .setSound(null)
            .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)

If you declare setSound like following (java code), we can use system's default notification sound.

Uri alarmSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
builder.setSound(alarmSound);

There should be some equivalent code in iOS. Of course, some user prefer the sound they provide. If they didn't provide, use notification sound, if provide use that sound. I believe that this logic will work. Also it's a good habit setAutoCancel true at this scenerio because we are wanting close the notification either user clicked or dismissed. I didn't try it but I guess notification never dismissed now. Best regards.

PS: If you use the method above, you also don't need background MediaPlayback and related permissions, too.

PS2: https://github.com/gdelataillade/alarm/issues/54 this issue may be related with setLargeIcon. I'm sharing my java code which I used in Android to createNotification. Maybe that will help because I was able to see notification icon without problem.

    private void generateNotification(Context context, int requestCode) {
        Intent intentLauncher = new Intent(context, MainActivity.class);
        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            pIntent = PendingIntent.getActivity(context, 0, intentLauncher, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
        } else {
            pIntent = PendingIntent.getActivity(context, 0, intentLauncher, PendingIntent.FLAG_UPDATE_CURRENT);
        }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            String channelName = "Daily notification";
            String channelDesc = "It notifies you about new reviews on a daily basis.";
            NotificationChannel mChannel = new NotificationChannel(String.valueOf(0), channelName, NotificationManager.IMPORTANCE_HIGH);
            mChannel.setDescription(channelDesc);
            mChannel.enableVibration(true);
            mChannel.setVibrationPattern(new long[]{500, 500, 500, 500});
            notificationManager.createNotificationChannel(mChannel);

            builder = new NotificationCompat.Builder(context, String.valueOf(0));
        } else {
            builder = new NotificationCompat.Builder(context);
        }
        notificationBuild(context, requestCode);

        //Send notification
        Notification notification = builder.build();
        notificationManager.notify(0, notification);
    }

    private void notificationBuild(Context context, int requestCode) {
        Bitmap bm = BitmapFactory.decodeResource(context.getResources(), R.mipmap.ic_launcher);
        SimpleDateFormat sdf = new SimpleDateFormat("d MMMM yyyy EEEE", Locale.getDefault());
        SimpleDateFormat sdfWeekly = new SimpleDateFormat("d MMMM", Locale.getDefault());
        String notificationContext;

        switch (requestCode) {
            case 100:
            default:
                notificationContext = sdf.format(new Date()) + ": " + context.getString(R.string.dailyNotificationSuffix);
                break;
            case 200:
                notificationContext = sdfWeekly.format(new Date()) + "-" + sdfWeekly.format(getDateWithOffset(7, new Date())) + ": " + context.getString(R.string.weeklyNotificationSuffix);
                break;
        }

        builder.setContentTitle(context.getString(R.string.app_name))
                .setContentText(notificationContext)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setAutoCancel(true)
                .setContentIntent(pIntent)
                .setLargeIcon(bm)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setVibrate(new long[]{500, 500, 500, 500});
    }

PS3: The java code which create alarm daily 10am using InexactRepeating OR Inexact as one bullet alarm.

    public static void createDailyAlarm(AlarmManager alarmManager, PendingIntent pendingIntent) {
        Calendar currentTime = Calendar.getInstance();

        int alarmHour = prefs.getInt("alarmHour", 10);
        int alarmMinute = prefs.getInt("alarmMinute", 0);

        Calendar calendar2 = Calendar.getInstance();
        calendar2.set(Calendar.HOUR_OF_DAY, alarmHour);
        calendar2.set(Calendar.MINUTE, alarmMinute);

        if (currentTime.getTimeInMillis() < calendar2.getTimeInMillis()) {
            alarmManager.setInexactRepeating(AlarmManager.RTC, calendar2.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
        } else {
            alarmManager.setInexactRepeating(AlarmManager.RTC, 1000 * 60 * 60 * 24 + calendar2.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
        }
    }

Final PS: These codes are not new/recent but the points I made above are still valid and solution of bugs are in there.

aykutuludag commented 10 months ago

Now I'm releasing new version with 3.0.5 for Android. I will release new version for iOS when u fix the bugs. I suppose those bugs are still waiting to fix. Have a nice week.

Note: I can verify that there is no crash with your library right now in Android. Bugs are fixed, but feature and suggestions are waiting your interest.

gdelataillade commented 10 months ago

Hey @aykutuludag

Thank you so much for sharing your code and all this valuable feedback. I'll keep you updated. I'm mostly working alone on this project and during my free time, so that's why it can take some time for me to release new features and fixes. Have a nice week too !

aykutuludag commented 10 months ago

Btw, Android app updated with Alarm 3.0.5 today. Only remaining error in android is following. I couldn't understand why it gives error:

Fatal Exception: java.lang.RuntimeException: Unable to start service com.gdelataillade.alarm.alarm.AlarmService@2b3568 with Intent { cmp=com.appname/com.gdelataillade.alarm.alarm.AlarmService (has extras) }: android.app.ForegroundServiceStartNotAllowedException: Service.startForeground() not allowed due to mAllowStartForeground false: service com.appname/com.gdelataillade.alarm.alarm.AlarmService
       at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:5261)
       at android.app.ActivityThread.-$$Nest$mhandleServiceArgs()
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2447)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loopOnce(Looper.java:226)
       at android.os.Looper.loop(Looper.java:313)
       at android.app.ActivityThread.main(ActivityThread.java:8762)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:604)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)
Caused by android.app.ForegroundServiceStartNotAllowedException: Service.startForeground() not allowed due to mAllowStartForeground false: service com.appname/com.gdelataillade.alarm.alarm.AlarmService
       at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:54)
       at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:50)
       at android.os.Parcel.readParcelableInternal(Parcel.java:4787)
       at android.os.Parcel.readParcelable(Parcel.java:4755)
       at android.os.Parcel.createExceptionOrNull(Parcel.java:3018)
       at android.os.Parcel.createException(Parcel.java:3007)
       at android.os.Parcel.readException(Parcel.java:2990)
       at android.os.Parcel.readException(Parcel.java:2932)
       at android.app.IActivityManager$Stub$Proxy.setServiceForeground(IActivityManager.java:6991)
       at android.app.Service.startForeground(Service.java:743)
       at com.gdelataillade.alarm.alarm.AlarmService.onStartCommand(AlarmService.java:152)
       at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:5243)
       at android.app.ActivityThread.-$$Nest$mhandleServiceArgs()
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2447)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loopOnce(Looper.java:226)
       at android.os.Looper.loop(Looper.java:313)
       at android.app.ActivityThread.main(ActivityThread.java:8762)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:604)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)

image

I couldn't see any error beside this. Thank you for your effort, have a nice day.

Note: I guess this error is about https://developer.android.com/develop/background-work/services/foreground-services. You added this permission to Manifest in previous versions, maybe it requires runtime request permission.

aykutuludag commented 10 months ago

Btw, Android app updated with Alarm 3.0.5 today. Only remaining error in android is following. I couldn't understand why it gives error:

Fatal Exception: java.lang.RuntimeException: Unable to start service com.gdelataillade.alarm.alarm.AlarmService@2b3568 with Intent { cmp=com.appname/com.gdelataillade.alarm.alarm.AlarmService (has extras) }: android.app.ForegroundServiceStartNotAllowedException: Service.startForeground() not allowed due to mAllowStartForeground false: service com.appname/com.gdelataillade.alarm.alarm.AlarmService
       at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:5261)
       at android.app.ActivityThread.-$$Nest$mhandleServiceArgs()
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2447)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loopOnce(Looper.java:226)
       at android.os.Looper.loop(Looper.java:313)
       at android.app.ActivityThread.main(ActivityThread.java:8762)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:604)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)
Caused by android.app.ForegroundServiceStartNotAllowedException: Service.startForeground() not allowed due to mAllowStartForeground false: service com.appname/com.gdelataillade.alarm.alarm.AlarmService
       at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:54)
       at android.app.ForegroundServiceStartNotAllowedException$1.createFromParcel(ForegroundServiceStartNotAllowedException.java:50)
       at android.os.Parcel.readParcelableInternal(Parcel.java:4787)
       at android.os.Parcel.readParcelable(Parcel.java:4755)
       at android.os.Parcel.createExceptionOrNull(Parcel.java:3018)
       at android.os.Parcel.createException(Parcel.java:3007)
       at android.os.Parcel.readException(Parcel.java:2990)
       at android.os.Parcel.readException(Parcel.java:2932)
       at android.app.IActivityManager$Stub$Proxy.setServiceForeground(IActivityManager.java:6991)
       at android.app.Service.startForeground(Service.java:743)
       at com.gdelataillade.alarm.alarm.AlarmService.onStartCommand(AlarmService.java:152)
       at android.app.ActivityThread.handleServiceArgs(ActivityThread.java:5243)
       at android.app.ActivityThread.-$$Nest$mhandleServiceArgs()
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2447)
       at android.os.Handler.dispatchMessage(Handler.java:106)
       at android.os.Looper.loopOnce(Looper.java:226)
       at android.os.Looper.loop(Looper.java:313)
       at android.app.ActivityThread.main(ActivityThread.java:8762)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:604)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)

image

I couldn't see any error beside this. Thank you for your effort, have a nice day.

Note: I guess this error is about https://developer.android.com/develop/background-work/services/foreground-services. You added this permission to Manifest in previous versions, maybe it requires runtime request permission.

This error occurring on Android 12, 13 and 14. I suppose this crash is about this: https://stackoverflow.com/a/70666991/4606368

aykutuludag commented 10 months ago

I'm checking everything and releasing new version with 3.0.5 again. I updated flutter and dart sdk, too. I'm closing this issue now, if anything happens on new version, I will create new bug file for Android. For ios current bugs are still valid, no need to open new one. Kind regards.