edsnider / localnotificationsplugin

Local Notifications Plugin for Xamarin and Windows
MIT License
198 stars 82 forks source link

Android Added JobScheduler for Android Lollipop and newer #42

Open DoktorAerzt opened 6 years ago

DoktorAerzt commented 6 years ago

In this PR i added support for JobScheduler. The point is, we don´t need an Service witch hold the AlarmManager "online" and we don´t need to safe the notification for reboot. Another point against the use of Service is that at android 8.0 they are very limitted.

Fixes issue #7 when using Lollipop or newer .

Changes proposed in this pull request:

Nice wishes from germany

DoktorAerzt commented 6 years ago

I Forgot a commit, thats why i closed and reopen the PR

agharium commented 6 years ago

Hi.. I really need this. How do I install your forked project?.. I tried to compile and replace my files with yours, but it gave me a reference error..

DoktorAerzt commented 6 years ago

You can just download the nuget file from the temporary build. https://ci.appveyor.com/project/edsnider/localnotificationsplugin/build/3.0.1-pr42-build24/artifacts

Then you safe it on your PC and add the folder as nuget repository. Then you can update it with nuget, you just need to check "pre Version " or something like that. And you need to choice your newly created repo

I hope me little write down will help you

agharium commented 6 years ago

Thank you so much!

It throws this error when compiling to Release in Android with linking properties set to SDK Assemblies:

Falha inesperada da tarefa "LinkAssemblies".
Mono.Linker.MarkException: Error processing method: 'System.Void Plugin.LocalNotifications.LocalNotificationsImplementation::Show(System.String,System.String,System.Int32)' in assembly: 'Plugin.LocalNotifications.dll' ---> Mono.Cecil.ResolutionException: Failed to resolve Android.App.NotificationChannel
   em Mono.Linker.Steps.MarkStep.HandleUnresolvedType(TypeReference reference)
   em Mono.Linker.Steps.MarkStep.MarkType(TypeReference reference)
   em MonoDroid.Tuner.MonoDroidMarkStep.MarkType(TypeReference reference)
   em Mono.Linker.Steps.MarkStep.MarkMethodBody(MethodBody body)
   em Mono.Linker.Steps.MarkStep.ProcessMethod(MethodDefinition method)
   em Mono.Linker.Steps.MarkStep.ProcessQueue()
   --- Fim do rastreamento de pilha de exceções internas ---
   em Mono.Linker.Steps.MarkStep.ProcessQueue()
   em Mono.Linker.Steps.MarkStep.ProcessPrimaryQueue()
   em Mono.Linker.Steps.MarkStep.Process()
   em Mono.Linker.Steps.MarkStep.Process(LinkContext context)
   em Mono.Linker.Pipeline.Process(LinkContext context)
   em MonoDroid.Tuner.Linker.Process(LinkerOptions options, ILogger logger, LinkContext& context)
   em Xamarin.Android.Tasks.LinkAssemblies.Execute(DirectoryAssemblyResolver res)
   em Xamarin.Android.Tasks.LinkAssemblies.Execute()
   em Microsoft.Build.BackEnd.TaskExecutionHost.Microsoft.Build.BackEnd.ITaskExecutionHost.Execute()
   em Microsoft.Build.BackEnd.TaskBuilder.<ExecuteInstantiatedTask>d__26.MoveNext() ProMama.Android         

With the linking set to "None", the error is not thrown and it compiles and runs just fine.

I'll be testing this version itself in the next couple of hours (closing the app and rebooting the phone, etc.) and I'll come back with some feedback.

Also, you think you may have a fix or workaround for the link thing? Without the Assembly link, my apk size goes from 39MB to 66MB.

I don't think it has anything to do with your changes to the code, probably it's due to version 3.0' changes (I was using version 2.1 until now), since the error has something to do with Android.App.NotificationChannel.

DoktorAerzt commented 6 years ago

For with SDK Version are you building and using?

agharium commented 6 years ago

