FireBase Notifications in Android Native

Hello,
I made FireBase Notifications working with notifications.v2 plugin. It works as expected as far as I build in the simulator. When I build native with Android Studio, the plugins seems to be activated, however I am getting an error:

 java.lang.NoClassDefFoundError: com.google.firebase.iid.FirebaseInstanceId

at the moment I try to generate FireBase token on the device. 

I followed Native Build Guidelines and I believe that I did everything what was suggested to make the plugin working. Did someone experience the same problem and how did you solve it, please?

It looks like the plugin doesn’t load correctly. Does anybody know where metadata.lua that comes with the plugin should go?

It looks like the problem is here: Logcat when the app starts: 

I/Corona: Platform: GT-I9300 / ARM Neon / 4.3 / Mali-400 MP / OpenGL ES 2.0 / 2017.3184 / English | US | en\_US | en V/Corona: \> Class.forName: shared.google.play.services.base.LuaLoader V/Corona: \> Class.forName: \_CoronaSetup.LuaLoader V/Corona: \> Class.forName: plugin.notifications.v2.LuaLoader V/Corona: \< Class.forName: plugin.notifications.v2.LuaLoader V/Corona: Loading via reflection: plugin.notifications.v2.LuaLoader I/dalvikvm: Could not find method com.google.firebase.iid.FirebaseInstanceId.getInstance, referenced from method plugin.notifications.v2.LuaLoader$GetDeviceToken.invoke W/dalvikvm: VFY: unable to resolve static method 21328: Lcom/google/firebase/iid/FirebaseInstanceId;.getInstance ()Lcom/google/firebase/iid/FirebaseInstanceId;

Going through Google Docs, making some progress, but it makes me to think: Is there a guide how to implement plugin.notifications.v2 in Android native? I clearly missed something somewhere.

Any needs should be in the plugin documentation. I’ll ask engineering about it.  Normally you have to run a download script to download the plugin, then copy the plugin into your project into a specific location and then do any hookups in your Android code (AndroidManifest.xml, etc.)

Rob

@Rob Miracle: Thanks a lot for the response. I believe that I did all described at https://docs.coronalabs.com/plugin/notifications-v2/index.html and https://docs.coronalabs.com/native/android/index.html, though the line 

from fileTree(dir: "$coronaEnterpriseAndroidLibDir/res", exclude: '\*\*/corona\_statusbar\_icon\_default.png')

was not possible and I had to use 

from fileTree(dir: "$coronaNativeAndroidLibDir/res", exclude: '\*\*/corona\_statusbar\_icon\_default.png')
  • but I don’t think this is the problem. 

Hello,

You either exceeded dex limit or have incompatible dependencies. Can you share your build.gradle file located in the “app” folder?

@Bektur: Thank you!

I tried to implement MultiDex. Succeeded with the implementation, still getting this error: 

java.lang.NoClassDefFoundError: com.google.firebase.iid.FirebaseInstanceId

So I guess that the problem is in dependencies. here is the original build.gradle just after plugin.notifications.v2 following Corona Docs:
 

