I'm using firebase_messaging in my flutter application.
To handle background messages with firebase messaging in pub they suggested to create new Application.java file and replace java file name in AndroidManifest file.
In my application i'm using kotlin and i already implemented some native code in MainActivity.kt
So how to write this code in kotlin.
package io.flutter.plugins.firebasemessagingexample;
import io.flutter.app.FlutterApplication;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback;
import io.flutter.plugins.GeneratedPluginRegistrant;
import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService;
public class Application extends FlutterApplication implements PluginRegistrantCallback {
#Override
public void onCreate() {
super.onCreate();
FlutterFirebaseMessagingService.setPluginRegistrant(this);
}
#Override
public void registerWith(PluginRegistry registry) {
GeneratedPluginRegistrant.registerWith(registry);
}
}
it is mandatory to replace MainActivity to Application in AndroidManifest file?
Here is the working background notification kotlin code:
package com.example.yourapp
import io.flutter.app.FlutterApplication
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.GeneratedPluginRegistrant
import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService
class Application : FlutterApplication(), PluginRegistrantCallback {
override fun onCreate() {
super.onCreate()
FlutterFirebaseMessagingService.setPluginRegistrant(this);
}
override fun registerWith(registry: PluginRegistry?) {
io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin.registerWith(registry?.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));
}
}
Here is the Kotlin code for the new firebase cloud messaging version:
package id.your.app
import io.flutter.app.FlutterApplication
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.firebase.messaging.FlutterFirebaseMessagingBackgroundService
// import io.flutter.plugins.firebase.messaging.FlutterFirebaseMessagingPlugin
class Application : FlutterApplication(), PluginRegistrantCallback {
override fun onCreate() {
super.onCreate()
FlutterFirebaseMessagingBackgroundService.setPluginRegistrant(this)
}
override fun registerWith(registry: PluginRegistry?) {
// FlutterFirebaseMessagingPlugin.registerWith(registry?.registrarFor("io.flutter.plugins.firebase.messaging.FlutterFirebaseMessagingPlugin"))
}
}
Related
I'm learning android development from Android_developer. while using CoroutinesWorker, I encountered a problem whilst working with Kotlin
Logcat
com.google.samples.apps.devbyteviewer E/WM-WorkerFactory: Could
not instantiate
com.google.samples.apps.devbyteviewer.work.RefreshDataWorker
java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
at
androidx.work.WorkerFactory.createWorkerWithDefaultFallback(Worker
Factory.java:96)
at
androidx.work.impl.WorkerWrapper.runWorker(WorkerWrapper.java:
244)
at androidx.work.impl.WorkerWrapper.run(WorkerWrapper.java:136)
The Application class from where I instantiate workmanager.
Application class
package com.google.samples.apps.devbyteviewer
import android.app.Application
import androidx.work.*
import
com.google.samples.apps.devbyteviewer.work.RefreshDataWorker
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import timber.log.Timber
import java.util.concurrent.TimeUnit
As I have read in related posts, my worker class is a top level class and in an independent file.
Worker class
package com.google.samples.apps.devbyteviewer.work
import android.content.Context
import androidx.work.CoroutineWorker
import androidx.work.WorkerParameters
import com.google.samples.apps.devbyteviewer.database.getDatabase
import com.google.samples.apps.devbyteviewer.repository.VideosRepository
import retrofit2.HttpException
import timber.log.Timber
class RefreshDataWorker(appContext: Context, params: WorkerParameters) :
CoroutineWorker(appContext, params) {
override suspend fun doWork(): Result {
val database = getDatabase(applicationContext)
val repository = VideosRepository(database)
try {
repository.refreshVideos()
Timber.d("Work request for sync is run")
} catch (e: HttpException) {
return Result.retry()
}
return Result.success()
}
companion object {
/**
* Define a work name to uniquely identify this worker.
*/
const val WORK_NAME = "com.example.android.devbyteviewer.work.RefreshDataWorker"
}
}
I've had the same problem while working with WorkManager.
Pulling meaning from your post and the error message, this is very likely an import related issue. Check your Application dependencies.
Rather than:
implementation "android.arch.work:work-runtime-ktx:$work_version"
Use:
implementation "androidx.work:work-runtime-ktx:$work_version"
I am trying to use the android_alarm_manager_plus package to schedule background tasks in flutter. In their documentation, they have added the Application class code for JAVA, which is as follows -
public class Application extends FlutterApplication implements PluginRegistrantCallback {
#Override
public void onCreate() {
super.onCreate();
AlarmService.setPluginRegistrant(this);
}
#Override
public void registerWith(PluginRegistry registry) {
GeneratedPluginRegistrant.registerWith(registry);
}
}
But I'm working on a kotlin based project (without any knowledge of kotlin, I may add) and there is no documentation for this. I tried writing the kotlin counterpart to this code myself, but I'm still facing errors.
My Application.kt file -
package com.example.my_app;
import io.flutter.app.FlutterApplication;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback;
import io.flutter.plugins.GeneratedPluginRegistrant;
import io.flutter.plugins.AndroidAlarmManager.AlarmService;
class Application : FlutterApplication(), PluginRegistry.PluginRegistrantCallback {
override fun onCreate() {
super.onCreate()
AlarmService.setPluginRegistrant(this);
}
override fun registerWith(registry: PluginRegistry?) {
registry?.registrarFor("GeneratedPluginRegistrant");
}
}
The errors -
Application.kt: (7, 27): Unresolved reference: AndroidAlarmManager
Application.kt: (15, 9): Unresolved reference: AlarmService
You do not need to add this Java/Kotlin code. That's for the old Android embedding. Note the "DEPRECATED" here in the title: https://github.com/fluttercommunity/plus_plugins/tree/main/packages/android_alarm_manager_plus#flutter-android-embedding-v1-deprecated
I've been using following Kotlin extension to add my lifecycle observers:
fun Lifecycle.addObserverUntilDestroy(observer: LifecycleObserver) {
addObserver(observer)
addObserver(object : LifecycleObserver {
#OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy() {
removeObserver(this)
removeObserver(observer)
}
})
}
The idea was to automatically remove the observer when the Lifecycle is being destroyed.
So I can call it in Activity's onPostCreate (in this case a Java code but it is the same when calling from Kotlin):
ArchitectureComponentExtensions.addObserverUntilDestroy(getLifecycle(), myViewModel);
And have this code in my ViewModel (a custom one, not the on from Architecture Components):
#OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
public void onViewDestroyed() {
// This is not being called anymore
}
But this stopped working after migrating from:
import android.arch.lifecycle.Lifecycle
import android.arch.lifecycle.LifecycleObserver
import android.arch.lifecycle.LifecycleOwner
import android.arch.lifecycle.OnLifecycleEvent
to:
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
Any ideas why this is not working anymore?
I'm trying to extend the FCM service, and broadcast to the MainActivity upon onNewToken() being called. onNewToken() is indeed called, but the onReceive() method is not. As far as I know, I do not need to define anything else in the manifest, since this is a local broadcast. These are my classes:
MainActivity.kt
package com.reali.app.mymessagingapp
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.v4.content.LocalBroadcastManager
import android.widget.TextView
import com.google.android.gms.tasks.OnCompleteListener
import com.reali.app.mymessagingapp.MyFirebaseMessagingService.Companion.TOKEN_REFRESHED_EVENT
import com.google.firebase.iid.FirebaseInstanceId
import com.google.firebase.iid.InstanceIdResult
class MainActivity : AppCompatActivity() {
private lateinit var broadcastReceiver: BroadcastReceiver
private lateinit var tvTitle: TextView
private lateinit var tvToken: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tvTitle = findViewById(R.id.tvTitle)
tvToken = findViewById(R.id.tvToken)
broadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
tvTitle.text = resources.getString(R.string.token_refreshed)
refreshTokenText()
}
}
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, IntentFilter(TOKEN_REFRESHED_EVENT))
refreshTokenText()
}
private fun refreshTokenText() {
FirebaseInstanceId.getInstance().instanceId.addOnCompleteListener(OnCompleteListener<InstanceIdResult> { task ->
if (!task.isSuccessful) {
return#OnCompleteListener
}
val token = task.result?.token
tvToken.text = token
})
}
}
MyFirebaseMessagingService.kt
package com.reali.app.mymessagingapp
import android.content.Intent
import android.util.Log
import com.google.firebase.messaging.FirebaseMessagingService
class MyFirebaseMessagingService : FirebaseMessagingService()
{
companion object {
const val TOKEN_REFRESHED_EVENT = "token_refreshed_event"
}
override fun onNewToken(token: String?) {
Log.d("MyFcmMessagingService", "Refreshed token: " + token!!)
applicationContext.sendBroadcast(Intent(TOKEN_REFRESHED_EVENT))
}
}
Broadcasts sent via Context.sendBroadcast() are not local broadcasts, that's why you're not receiving them.
You need to use LocalBroadcastManager for the sending as well.
Instead of the following:
applicationContext.sendBroadcast(Intent(TOKEN_REFRESHED_EVENT))
You should do something like this:
LocalBroadcastManager.getInstance(this)
.sendBroadcast(Intent(TOKEN_REFRESHED_EVENT))
Also, do not forget to unregister your receiver when appropriate to avoid memory leaks:
// most likely in onDestroy() (since the registration is in onCreate())
LocalBroadcastManager.getInstance(this)
.unregisterReceiver(broadcastReceiver)
I want to start using Robolectric and RoboGuice in my Android-Apps. While I make satisfactory progress using Robolectric I am stuck using RoboGuice. I created a small Android-App for experimenting. It is only one Activity, injecting a Button and setting its OnClickListener.
In the related Test-Class I want to Inject this Activity, to be able to test the Button. I tried a lot of things I found all over the internet, but none of these worked, so I give it a go here.
Here is some code:
MainActivity.java:
package com.example.TrialApp;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import roboguice.activity.RoboActivity;
import roboguice.inject.InjectView;
public class MainActivity extends RoboActivity implements View.OnClickListener {
#InjectView(R.id.main_LoginButton) private Button loginButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
loginButton.setOnClickListener(this);
}
#Override
public void onClick(View view) {
SharedPreferences.Editor editor;
if (view.getId() == R.id.main_Login_Button)
Log.i("Login-Button pressed... ", "");
}
}
CustomRobolectricTestRunner.java:
package com.example.TrialApp;
import com.xtremelabs.robolectric.RobolectricTestRunner;
import org.junit.runners.model.InitializationError;
import java.io.File;
public class CustomRobolectricTestRunner extends RobolectricTestRunner {
public CustomRobolectricTestRunner(Class testClass) throws InitializationError {
// defaults to "AndroidManifest.xml", "res" in the current directory
super(testClass, new File("TrialApp"));
}
}
MainActivity_Test.java:
package com.example.TrialApp;
import com.google.inject.Inject;
import com.xtremelabs.robolectric.Robolectric;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertNotNull;
#RunWith(CustomRobolectricTestRunner.class)
public class MainActivity_Test {
#Inject MainActivity mainActivity;
#Inject ClassWithoutAName classWithoutAName;
#Before
public void setUp() {
}
#Test
public void mainActivityShouldNotBeNull() {
assertNotNull(mainActivity);
}
#Test
public void classWithoutANameShouldNotBeNull() {
assertNotNull(classWithoutAName);
}
}
classWithoutAName is just a non-Activity-class with no content. I added just for injecting a non-Activity-class.
Running the Test-Class both tests fail giving the following errors:
java.lang.AssertionError
at com.example.TrialApp.MainActivity_Test.mainActivityShouldNotBeNull(MainActivity_Test.java:33) <8 internal calls>
at com.xtremelabs.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:288) <16 internal calls>
and
java.lang.AssertionError
at com.example.TrialApp.MainActivity_Test.classWithoutANameShouldNotBeNull(MainActivity_Test.java:38) <8 internal calls>
at com.xtremelabs.robolectric.RobolectricTestRunner$1.evaluate(RobolectricTestRunner.java:288) <16 internal calls>
Obviously something is missing. Injecting in MainActivity works fine, and the app is running.
Why is the same pattern of injecting dependencies in a Robolectric-Testclass not working? Where is the missing link?
Thank you
After reading some more articles and threads I found a partial solution. I still haven't found a way to inject UI-elements in a testclass, but I found out, how to inject non-UI-elements in a testclass. The trick is to implement a "RobolectricTestModule" extending the AbstractModule. The bindings made in the modules declared in the roboguice.xml are not present in the test-environment, so we need to declare the bindings for the test-environment in this extra-module. In the customized Testrunner we replace the DefaultRoboModule with the new RobolectricTestModule.
ClassWithoutAName.java:
package com.example.TrialApp;
public class ClassWithoutAName {
private String string;
public ClassWithoutAName(String string) {
this.string = string;
}
public String getString() {
return string;
}
}
ClassWithoutANameProvider.java
package com.example.TrialApp.GuiceModules;
import com.example.TrialApp.ClassWithoutAName;
import com.google.inject.Provider;
public class ClassWithoutANameProvider implements Provider<ClassWithoutAName> {
#Override
public ClassWithoutAName get() {
return new ClassWithoutAName("testString");
}
}
ClassWithOutANameModule.java
package com.example.TrialApp.GuiceModules;
import com.example.TrialApp.ClassWithoutAName;
import com.google.inject.AbstractModule;
public class ClassWithOutANameModule extends AbstractModule {
#Override
protected void configure() {
bind(ClassWithoutAName.class).toProvider(ClassWithoutANameProvider.class);
}
}
RobolectricTestModule.java
package com.example.TrialApp.GuiceModules;
import com.example.TrialApp.WeirdThings;
import com.google.inject.AbstractModule;
public class RobolectricTestModule extends AbstractModule {
#Override
protected void configure() {
bind(ClassWithoutAName.class).toProvider(ClassWithoutANameProvider.class);
}
}
CustomRobolectricTestRunner.java
package com.example.TrialApp;
import android.app.Application;
import com.example.TrialApp.GuiceModules.RobolectricTestModule;
import com.xtremelabs.robolectric.Robolectric;
import com.xtremelabs.robolectric.RobolectricTestRunner;
import org.junit.runners.model.InitializationError;
import roboguice.RoboGuice;
import java.io.File;
public class CustomRobolectricTestRunner extends RobolectricTestRunner {
public CustomRobolectricTestRunner(Class testClass) throws InitializationError {
// defaults to "AndroidManifest.xml", "res" in the current directory
super(testClass, new File("TrialApp"));
}
#Override
public void prepareTest(Object test) {
Application application = (Application) Robolectric.application;
RoboGuice.setBaseApplicationInjector(application, RoboGuice.DEFAULT_STAGE,
RoboGuice.newDefaultRoboModule(application), new RobolectricTestModule());
RoboGuice.getInjector(application).injectMembers(test);
}
}
Thats it for the non-UI-Elements.