7.1 (so I could use version 2.1, otherwise it wouldn't work)

Maybe it's because of that.. good point

DoktorAerzt commented 6 years ago

Could you try to build it with Android 8.1 ?

agharium commented 6 years ago

Yeah, just built it on 8.1 and it went just fine. Tested it with instant notifications and it's working alright. Now I'm testing with the app closed and rebooting.

agharium commented 6 years ago

It's working good. But why the icon I set is not being used?

screenshot-1531681250693

If the notification is fired while the app is open, it works out ok:

screenshot-1531681636546

Do you have any idea of how I can fix this?

DoktorAerzt commented 6 years ago

First things first, i was not able to get the Notification running when the app is closed and you had started the app from Visual Studio. But when you start the app in the Emulator from icon then all should work.

For the icon i need to make a new commit, i will work this evening on it.

agharium commented 6 years ago

First things first, i was not able to get the Notification running when the app is closed and you had started the app from Visual Studio. But when you start the app in the Emulator from icon then all should work.

Yeah. I got it working by archiving the android project and then I got the .apk and installed it on my phone. It worked fine. But it still didn't work when I rebooted the phone.

For the icon i need to make a new commit, i will work this evening on it.

Thank you very much!

DoktorAerzt commented 6 years ago

Im testing the reboot. Do you have first marked "RECEIVE_BOOT_COMPLETED" in manifest and ask for that permission?

Could you check in your app what

Application.Context.CheckSelfPermission("RECEIVE_BOOT_COMPLETED") 

will give you?

agharium commented 6 years ago

07-17 09:53:52.715 D/MyApp (30372): Denied

It's denied.. How can I ask for that permission on runtime? I've searched a lot and this was what I came up with:

async Task GetReceivedBootCompletePermission()
{
    string[] BootPermission = { Manifest.Permission.ReceiveBootCompleted };

    if (CheckSelfPermission(BootPermission[0]) != (int)Permission.Granted && (int)Build.VERSION.SdkInt >= 23)
    {
        AlertDialog.Builder alert = new AlertDialog.Builder(this);
        alert.SetTitle("Permissão de inicialização");
        alert.SetMessage("O aplicativo precisa da permissão de inicialização para enviar notificações corretamente.");
        Dialog dialog = alert.Create();
        dialog.Show();

        RequestPermissions(BootPermission, 0);
    }
}

I wrote that code on MainActivity.cs, turned my OnCreatemethod into an async method and invoked it with await GetReceivedBootCompletePermission();, but nothing happens. Any idea of how I can do this?

DoktorAerzt commented 6 years ago

This is one think i also need to get to work. And at the moment i have no fix for it.

agharium commented 6 years ago

No problem, man. You already helped a lot with fixing the issue where the notifications wouldn't fire if the app was closed, and that's a lot for me. Thank you!

DoktorAerzt commented 6 years ago

Thank you but the fix for the permission error should be a easy one.

agharium commented 6 years ago

I see. If you achieve it, let me know, please :)

agharium commented 6 years ago

Hey, I'm getting some unexpected behavior.

When I try to install on any Android below 8.0, this error shows up:

3>Unexpected install output: Failure [INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: Failed parse during installPackageLI: /data/app/vmdl1165202956.tmp/base.apk (at Binary XML file line #28): <service> does not have valid android:name]
3>
3>   em Mono.AndroidTools.Internal.AdbOutputParsing.CheckInstallSuccess(String output, String packageName)
3>   em Mono.AndroidTools.AndroidDevice.<>c__DisplayClass94_0.<InstallPackage>b__0(Task`1 t)
3>   em System.Threading.Tasks.ContinuationTaskFromResultTask`1.InnerInvoke()
3>   em System.Threading.Tasks.Task.Execute()
3>
3>A implantação falhou em Motorola Moto G (5) Plus

Now, the error says "[...] (at Binary XML file line #28): \<service> does not have valid android:name [...]". In line #28 on my project's generated AndroidManifest.xml (found on "ANDROID_PROJECT_FOLDER\obj\Debug\android"), this shows up:

<service android:name="Plugin.LocalNotifications.ScheduledJobHandler" android:permission="android.permission.BIND_JOB_SERVICE" />

The pattern in all other service's android:name's found in my generated AndroidManifest is some kind of md5 string like this:

<service android:name="md5dcb6eccdc824e0677ffae8ccdde42930.KeepAliveService" />

How can I fix this? This is somewhat of an urgent matter for me. I have a ten days' deadline to finish this project.

agharium commented 6 years ago

Hey, checking the changes you made, I noticed you set the service like this:

[Service(Name = "Plugin.LocalNotifications.ScheduledJobHandler", Permission = "android.permission.BIND_JOB_SERVICE")]

from here

But I looked up for the "KeepAliveService" code (it's the other service I have in my generated AndroidManifest and it has the md5 string before it's name) and it's written simply like this:

[Service]

from here

Maybe, if you set it like this, it will work fine:

[Service(Permission = "android.permission.BIND_JOB_SERVICE")]

It's just an idea. Maybe it can help, maybe not.

Thanks.

DoktorAerzt commented 6 years ago

The point is, at my phone (running android 7) i have no problem with the installation.

Could you send me your complete android manifest? Maybe it will help.

And where do you get the error from? You builded the app and copyed to you android 7 device and tryed to install it there?

DoktorAerzt commented 6 years ago

And the Name shouldn´t be the problem.

Look https://docs.microsoft.com/en-us/xamarin/android/app-fundamentals/services/creating-a-service/

And please also mention what build optimisation tools you use. Maybe the service get cut out too safe some space.

agharium commented 6 years ago

You builded the app and copyed to you android 7 device and tryed to install it there?

If I try to install with .apk, it gives an error saying that "there was a problem when analyzing the package".

And where do you get the error from?

If I try to install with Visual Studio (on Release or Debug mode, error happens both ways), it throws out the error I already sent you:

3>Unexpected install output: Failure [INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: Failed parse during installPackageLI: /data/app/vmdl1165202956.tmp/base.apk (at Binary XML file line #28): <service> does not have valid android:name]
3>
3>   em Mono.AndroidTools.Internal.AdbOutputParsing.CheckInstallSuccess(String output, String packageName)
3>   em Mono.AndroidTools.AndroidDevice.<>c__DisplayClass94_0.<InstallPackage>b__0(Task`1 t)
3>   em System.Threading.Tasks.ContinuationTaskFromResultTask`1.InnerInvoke()
3>   em System.Threading.Tasks.Task.Execute()
3>
3>A implantação falhou em Motorola Moto G (5) Plus

This is my AndroidManifest before compiling:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="br.gov.rs.osorio.promama" android:installLocation="auto" android:versionCode="1" android:versionName="1.0">
    <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="27" />
  <uses-permission android:name="android.permission.CAMERA" />
  <!--<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />-->
    <application android:label="Pró-Mamá" android:icon="@drawable/icon">
        <provider android:name="android.support.v4.content.FileProvider" android:authorities="br.gov.rs.osorio.promama.fileprovider" android:exported="false" android:grantUriPermissions="true">
            <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"></meta-data>
        </provider>
    </application>
</manifest>

And this is after compilation:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="br.gov.rs.osorio.promama" android:installLocation="auto" android:versionCode="1" android:versionName="1.0">
  <uses-sdk android:minSdkVersion="19" android:targetSdkVersion="27" />
  <uses-permission android:name="android.permission.CAMERA" />
  <!--<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />-->
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
  <uses-feature android:name="android.hardware.camera" android:required="false" />
  <uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
  <application android:label="Pró-Mamá" android:icon="@drawable/icon" android:name="md53e3df0725c458c2699e003c3433b7eb1.MainApplication" android:allowBackup="true" android:debuggable="true">
    <provider android:name="android.support.v4.content.FileProvider" android:authorities="br.gov.rs.osorio.promama.fileprovider" android:exported="false" android:grantUriPermissions="true">
      <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"></meta-data>
    </provider>
    <activity android:configChanges="orientation|screenSize" android:icon="@drawable/icon" android:label="Pró-Mamá" android:theme="@style/MainTheme" android:name="md53e3df0725c458c2699e003c3433b7eb1.MainActivity" />
    <activity android:label="Pró-Mamá" android:noHistory="true" android:theme="@style/MainTheme.Splash" android:name="md53e3df0725c458c2699e003c3433b7eb1.SplashActivity">
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
    <receiver android:enabled="true" android:exported="false" android:label="Connectivity Plugin Broadcast Receiver" android:name="md59628c2715c1bb8febcc7ae8402df0582.ConnectivityChangeBroadcastReceiver" />
    <receiver android:enabled="true" android:label="Local Notifications Plugin Broadcast Receiver" android:name="md51251ca0436b286678a1cd2c1ca81248e.ScheduledAlarmHandler" />
    <service android:name="Plugin.LocalNotifications.ScheduledJobHandler" android:permission="android.permission.BIND_JOB_SERVICE" />
    <activity android:configChanges="orientation|screenSize" android:name="md54cd65ac53ae710bff80022afc423e0f3.MediaPickerActivity" />
    <service android:name="md5dcb6eccdc824e0677ffae8ccdde42930.KeepAliveService" />
    <receiver android:enabled="true" android:exported="false" android:name="md51558244f76c53b6aeda52c8a337f2c37.PowerSaveModeBroadcastReceiver" />
    <provider android:name="mono.MonoRuntimeProvider" android:exported="false" android:initOrder="2147483647" android:authorities="br.gov.rs.osorio.promama.mono.MonoRuntimeProvider.__mono_init__" />
    <!--suppress ExportedReceiver-->
    <receiver android:name="mono.android.Seppuku">
      <intent-filter>
        <action android:name="mono.android.intent.action.SEPPUKU" />
        <category android:name="mono.android.intent.category.SEPPUKU.br.gov.rs.osorio.promama" />
      </intent-filter>
    </receiver>
  </application>
  <uses-permission android:name="com.sec.android.provider.badge.permission.READ" />
  <uses-permission android:name="com.sec.android.provider.badge.permission.WRITE" />
  <uses-permission android:name="com.htc.launcher.permission.READ_SETTINGS" />
  <uses-permission android:name="com.htc.launcher.permission.UPDATE_SHORTCUT" />
  <uses-permission android:name="com.sonyericsson.home.permission.BROADCAST_BADGE" />
  <uses-permission android:name="com.sonymobile.home.permission.PROVIDER_INSERT_BADGE" />
  <uses-permission android:name="com.anddoes.launcher.permission.UPDATE_COUNT" />
  <uses-permission android:name="com.majeur.launcher.permission.UPDATE_BADGE" />
  <uses-permission android:name="com.huawei.android.launcher.permission.CHANGE_BADGE" />
  <uses-permission android:name="com.huawei.android.launcher.permission.READ_SETTINGS" />
  <uses-permission android:name="com.huawei.android.launcher.permission.WRITE_SETTINGS" />
  <uses-permission android:name="android.permission.READ_APP_BADGE" />
  <uses-permission android:name="com.oppo.launcher.permission.READ_SETTINGS" />
  <uses-permission android:name="com.oppo.launcher.permission.WRITE_SETTINGS" />
  <uses-permission android:name="me.everything.badger.permission.BADGE_COUNT_READ" />
  <uses-permission android:name="me.everything.badger.permission.BADGE_COUNT_WRITE" />
</manifest>

I tried to install it on Android 6.0 and 7.0 with no success. But, on Android 8.0 and 8.1 it works just fine.

agharium commented 6 years ago

And please also mention what build optimisation tools you use. Maybe the service get cut out too safe some space.

I only saw that message now..

I use only the "Linking" that comes with Xamarin. Currently is set on "Only SDK Assemblies", but I set to "None" and it didn't work as well.

And the Name shouldn´t be the problem.

Yeah, that was a long shot haha

agharium commented 6 years ago

Hey.

So I've been running tests on different Android versions (4.4 up to 8.1 - it's the range that I need my app to work on). My conclusions are:

Android 8.1 (API 27) - works Android 8.0 (API 26) - works Android 7.1 (API 25) - works Android 7.0 (API 24) - does not work Android 6.0 (API 23) - does not work Android 5.1 (API 22) - does not work Android 5.0 (API 21) - does not work Android 4.4 (API 19) - does not work (which is actually ok... JobScheduler was only added in API 21)

I can only assume that something about the manifest changed on 7.1 (API 25).

Can you verify if your phone is running 7.1 or 7.0?

Also, I tried on Android 6.0 without the plugin installed in my project and it deployed just fine, so...

agharium commented 6 years ago

Check this out: https://stackoverflow.com/questions/49435390/adding-service-causes-deployment-failed

DoktorAerzt commented 6 years ago

So my app is running on different versions. see:

grafik

I will look into your link. But im away for 2 weeks. I will look what i can do. Maybe you need to made the changes for your self.

DoktorAerzt commented 6 years ago

Is it working now? See here for the build

DoktorAerzt commented 6 years ago

Interresting is, you don´t need to ask for the Permission because it´s a normal one.

agharium commented 6 years ago

Maybe you need to made the changes for your self.

I tried to download the project, make the changes, compile and copy and paste the output files into my package folder (replacing the originals), but it didn't work. I have no idea on how to start editing those files hehehhe.

Anyway...

Is it working now? See here for the build

Yes! It's working now! Thank you so much, man.

DoktorAerzt commented 6 years ago

@edsnider The PR is ready to merk, what do you think about it?

agharium commented 6 years ago

@edsnider The PR is ready to merk, what do you think about it?

@edsnider I can vouch for this. I know that if you plan on merging this PR you'll test it extensively, but if my word has any value, I have been using testing and this fork to it's latest update and it's working perfectly.. My app is currently published on Google Play and all :)

@DoktorAerzt Thanks for this PR, man. May God bless you.

edsnider commented 6 years ago

@agharium @DoktorAerzt thank you! I will look through the PR and try and get it merged this week.

AxelUser commented 6 years ago

Hi! @edsnider and @DoktorAerzt, thank you for amaizing time-saving nuget! Do you have any information about when this PR will be merged?

DoktorAerzt commented 5 years ago

Hi @edsnider are there any new news when this PR will get merged? Or do i need to add something?

EmilAlipiev commented 5 years ago

was that merged in any version like Pre release etc?

edsnider commented 5 years ago

@EmilAlipiev for now, you can use CI build package: https://ci.appveyor.com/project/edsnider/localnotificationsplugin/build/3.0.1-pr42-build30/artifacts

qkfang2 commented 5 years ago

I noticed some errors from app centre using the CI build package from Android 5.0 - 5.1.1. Might need to add another check in CheckBootPermission func for version.

The method Context#checkSelfPermission(String) was added to the API 23. Also below API 23 it's pointless as the permission is always granted. Make a check for the API version before handling the permissions https://stackoverflow.com/questions/33407250/checkselfpermission-method-is-not-working-in-targetsdkversion-22

LocalNotificationsImplementation.CheckBootPermission ()
Java.Lang.LinkageError: no non-static method "Landroid/content/ContextWrapper;.checkSelfPermission(Ljava/lang/String;)I"
Java.Interop
JniEnvironment+InstanceMethods.GetMethodID (Java.Interop.JniObjectReference type, System.String name, System.String signature)
Java.Interop
JniType.GetInstanceMethod (System.String name, System.String signature)
Java.Interop
JniPeerMembers+JniInstanceMethods.GetMethodInfo (System.String encodedMember)
Java.Interop
JniPeerMembers+JniInstanceMethods.InvokeVirtualInt32Method (System.String encodedMember, Java.Interop.IJavaPeerable self, Java.Interop.JniArgumentValue* parameters)
Android.Content
ContextWrapper.CheckSelfPermission (System.String permission)
Plugin.LocalNotifications
LocalNotificationsImplementation.CheckBootPermission ()
Plugin.LocalNotifications
LocalNotificationsImplementation.Show (System.String title, System.String body, System.Int32 id, System.DateTime notifyTime)
giftcardbal.PageView
GiftCardListPage.Notification (System.String cardList)
java.lang.NoSuchMethodError: no non-static method "Landroid/content/ContextWrapper;.checkSelfPermission(Ljava/lang/String;)I"
mono.java.lang.RunnableImplementor.n_run(Native Method)
mono.java.lang.RunnableImplementor.run(RunnableImplementor.java:30)
android.os.Handler.handleCallback(Handler.java:739)
android.os.Handler.dispatchMessage(Handler.java:95)
android.os.Looper.loop(Looper.java:135)
android.app.ActivityThread.main(ActivityThread.java:5343)
java.lang.reflect.Method.invoke(Native Method)
java.lang.reflect.Method.invoke(Method.java:372)
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:905)
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:700)
DoktorAerzt commented 5 years ago

@wodaidai i will look into it in a few days.

DoktorAerzt commented 5 years ago

@wodaidai sorry for the long time you needed to wait but now the fix is included.

@edsnider are there any news when this pr get merged?

pfedotovsky commented 5 years ago

@edsnider any updates?

edsnider commented 5 years ago

@DoktorAerzt could you pull in the last two commits on master?

DoktorAerzt commented 5 years ago

@edsnider i made the changes, is there anything else i need to change?

edsnider commented 5 years ago

@DoktorAerzt awesome, thank you! The .sln still has changes that shouldn't need to be committed.

DoktorAerzt commented 5 years ago

@edsnider i hope it´s better now.

DoktorAerzt commented 5 years ago

@edsnider is there something more i missed?

edsnider commented 5 years ago

@DoktorAerzt Nope, I think that's it... thank you! Could you enable maintainer edits on this pull request? I'd like to make a few minor code cleanup adjustments before I merge it into master.

DoktorAerzt commented 5 years ago

@edsnider all right. Maintainer Edits should be enabled.

JaoHundred commented 4 years ago

Any news on this?