apply plugin: 'com.android.application' // Application name variables def appName = new XmlSlurper().parse(file('src/main/AndroidManifest.xml')).@package.text() def apkName = appName.toString().split('\\.').last() def platform = "mac" if (System.properties['os.name'].toLowerCase().contains('windows')) { platform = "win" } // Paths used throughout the build process def coronaNativeDir = System.getProperty("user.home") + "/Library/Application Support/Corona/Native" if (platform == "win") { coronaNativeDir = System.getenv("CORONA\_ROOT") } // Check to see if Corona Native has been properly setup def coronaNativeDirObj = new File(coronaNativeDir) if( ! coronaNativeDirObj.exists() ) { throw new GradleException("error: Corona Native has not been setup. Run 'Native/SetupCoronaNative.app' in your Corona install to set it up") } def assetsDir = "$projectDir/src/main/assets" def jniLibsDir = "$projectDir/src/main/jniLibs" def coronaNativeSharedDir = "$coronaNativeDir/Corona/shared" def coronaNativeAndroidLibDir = "$coronaNativeDir/Corona/android/lib/Corona" def coronaNativeMacBinDir = "$coronaNativeDir/Corona/" + platform + "/bin" def execPath = System.getenv("PATH") if (platform == "win") { assetsDir = "$projectDir\\src\\main\\assets" jniLibsDir = "$projectDir\\src\\main\\jniLibs" coronaNativeSharedDir = "$coronaNativeDir\\Corona\\shared" coronaNativeAndroidLibDir = "$coronaNativeDir\\Corona\\android\\lib\\Corona" coronaNativeMacBinDir = "$coronaNativeDir\\Corona\\" + platform + "\\bin" execPath = System.getenv("PATH") + System.getProperty("path.separator") + System.getenv("CORONA\_PATH") } android { compileSdkVersion 25 buildToolsVersion '26.0.2' defaultConfig { applicationId "$appName" minSdkVersion 15 targetSdkVersion 25 versionCode 362 versionName "3.62" } applicationVariants.all { variant -\> variant.outputs.all { output -\> outputFileName = "${variant.name}-${variant.versionName}.apk" } } } task cleanAssets(type: Delete, description: 'remove Corona assets and libcorona.so') { delete "$jniLibsDir/armeabi-v7a/libcorona.so" delete "$assetsDir" doFirst { println "== cleanAssets ==" } doLast { new File("$assetsDir").mkdirs() } } task compileLua(type: Exec, description: 'compile Lua source code') { executable = "$coronaNativeMacBinDir/lua" if (platform == "win") { executable = "$coronaNativeMacBinDir\\lua.exe" } workingDir = "$coronaNativeMacBinDir" def packagePath = "package.path='$coronaNativeSharedDir/bin/?.lua;$coronaNativeSharedDir/bin/?/init.lua;'..package.path" // hmm, Lua does not like '\\' packagePath = packagePath.replace("\\","/") def compileLua = "$coronaNativeSharedDir/bin/Compile.lua" if (platform == "win") { compileLua = "$coronaNativeSharedDir\\bin\\Compile.lua" } args = ['-e', packagePath, "$compileLua", platform, "$coronaNativeDir"] def luacpath = "$coronaNativeMacBinDir/?.so" if (platform == "win") { luacpath = "$coronaNativeMacBinDir\\?.dll" } def coronaAssetsDir = "$rootDir/../Corona" if (platform == "win") { coronaAssetsDir = "$rootDir\\..\\Corona" } environment "PATH", "$execPath" environment "LUA\_CPATH", "$luacpath" environment "TARGET\_PLATFORM", 'android' environment "PROJECT\_DIR", "$rootDir" environment "CORONA\_COPY\_PNG\_PRESERVE", '--preserve' environment "CONFIGURATION", 'release' environment "CORONA\_ASSETS\_DIR", "$coronaAssetsDir" environment "CORONA\_TARGET\_RESOURCES\_DIR", "$assetsDir" environment "CORONA\_TARGET\_EXECUTABLE\_DIR", "$assetsDir" dependsOn 'cleanAssets' doFirst { println '== compileLua ==' } } task copyCoronaResources(type: Copy, description: 'include resources from Corona Native') { from fileTree(dir: "$coronaNativeAndroidLibDir/res", include: '\*\*/\*') into "$projectDir/src/main/res" dependsOn 'compileLua' doFirst { println '== copyCoronaResources ==' } } task copyCoronaNativeLibs(type: Copy, description: 'include precompiled libraries from Corona Native') { from fileTree(dir: "$coronaNativeAndroidLibDir/libs", exclude: '\*\*/corona\_statusbar\_icon\_default.png') into "$jniLibsDir" dependsOn 'copyCoronaResources' doFirst { println '== copyCoronaNativeLibs ==' } } task certifyBuild(type: Exec, description: 'certify libcorona.so with resource.car hash and developerkey.cert') { executable = "$coronaNativeMacBinDir/CoronaBuilder.app/Contents/MacOS/CoronaBuilder" if (platform == "win") { executable = "$coronaNativeMacBinDir/CoronaBuilder.exe" } workingDir = "$coronaNativeMacBinDir" environment "PATH", "$execPath" def developerKey = "$coronaNativeSharedDir/resource/developerkey.cert" def resourceCar = "$assetsDir/resource.car" def libcoronaSo = "$jniLibsDir/armeabi-v7a/libcorona.so" if (platform == "win") { developerKey = "$coronaNativeSharedDir\\resource\\developerkey.cert" resourceCar = "$assetsDir\\resource.car" libcoronaSo = "$jniLibsDir\\armeabi-v7a\\libcorona.so" } args = ['app\_sign', 'sign', developerKey, resourceCar, libcoronaSo, 'little', 'android', "$appName"] dependsOn 'copyCoronaNativeLibs' doFirst { println '== certifyBuild ==' } } tasks.preBuild.dependsOn('certifyBuild') dependencies { compile fileTree(dir: 'libs', include: '\*.jar') compile 'com.android.support:appcompat-v7:25.3.1' compile project(':plugin') }

I have 2 questions:

  1. Should Android Studio > Preferences > Plugins FireBase Services be ON?
  2. When I downloaded the plugin, I got plugin.notifications.v2.jar file, that goes to MyApp > android > plugin > libs. I got also metadata.lua. What is that for? Where does it goes? It looks like the part should go to AndroidManifest.xml in the app, the part is already in the build.settings in Corona…

Thank you! 

  1. No, it’s just assistant. You can do setup without it.

  2. Just ignore it. It’s used for Simulator builds afaik.
     
    Regarding build.gradle. You have to have the following lines:
     

    classpath ‘com.google.gms:google-services:3.0.1’

in your top-level build.gradle file and
 

apply plugin: 'com.google.gms.google-services'

 
in the app build.gradle file

I attached zip with my gradle files from sample project with working notifications v2 plugin 

Thank you! I did it and it started to work. 

Actually, the same lines as in your examples work:
classpath ‘com.android.tools.build:gradle:3.0.1’
classpath ‘com.google.gms:google-services:3.1.2’

While classpath ‘com.google.gms:google-services:3.0.1’ generated “not found” errors when syncing the gradle.

So far so good. The app generates the FCM token and doesn’t crash. However when I use this token in the FB console, the push notification is not delivered. It fails. 

So there is something else I am missing. The build with the simulator works fine. Something about google-services.json? It should be in the app directory now, right?

Yes, google-services.json should be in the app directory.

Android Native is tricky, you have to figure out all these dependencies…  Perhaps you should follow Google tutorial and add something like 

com.google.firebase:firebase-messaging:12.0.1

into your gradle dependencies.

Thank you!

To sum it up, In Native Android I implement FCM in Java and plugin.notifications.v2.jar helps just to call FCM from .lua files, right? build.settings is ignored in this case, I guess.

Yes, you got it right.

As said here https://coronalabs.com/blog/2017/10/19/using-plugins-with-native-android-builds/

it’s your responsibility to integrate them into your Android Studio project. Each Android plugin may have additional settings and configuration that you need to complete before you can use it.

It looks like the plugin doesn’t load correctly. Does anybody know where metadata.lua that comes with the plugin should go?

It looks like the problem is here: Logcat when the app starts: 

I/Corona: Platform: GT-I9300 / ARM Neon / 4.3 / Mali-400 MP / OpenGL ES 2.0 / 2017.3184 / English | US | en\_US | en V/Corona: \> Class.forName: shared.google.play.services.base.LuaLoader V/Corona: \> Class.forName: \_CoronaSetup.LuaLoader V/Corona: \> Class.forName: plugin.notifications.v2.LuaLoader V/Corona: \< Class.forName: plugin.notifications.v2.LuaLoader V/Corona: Loading via reflection: plugin.notifications.v2.LuaLoader I/dalvikvm: Could not find method com.google.firebase.iid.FirebaseInstanceId.getInstance, referenced from method plugin.notifications.v2.LuaLoader$GetDeviceToken.invoke W/dalvikvm: VFY: unable to resolve static method 21328: Lcom/google/firebase/iid/FirebaseInstanceId;.getInstance ()Lcom/google/firebase/iid/FirebaseInstanceId;

Going through Google Docs, making some progress, but it makes me to think: Is there a guide how to implement plugin.notifications.v2 in Android native? I clearly missed something somewhere.

Any needs should be in the plugin documentation. I’ll ask engineering about it.  Normally you have to run a download script to download the plugin, then copy the plugin into your project into a specific location and then do any hookups in your Android code (AndroidManifest.xml, etc.)

Rob

@Rob Miracle: Thanks a lot for the response. I believe that I did all described at https://docs.coronalabs.com/plugin/notifications-v2/index.html and https://docs.coronalabs.com/native/android/index.html, though the line 

from fileTree(dir: "$coronaEnterpriseAndroidLibDir/res", exclude: '\*\*/corona\_statusbar\_icon\_default.png')

was not possible and I had to use 

from fileTree(dir: "$coronaNativeAndroidLibDir/res", exclude: '\*\*/corona\_statusbar\_icon\_default.png')
  • but I don’t think this is the problem. 

Hello,

You either exceeded dex limit or have incompatible dependencies. Can you share your build.gradle file located in the “app” folder?

@Bektur: Thank you!

I tried to implement MultiDex. Succeeded with the implementation, still getting this error: 

java.lang.NoClassDefFoundError: com.google.firebase.iid.FirebaseInstanceId

So I guess that the problem is in dependencies. here is the original build.gradle just after plugin.notifications.v2 following Corona Docs:
 

apply plugin: 'com.android.application' // Application name variables def appName = new XmlSlurper().parse(file('src/main/AndroidManifest.xml')).@package.text() def apkName = appName.toString().split('\\.').last() def platform = "mac" if (System.properties['os.name'].toLowerCase().contains('windows')) { platform = "win" } // Paths used throughout the build process def coronaNativeDir = System.getProperty("user.home") + "/Library/Application Support/Corona/Native" if (platform == "win") { coronaNativeDir = System.getenv("CORONA\_ROOT") } // Check to see if Corona Native has been properly setup def coronaNativeDirObj = new File(coronaNativeDir) if( ! coronaNativeDirObj.exists() ) { throw new GradleException("error: Corona Native has not been setup. Run 'Native/SetupCoronaNative.app' in your Corona install to set it up") } def assetsDir = "$projectDir/src/main/assets" def jniLibsDir = "$projectDir/src/main/jniLibs" def coronaNativeSharedDir = "$coronaNativeDir/Corona/shared" def coronaNativeAndroidLibDir = "$coronaNativeDir/Corona/android/lib/Corona" def coronaNativeMacBinDir = "$coronaNativeDir/Corona/" + platform + "/bin" def execPath = System.getenv("PATH") if (platform == "win") { assetsDir = "$projectDir\\src\\main\\assets" jniLibsDir = "$projectDir\\src\\main\\jniLibs" coronaNativeSharedDir = "$coronaNativeDir\\Corona\\shared" coronaNativeAndroidLibDir = "$coronaNativeDir\\Corona\\android\\lib\\Corona" coronaNativeMacBinDir = "$coronaNativeDir\\Corona\\" + platform + "\\bin" execPath = System.getenv("PATH") + System.getProperty("path.separator") + System.getenv("CORONA\_PATH") } android { compileSdkVersion 25 buildToolsVersion '26.0.2' defaultConfig { applicationId "$appName" minSdkVersion 15 targetSdkVersion 25 versionCode 362 versionName "3.62" } applicationVariants.all { variant -\> variant.outputs.all { output -\> outputFileName = "${variant.name}-${variant.versionName}.apk" } } } task cleanAssets(type: Delete, description: 'remove Corona assets and libcorona.so') { delete "$jniLibsDir/armeabi-v7a/libcorona.so" delete "$assetsDir" doFirst { println "== cleanAssets ==" } doLast { new File("$assetsDir").mkdirs() } } task compileLua(type: Exec, description: 'compile Lua source code') { executable = "$coronaNativeMacBinDir/lua" if (platform == "win") { executable = "$coronaNativeMacBinDir\\lua.exe" } workingDir = "$coronaNativeMacBinDir" def packagePath = "package.path='$coronaNativeSharedDir/bin/?.lua;$coronaNativeSharedDir/bin/?/init.lua;'..package.path" // hmm, Lua does not like '\\' packagePath = packagePath.replace("\\","/") def compileLua = "$coronaNativeSharedDir/bin/Compile.lua" if (platform == "win") { compileLua = "$coronaNativeSharedDir\\bin\\Compile.lua" } args = ['-e', packagePath, "$compileLua", platform, "$coronaNativeDir"] def luacpath = "$coronaNativeMacBinDir/?.so" if (platform == "win") { luacpath = "$coronaNativeMacBinDir\\?.dll" } def coronaAssetsDir = "$rootDir/../Corona" if (platform == "win") { coronaAssetsDir = "$rootDir\\..\\Corona" } environment "PATH", "$execPath" environment "LUA\_CPATH", "$luacpath" environment "TARGET\_PLATFORM", 'android' environment "PROJECT\_DIR", "$rootDir" environment "CORONA\_COPY\_PNG\_PRESERVE", '--preserve' environment "CONFIGURATION", 'release' environment "CORONA\_ASSETS\_DIR", "$coronaAssetsDir" environment "CORONA\_TARGET\_RESOURCES\_DIR", "$assetsDir" environment "CORONA\_TARGET\_EXECUTABLE\_DIR", "$assetsDir" dependsOn 'cleanAssets' doFirst { println '== compileLua ==' } } task copyCoronaResources(type: Copy, description: 'include resources from Corona Native') { from fileTree(dir: "$coronaNativeAndroidLibDir/res", include: '\*\*/\*') into "$projectDir/src/main/res" dependsOn 'compileLua' doFirst { println '== copyCoronaResources ==' } } task copyCoronaNativeLibs(type: Copy, description: 'include precompiled libraries from Corona Native') { from fileTree(dir: "$coronaNativeAndroidLibDir/libs", exclude: '\*\*/corona\_statusbar\_icon\_default.png') into "$jniLibsDir" dependsOn 'copyCoronaResources' doFirst { println '== copyCoronaNativeLibs ==' } } task certifyBuild(type: Exec, description: 'certify libcorona.so with resource.car hash and developerkey.cert') { executable = "$coronaNativeMacBinDir/CoronaBuilder.app/Contents/MacOS/CoronaBuilder" if (platform == "win") { executable = "$coronaNativeMacBinDir/CoronaBuilder.exe" } workingDir = "$coronaNativeMacBinDir" environment "PATH", "$execPath" def developerKey = "$coronaNativeSharedDir/resource/developerkey.cert" def resourceCar = "$assetsDir/resource.car" def libcoronaSo = "$jniLibsDir/armeabi-v7a/libcorona.so" if (platform == "win") { developerKey = "$coronaNativeSharedDir\\resource\\developerkey.cert" resourceCar = "$assetsDir\\resource.car" libcoronaSo = "$jniLibsDir\\armeabi-v7a\\libcorona.so" } args = ['app\_sign', 'sign', developerKey, resourceCar, libcoronaSo, 'little', 'android', "$appName"] dependsOn 'copyCoronaNativeLibs' doFirst { println '== certifyBuild ==' } } tasks.preBuild.dependsOn('certifyBuild') dependencies { compile fileTree(dir: 'libs', include: '\*.jar') compile 'com.android.support:appcompat-v7:25.3.1' compile project(':plugin') }

I have 2 questions:

  1. Should Android Studio > Preferences > Plugins FireBase Services be ON?
  2. When I downloaded the plugin, I got plugin.notifications.v2.jar file, that goes to MyApp > android > plugin > libs. I got also metadata.lua. What is that for? Where does it goes? It looks like the part should go to AndroidManifest.xml in the app, the part is already in the build.settings in Corona…

Thank you!