For API level 26 we have to set a channel id as a reference. I learned how to do it without a channelID and below there is my firebase messaging set-up code. But now for the new Android api 26 level
NotificationCompat.Builder(this);
This doesn't work and I should add a reference to channelID as
NotificationCompat.Builder(this, channelID);
But I don't know how to do it.
Can you help me to understand how to create a channel ID for Firebase cloud messaging?
AndroidManifest.xml
<service android:name=".MyFirebaseInstanceIdService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT"></action>
</intent-filter>
</service>
<service android:name=".MyFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT"></action>
</intent-filter>
</service>
MyFirebaseInstanceIdService Java Class:
import android.util.Log;
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;
public class MyFirebaseInstanceIdService extends FirebaseInstanceIdService {
private static final String REG_TOKEN = "REG_TOKEN";
#Override
public void onTokenRefresh() {
String recent_token = FirebaseInstanceId.getInstance().getToken();
Log.d(REG_TOKEN, recent_token);
}
}
MyFirebaseMessagingService Java Class
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.NotificationCompat;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
public class MyFirebaseMessagingService extends FirebaseMessagingService {
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_ONE_SHOT);
// Here I will set up a channel ID as "CH_ID_1" but I do not know how to do this.
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, "CH_ID_1");
//****************************************************
notificationBuilder.setContentTitle("FCM NOTIFICATION");
notificationBuilder.setContentText(remoteMessage.getNotification().getBody());
notificationBuilder.setAutoCancel(true);
notificationBuilder.setSmallIcon(R.mipmap.ic_launcher);
notificationBuilder.setContentIntent(pendingIntent);
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0, notificationBuilder.build());
}
}
Gradle - Dependencies
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
implementation 'com.android.support:design:26.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.google.firebase:firebase-core:16.0.0'
implementation 'com.google.firebase:firebase-messaging:17.0.0'
}
apply plugin: 'com.google.gms.google-services'
What I did in an app before was to initialize the Notification Channels as soon as the app starts. So I added an init function in my Application class, like so:
#TargetApi(Build.VERSION_CODES.O)
private void initNotificationChannels() {
NotificationChannel publicChannel = new NotificationChannel(
Constants.NOTIFICATION_CHANNEL_PUBLIC,
Constants.NOTIFICATION_CHANNEL_PUBLIC,
NotificationManager.IMPORTANCE_DEFAULT);
publicChannel.setDescription(Constants.NOTIFICATION_CHANNEL_PUBLIC);
NotificationChannel topicChannel = new NotificationChannel(
Constants.NOTIFICATION_CHANNEL_TOPIC,
Constants.NOTIFICATION_CHANNEL_TOPIC,
NotificationManager.IMPORTANCE_DEFAULT);
topicChannel.setDescription(Constants.NOTIFICATION_CHANNEL_TOPIC);
NotificationChannel privateChannel = new NotificationChannel(
Constants.NOTIFICATION_CHANNEL_PRIVATE,
Constants.NOTIFICATION_CHANNEL_PRIVATE,
NotificationManager.IMPORTANCE_HIGH);
privateChannel.setDescription(Constants.NOTIFICATION_CHANNEL_PRIVATE);
privateChannel.canShowBadge();
List<NotificationChannel> notificationChannels = new ArrayList<>();
notificationChannels.add(publicChannel);
notificationChannels.add(topicChannel);
notificationChannels.add(privateChannel);
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (mNotificationManager != null) {
mNotificationManager.createNotificationChannels(notificationChannels);
}
}
Then in my FirebaseMessagingService, I made a function that gets the channelId when building notifications:
private String getChannelId(String source) {
if (!TextUtils.isEmpty(source)) {
if (source.contains(Constants.TOPIC_PREFIX)) {
return (TextUtils.equals((TOPIC_PREFIX + Constants.NOTIFICATION_CHANNEL_PUBLIC), source)) ?
Constants.NOTIFICATION_CHANNEL_PUBLIC : Constants.NOTIFICATION_CHANNEL_TOPIC;
} else {
return Constants.NOTIFICATION_CHANNEL_PRIVATE;
}
} else {
return Constants.NOTIFICATION_CHANNEL_PUBLIC;
}
}
This caters to three types of notification that our app needs -- Public, Topic, and Private. You can specify the channels you need on your own.
Related
I have a WebView application, with firebase cloud Firebase Cloud Messaging for push notifications.I want that clicking on a notification to open a URL in my WebView, and not the main page of the default site in my app.I need it to be a different URL on every notification that I send through FCM each time.I searched a lot on Google and found all kinds of answers. But in practice it doesn't work.
for exmaple, Android Push Notification URL
.... thank you!
my code:
MainActivity.java
`
package com.webview.myapplication;
import android.Manifest;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.DownloadManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.webkit.CookieManager;
import android.webkit.URLUtil;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import androidx.annotation.RequiresApi;
import androidx.core.app.ActivityCompat;
import androidx.core.app.NotificationCompat;
import java.util.Arrays;
import java.util.List;
public class MainActivity extends Activity {
private final int STORAGE_PERMISSION_CODE = 1;
private WebView mWebView;
public void notifyParse(){
if (getIntent().getExtras() != null) {
if (getIntent().getStringExtra("LINK")!=null) {
Intent i=new Intent(this,urlFromNotify.class);
i.putExtra("link",getIntent().getStringExtra("LINK"));
MainActivity.this.startActivity(i);
finish();
}
}
}
private void requestStoragePermission() {
if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) {
new AlertDialog.Builder(this)
.setTitle("Permission needed")
.setMessage("This permission is needed to download files")
.setPositiveButton("ok", (dialog, which) -> ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, STORAGE_PERMISSION_CODE))
.setNegativeButton("cancel", (dialog, which) -> dialog.dismiss())
.create().show();
} else {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, STORAGE_PERMISSION_CODE);
}
}
#RequiresApi(api = Build.VERSION_CODES.Q)
#Override
#SuppressLint("SetJavaScriptEnabled")
protected void onCreate(Bundle savedInstanceState) {
requestStoragePermission();
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = findViewById(R.id.activity_main_webview);
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
mWebView.setWebViewClient(new HelloWebViewClient());
mWebView.setDownloadListener((url, userAgent, contentDisposition, mimeType, contentLength) -> {
Uri source = Uri.parse(url);
DownloadManager.Request request = new DownloadManager.Request(source);
String cookies = CookieManager.getInstance().getCookie(url);
request.addRequestHeader("cookie", cookies);
request.addRequestHeader("User-Agent", userAgent);
request.setDescription("Downloading File...");
request.setTitle(URLUtil.guessFileName(url, contentDisposition, mimeType));
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, URLUtil.guessFileName(url, contentDisposition, mimeType));
DownloadManager dm = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
dm.enqueue(request);
Toast.makeText(this, R.string.downloading, Toast.LENGTH_LONG).show();
});
mWebView.loadUrl("https://noam.org.il"); //Replace The Link Here
}
#Override
public void onBackPressed() {
if (mWebView.canGoBack()) {
mWebView.goBack();
} else {
super.onBackPressed();
}
}
// checking if URL is allowed
private class HelloWebViewClient extends WebViewClient {
#Override
public boolean shouldOverrideUrlLoading(final WebView view, final String url)
{
view.loadUrl(url);
return true;
}
}
}
`
FirebaseMessageReceiver.java
`
package com.webview.myapplication;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.RingtoneManager;
import android.net.Uri;
import android.os.Build;
import android.widget.RemoteViews;
import androidx.core.app.NotificationCompat;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
public class FirebaseMessageReceiver
extends FirebaseMessagingService {
Bitmap bitmap;
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
String message = remoteMessage.getData().get("message");
//imageUri will contain URL of the image to be displayed with Notification
String imageUri = remoteMessage.getData().get("image");
String link=remoteMessage.getData().get("link");
//To get a Bitmap image from the URL received
bitmap = getBitmapfromUrl(imageUri);
sendNotification(message, bitmap,link);
}
/**
* Create and show a simple notification containing the received FCM message.
*/
private void sendNotification(String messageBody, Bitmap image, String link) {
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra("LINK",link);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
PendingIntent.FLAG_ONE_SHOT);
Uri defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.setLargeIcon(image)/*Notification icon image*/
.setSmallIcon(R.drawable.gfg)
.setContentTitle(messageBody)
.setStyle(new NotificationCompat.BigPictureStyle()
.bigPicture(image))/*Notification with Image*/
.setAutoCancel(true)
.setSound(defaultSoundUri)
.setContentIntent(pendingIntent);
NotificationManager notificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
}
public Bitmap getBitmapfromUrl(String imageUrl) {
try {
URL url = new URL(imageUrl);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.connect();
InputStream input = connection.getInputStream();
Bitmap bitmap = BitmapFactory.decodeStream(input);
return bitmap;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}}
`
urlFromNotify.java
`
package com.webview.myapplication;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.webkit.WebView;
public class urlFromNotify extends AppCompatActivity {
void urlOpen(){
WebView webView = (WebView) findViewById(R.id.activity_main_webview);
Intent i = getIntent();
String url = i.getStringExtra("link");
webView.loadUrl(url);
}
}
`
im working on my first project
i used FirebaseAuth and firebase realtime database
i followed every step carefully at first everything is alright and my project has connect to the db successfully(two weeks ago) i had even test my db , but after i update my android studio version something went wrong :(
my app keep crash every time i click on the buttons.
i really don't know what is the problem? before everything was alright
"i read articles about the api-key and i checked it there is nothing wrong with it"
here is my register code :
package com.example.lastone;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.santalu.maskedittext.MaskEditText;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.view.View;
import android.widget.Toast;
import com.basgeekball.awesomevalidation.AwesomeValidation;
import com.basgeekball.awesomevalidation.ValidationStyle;
import com.basgeekball.awesomevalidation.utility.RegexTemplate;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
public class Register2 extends AppCompatActivity {
MaskEditText license,phone;
private EditText email,pass1,pass2;
private Button but;
AwesomeValidation awesomeValidation;
private FirebaseDatabase rootNode;
//realtime
DatabaseReference reference;
//authinicate
private FirebaseAuth firebaseAuth;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_register2);
//validation
awesomeValidation=new AwesomeValidation(ValidationStyle.BASIC);
updateUI();
//authinication
firebaseAuth= FirebaseAuth.getInstance();
}
private void updateUI() {
license=findViewById(R.id.license);
email=(EditText)findViewById(R.id.email);
phone=findViewById(R.id.phone);
pass1=(EditText)findViewById(R.id.pass1);
pass2=(EditText)findViewById(R.id.pass2);
but=(Button)findViewById(R.id.but);
String regexPassword = "(?=.*[a-z])(?=.*[A-Z])(?=.*[\\d])(?=.*[~`!##\\$%\\^&\\*\\(\\)\\-_\\+=\\{\\}\\[\\]\\|\\;:\"<>,./\\?]).{8,}";
String Regex = ("[0-9]{10}");
awesomeValidation.addValidation(Register2.this,R.id.license, Regex, R.string.licensee);
awesomeValidation.addValidation(Register2.this,R.id.email,android.util.Patterns.EMAIL_ADDRESS, R.string.emailrr);
awesomeValidation.addValidation(Register2.this,R.id.phone, RegexTemplate.TELEPHONE, R.string.phonen);
awesomeValidation.addValidation(Register2.this,R.id.pass1,regexPassword,R.string.pass11);
awesomeValidation.addValidation(Register2.this,R.id.pass2,R.id.pass1,R.string.pass22);
but.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View view)
{
signUp();
// send to the database
rootNode= FirebaseDatabase.getInstance();
reference=rootNode.getReference("users");
//get all the values from the textfield
String License =license.getText().toString();
String Phone_number =phone.getText().toString();
String Email =email.getText().toString();
String password =pass1.getText().toString();
UserHelperClass helper=new UserHelperClass(License , Phone_number, Email, password);
reference.child(License).setValue(helper);
//validate
if(awesomeValidation.validate())
{
Toast.makeText(Register2.this, "Data Received Successfully", Toast.LENGTH_SHORT).show();
}
else
Toast.makeText(Register2.this, "Error", Toast.LENGTH_SHORT).show();
}
});
}
private void signUp() {
firebaseAuth.createUserWithEmailAndPassword(email.getText().toString(), pass1.getText().toString())
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// Sign in success, update UI with the signed-in user's information
FirebaseUser user = firebaseAuth.getCurrentUser();
String email=user.getEmail();
String name=user.getDisplayName();
Toast.makeText(Register2.this, "Name : "+name+"\n"+"Email : "+email,
Toast.LENGTH_SHORT).show();
Intent intent=new Intent(Register2.this,Login.class);
startActivity(intent);
} else {
// If sign in fails, display a message to the user.
Toast.makeText(Register2.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
// updateUI(null);
}
// ...
}
});
}
}
login code:
package com.example.lastone;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.app.Dialog;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.Query;
import com.google.firebase.database.ValueEventListener;
public class Login extends AppCompatActivity
{
private EditText email,pass;
private Button lo;
private FirebaseAuth firebaseAuth;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
email=(EditText)findViewById(R.id.email);
pass=(EditText)findViewById(R.id.pass);
lo=(Button)findViewById(R.id.lo);
firebaseAuth= FirebaseAuth.getInstance();
lo.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Signin();
}
});
}
private void Signin()
{
firebaseAuth.signInWithEmailAndPassword(email.getText().toString(), pass.getText().toString())
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// Sign in success, update UI with the signed-in user's information
FirebaseUser user = firebaseAuth.getCurrentUser();
String email=user.getEmail();
String name=user.getDisplayName();
Toast.makeText(Login.this,"Logged in successuly",
Toast.LENGTH_SHORT).show();
} else {
// If sign in fails, display a message to the user.
Toast.makeText(Login.this, "Authentication failed.",
Toast.LENGTH_SHORT).show();
// Intent intent=new Intent(Login.this,profile.class);
}
}
});
}
}
error:
2020-04-04 02:25:41.923 7642-7642/com.example.lastone E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.lastone, PID: 7642
java.lang.IllegalArgumentException: Given String is empty or null
at com.google.android.gms.common.internal.Preconditions.checkNotEmpty(Unknown Source)
at com.google.firebase.auth.FirebaseAuth.signInWithEmailAndPassword(com.google.firebase:firebase-auth##19.3.0:205)
at com.example.lastone.Login.Signin(Login.java:53)
at com.example.lastone.Login.access$000(Login.java:27)
at com.example.lastone.Login$1.onClick(Login.java:46)
at android.view.View.performClick(View.java:5637)
at android.view.View$PerformClick.run(View.java:22429)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
also build gradle :
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'
android {
compileSdkVersion 29
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.example.lastone"
minSdkVersion 16
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'com.basgeekball:awesome-validation:4.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.firebase:firebase-database:19.2.1'
implementation 'com.google.firebase:firebase-auth:19.3.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'com.github.santalu:mask-edittext:1.0.2'
}
the deadline is on this week please help :((
Add these dependencies and run your your projet again
implementation 'com.google.firebase:firebase-analytics:17.2.3'
implementation 'com.google.firebase:firebase-core:17.2.3'
As soon as I updated the flutter to version 1.12.13 I found this issue and can't fix it. I did as the firebase_messaging tutorial sent and got the following error:
"error: incompatible types: PluginRegistry cannot be converted to FlutterEngine
GeneratedPluginRegistrant.registerWith (registry); "
My code is as follows:
package io.flutter.plugins;
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;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.os.Build;
public class Application extends FlutterApplication implements PluginRegistrantCallback {
#Override
public void onCreate() {
super.onCreate();
FlutterFirebaseMessagingService.setPluginRegistrant(this);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
NotificationChannel channel = new NotificationChannel("messages","Messages", NotificationManager.IMPORTANCE_LOW);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(channel);
}
}
#Override
public void registerWith(PluginRegistry registry) {
GeneratedPluginRegistrant.registerWith(registry);
}
}
Replace this code line:
GeneratedPluginRegistrant.registerWith(registry);
with this:
FirebaseMessagingPlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));
Make sure to import:
import io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin;
Updated on December 31, 2019.
You should not use the Firebase cloud messaging tool to send notifications, as it forces you to use the title and body.
You must send a notification without the title and body. have the application in the background, that should work for you.
If it works for you, I would appreciate it if you could give me a vote on this answer, thank you.
I have found a temporary solution. I am not sure this is the best fix but my plugins work as expected and I assume the problem has to be with the registry provided by io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService on line 164.
My AndroidManifest.xml file:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="Your Package"> // CHANGE THIS
<application
android:name=".Application"
android:label="" // YOUR NAME APP
android:icon="#mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="#style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<!-- BEGIN: Firebase Cloud Messaging -->
<intent-filter>
<action android:name="FLUTTER_NOTIFICATION_CLICK" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<!-- END: Firebase Cloud Messaging -->
</activity>
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
My Application.java
package YOUR PACKAGE HERE;
import io.flutter.app.FlutterApplication;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback;
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) {
FirebaseCloudMessagingPluginRegistrant.registerWith(registry);
}
}
My FirebaseCloudMessagingPluginRegistrant.java
package YOUR PACKAGE HERE;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin;
public final class FirebaseCloudMessagingPluginRegistrant{
public static void registerWith(PluginRegistry registry) {
if (alreadyRegisteredWith(registry)) {
return;
}
FirebaseMessagingPlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));
}
private static boolean alreadyRegisteredWith(PluginRegistry registry) {
final String key = FirebaseCloudMessagingPluginRegistrant.class.getCanonicalName();
if (registry.hasPlugin(key)) {
return true;
}
registry.registrarFor(key);
return false;
}
}
Send Notification in dart:
Future<void> sendNotificationOnBackground({
#required String token,
}) async {
await firebaseMessaging.requestNotificationPermissions(
const IosNotificationSettings(sound: true, badge: true, alert: true, provisional: false),
);
await Future.delayed(Duration(seconds: 5), () async {
await http.post(
'https://fcm.googleapis.com/fcm/send',
headers: <String, String>{
'Content-Type': 'application/json',
'Authorization': 'key=$SERVERTOKEN', // Constant string
},
body: jsonEncode(
<String, dynamic>{
'notification': <String, dynamic>{
},
'priority': 'high',
'data': <String, dynamic>{
'click_action': 'FLUTTER_NOTIFICATION_CLICK',
'id': '1',
'status': 'done',
'title': 'title from data',
'message': 'message from data'
},
'to': token
},
),
);
});
}
I added a wait with a duration of 5 seconds so you can put the application in the background and verify that the message in the background is running
A port of DomingoMG's code to Kotlin can be found below (file paths included).
Tested and working on 10.2020.
/pubspec.yaml
firebase_messaging: ^7.0.0
/android/app/src/main/kotlin/Application.kt
package YOUR_PACKAGE_HERE
import io.flutter.app.FlutterApplication
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback
import io.flutter.plugins.firebasemessaging.FlutterFirebaseMessagingService
public class Application: FlutterApplication(), PluginRegistrantCallback {
override fun onCreate() {
super.onCreate()
FlutterFirebaseMessagingService.setPluginRegistrant(this)
}
override fun registerWith(registry: PluginRegistry) {
FirebaseCloudMessagingPluginRegistrant.registerWith(registry)
}
}
/android/app/src/main/kotlin/FirebaseCloudMessagingPluginRegistrant.kt
package YOUR_PACKAGE_HERE
import io.flutter.plugin.common.PluginRegistry
import io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin
class FirebaseCloudMessagingPluginRegistrant {
companion object {
fun registerWith(registry: PluginRegistry) {
if (alreadyRegisteredWith(registry)) {
return;
}
FirebaseMessagingPlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"))
}
fun alreadyRegisteredWith(registry: PluginRegistry): Boolean {
val key = FirebaseCloudMessagingPluginRegistrant::class.java.name
if (registry.hasPlugin(key)) {
return true
}
registry.registrarFor(key)
return false
}
}
}
Some research and I found this to be working.
Seached up the official Flutterfire repository, and checked out the latest plugins on it.
GeneratedPluginRegistrant.registerWith(registry); was not working, which is mentioned on the official docs as well. FlutterFire firebase_messaging.
Using the plugin io.flutter.plugins.firebase.messaging.FlutterFirebaseMessagingPlugin, which I discovered from Flutterfire GitHub repo
Currently on firabase_messaging: 9.0.0
//
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.common.PluginRegistry.PluginRegistrantCallback;
import io.flutter.plugins.GeneratedPluginRegistrant;
import io.flutter.plugins.firebase.messaging.FlutterFirebaseMessagingBackgroundService;
// Be sure to import the exact Plugin
import io.flutter.plugins.firebase.messaging.FlutterFirebaseMessagingPlugin;
public class Application extends FlutterApplication implements PluginRegistrantCallback {
//
#Override
public void onCreate() {
super.onCreate();
FlutterFirebaseMessagingBackgroundService.setPluginRegistrant(this);
}
#Override
public void registerWith(PluginRegistry registry) {
FlutterFirebaseMessagingPlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));
}
}
Just Do
GeneratedPluginRegistrant.registerWith((FlutterEngine) registry);
inplace of
GeneratedPluginRegistrant.registerWith(registry);
In addition to DomingoMG's answer, don't forget to remove
#Override
public void configureFlutterEngine(#NonNull FlutterEngine flutterEngine) {
GeneratedPluginRegistrant.registerWith(flutterEngine);
from the mainactivity file under the android folder. If not, you will get an error.
I added only water class as extra from the steps in Firebase Messaging package and it was solved :
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin;
public final class FirebaseCloudMessagingPluginRegistrant{
public static void registerWith(PluginRegistry registry) {
if (alreadyRegisteredWith(registry)) {
return;
}
FirebaseMessagingPlugin.registerWith(registry.registrarFor("io.flutter.plugins.firebasemessaging.FirebaseMessagingPlugin"));
}
private static boolean alreadyRegisteredWith(PluginRegistry registry) {
final String key = FirebaseCloudMessagingPluginRegistrant.class.getCanonicalName();
if (registry.hasPlugin(key)) {
return true;
}
registry.registrarFor(key);
return false;
}}
I found this to work:
#Override
public void configureFlutterEngine(#NonNull FlutterEngine flutterEngine) {
super.configureFlutterEngine(flutterEngine);
// continue with custom method channel registration.
...
}
Not sure if you also need to use cleanUpFlutterEngine to clean method channel registration.
Change your MainActivity.java file to:
package co.appbrewery.micard;
import io.flutter.embedding.android.FlutterActivity;
public class MainActivity extends FlutterActivity {
}
As per the Dagger 2 documentation
I am trying to set up a test environment according to Dagger2's documentation
(Just for context, I have successfully done this in Dagger 1.)
The problem, specifically, is that while Dagger2 correctly generates DaggerRoboDaggerComponent (as used in App.java), it does not generate DaggerTestRoboDaggerComponent (as used in MainActivityTest.java). I have checked the directory structure to make sure it's not hiding in some obscure place, and done the requisite clean, rebuild, Invalidate Caches / Restart, etc.
If someone could please let me know the error of my ways, I'd appreciate it. Thanks in advance.
You can clone the project here
Or, browse thru the project files below:
build.gradle
apply plugin: 'com.android.application'
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "xx.robodagger"
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.3.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.google.dagger:dagger:2.10'
annotationProcessor "com.google.dagger:dagger-compiler:2.10"
testCompile 'junit:junit:4.12'
testCompile "org.robolectric:robolectric:3.3.1"
}
App.java
package xx.robodagger;
import android.app.Application;
public class App extends Application {
static RoboDaggerComponent roboDaggerComponent;
static RoboDaggerComponent getComponent() {
return roboDaggerComponent;
}
#Override public void onCreate() {
super.onCreate();
roboDaggerComponent = DaggerRoboDaggerComponent.builder()
.roboDaggerModule(new RoboDaggerModule())
.build();
}
}
Foo.java
package xx.robodagger;
class Foo {
#Override public String toString() {
return "foo";
}
}
MainActivity.java
package xx.robodagger;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
import javax.inject.Inject;
public class MainActivity extends AppCompatActivity {
#Inject Foo foo;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
App.getComponent().inject(this);
setContentView(R.layout.activity_main);
TextView tv = (TextView)findViewById(R.id.textview);
tv.setText(foo.toString());
}
}
RoboDaggerComponent.java
package xx.robodagger;
import javax.inject.Singleton;
import dagger.Component;
#Singleton
#Component(modules={RoboDaggerModule.class})
interface RoboDaggerComponent {
void inject(MainActivity mainActivity);
}
RoboDaggerModule.java
package xx.robodagger;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
#SuppressWarnings("unused")
#Module
class RoboDaggerModule {
#Provides
#Singleton
Foo providesFoo() { return new Foo(); }
}
And now in the test directory,
FakeFoo.java
package xx.robodagger;
class FakeFoo extends Foo {
#Override public String toString() {
return "bar";
}
}
MainActivityTest.java
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import javax.inject.Inject;
#RunWith(RobolectricTestRunner.class)
#Config(constants = BuildConfig.class)
public class MainActivityTest {
TestRoboDaggerComponent testRoboDaggerComponent;
#Inject Foo foo;
#Before
public void setup() {
testRoboDaggerComponent = DaggerTestRoboDaggerComponent.builder()
.testRoboDaggerModule(new TestRoboDaggerModule())
.build();
testRoboDaggerComponent.inject(this);
}
#Test
public void fooBarTest() {
assert(foo.toString().equals("bar"));
}
}
TestRoboDaggerComponent.java
package xx.robodagger;
import javax.inject.Singleton;
import dagger.Component;
#Singleton
#Component(modules={TestRoboDaggerModule.class})
interface TestRoboDaggerComponent {
void inject(MainActivityTest mainActivityTest);
}
TestRoboDaggerModule.java
package xx.robodagger;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
#SuppressWarnings("unused")
#Module
class TestRoboDaggerModule {
#Provides
#Singleton
Foo providesFoo() { return new FakeFoo(); }
}
Delete TestRoboDaggerComponent.java, it's not needed
Modify App.java to setup the component via a protected method
#Override public void onCreate() {
super.onCreate();
setupComponent();
}
protected void setupComponent() {
roboDaggerComponent = DaggerRoboDaggerComponent.builder()
.roboDaggerModule(new RoboDaggerModule())
.build();
}
Modify TestApp.java to extend App, and override the previously mentioned method with the TestModule
public class TestApp extends App {
#Override
protected void setupComponent() {
roboDaggerComponent = DaggerRoboDaggerComponent.builder()
.roboDaggerModule(new TestRoboDaggerModule())
.build();
}
}
And finally, in the actual test, leverage Robolectric's power to specify the Application module
#RunWith(RobolectricTestRunner.class)
#Config(constants = BuildConfig.class,
application = TestApp.class)
public class MainActivityTest {
private MainActivity mainActivity;
#Before
public void setup() {
mainActivity = Robolectric.setupActivity(MainActivity.class);
}
#Test
public void fooBarTest() {
TextView tv = (TextView)mainActivity.findViewById(R.id.textview);
assert(tv.getText().equals("bar"));
}
}
Notice that there isn't any DI in the actual test proper. The injection of MainActivity takes place as expected, and with the overridden module. If you have a dependency that uses #Injects in the constructor, the solution per Dagger's documentation is not to use injection in the test, but to just call the normal constructor with mocked objects. This all makes sense. I still think the originally reported issue is a bug, but whatever.
Following my previous question about messaging on javafx,
I want to notify user when message arrive.
Recently I use NotificationCompat.
When user touch the notification from notification bar, it must directly open related view (DirectMessageView).
I’ve configure an activity class (NotificationActivy extends Activity) on receive tag in androidmanifest.xml and call DirectMessageView and it's presenter on method "onCreate".
when user touch message on notification, it won’t display DirectMessageView, but the method inside presenter is called, and the view is not visible. Perhaps it’s my wrong implementation, please help
Here are classes I have created
Class SKSAplication that extends MobileAplication
public class SKSApplication extends MobileApplication{
private static SKSApplication instance;
public static final String DIRECT_MESSAGE_VIEW = "DIRECT_MESSAGE_VIEW";
public static final String GROUP_MESSAGE_VIEW = "GROUP_MESSAGE_VIEW";
private ViewRefresh activeView;
public SKSApplication() {
instance = this;
}
public static SKSApplication getInstance() {
return instance;
}
#Override
public void init() {
addViewFactory(HOME_VIEW, () -> {
HomeView homeView = new HomeView();
homePresenter = (HomePresenter) homeView.getPresenter();
return (View) homeView.getView();
});
addViewFactory(DIRECT_MESSAGE_VIEW, () -> {
DirectMessageView directMessageView = new DirectMessageView();
return (View) directMessageView.getView();
});
addViewFactory(GROUP_MESSAGE_VIEW, () -> {
GroupMessageView groupMessageView = new GroupMessageView();
return (View) groupMessageView.getView();
});
public void doRefreshMessageUI(Object objectModel) {
System.out.println("SKSApplication.doRefreshMessageUI " + getView().getName());
if (getActiveView() != null)
getActiveView().doRefresh(objectModel);
}
public ViewRefresh getActiveView() {
return activeView;
}
public void setActiveView(ViewRefresh activeView) {
this.activeView = activeView;
}
}
Class MyGCMListenerService
public class MyGCMListenerService extends GcmListenerService {
private final String NOTIFICATION_TAG = "NotificationExample";
public MyGCMListenerService() {
}
#Override
public void onMessageReceived(String from, Bundle data) {
String varMessage = data.getString("message");
try {
JSONObject json = new JSONObject(varMessage);
String messageContent = getStringFromJSON(json, "message");
Integer senderId = getIntegerFromJSON(json, "senderId");
String senderName = getStringFromJSON(json, "senderName");
String comId = getStringFromJSON(json, "communityId");
String salesGroup = getStringFromJSON(json, "salesGroup");
Integer messageType = getIntegerFromJSON(json, "type");
doViewNotification(messageType, senderName, salesGroup);
SKSApplication.getInstance().doRefreshMessageUI(messageContent,senderId,senderName,comId );
} catch (JSONException e) {
e.printStackTrace();
}
}
private void doViewNotification(Integer messageType, String senderName, String salesGroup) {
StringBuilder msg = new StringBuilder()
.append("Message from ")
.append(senderName)
.append(" #").append(salesGroup);
Intent resultIntent = new Intent(FXActivity.getInstance(), NotificationActivity.class);
resultIntent.putExtra(Constants.EXTRA_INTENT.MESSAGE_TYPE.getValue(), messageType);
PendingIntent resultPendingIntent =
PendingIntent.getActivity(
FXActivity.getInstance(),
0,
resultIntent,
PendingIntent.FLAG_UPDATE_CURRENT
);
Uri uri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
long[] v = {500, 1000};
NotificationCompat.Builder builder =
new NotificationCompat.Builder(this)
.setSound(uri)
.setSmallIcon(FXActivity.getInstance().getApplicationInfo().icon)
.setContentTitle(getApplicationName(FXActivity.getInstance().getApplicationContext()))
.setVibrate(v)
.setContentText(msg.toString())
.setPriority(Notification.PRIORITY_DEFAULT)
.setNumber(100)
.setWhen(System.currentTimeMillis())
.setContentIntent(resultPendingIntent)
.setAutoCancel(true)
.addAction(FXActivity.getInstance().getApplicationInfo().icon, "Action", null);
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ECLAIR) {
notificationManager.notify(NOTIFICATION_TAG, 0, builder.build());
} else {
notificationManager.notify(NOTIFICATION_TAG.hashCode(), builder.build());
}
}
}
layout xml file (directmessage.fxml)
<View xmlns:fx="http://javafx.com/fxml/1" fx:id="directMessageView" prefHeight="600.0" prefWidth="400.0"
xmlns="http://javafx.com/javafx/8.0.40"
fx:controller="com.tenma.mobile.message.directmessage.DirectMessagePresenter">
</View>
Class DirectMessageView
public class DirectMessageView extends FXMLView {
}
Class DirectMessagePresenter
public class DirectMessagePresenter implements Initializable, ViewRefresh{
#Override
public void initialize(URL location, ResourceBundle resources) {
{
directMessageView.showingProperty().addListener((observable, oldValue, newValue) -> {
if (newValue) {
SKSApplication.getInstance().setActiveView(this);
doViewMessage();
}
});
}
private void doViewMessage() {
listMessage.getItems().clear();
MessageStoryHelper hlp = new MessageStoryHelper();
List<MessageModel> ls = null;
try {
ls = hlp.getMessages(Constants.MESSAGE_TYPE.DIRECT);
if (ls != null && ls.size() != 0)
for (MessageModel m :ls)
listMessage.add(m);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
androidmanifest
<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.tenma.mobile"
android:versionCode="1" android:versionName="1.0">
<supports-screens android:xlargeScreens="true"/>
<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.VIBRATE"/>
<uses-permission android:name="android.permission.MANAGE_DOCUMENTS"/>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="17"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<permission android:name="com.tenma.mobile.permission.C2D_MESSAGE"
android:protectionLevel="signature"/>
<uses-permission android:name="com.tenma.mobile.permission.C2D_MESSAGE"/>
<application android:label="MobileSales" android:name="android.support.multidex.MultiDexApplication"
android:icon="#mipmap/ic_launcher">
<activity android:name="javafxports.android.FXActivity" android:label="MobileSales"
android:configChanges="orientation|screenSize">
<meta-data android:name="main.class" android:value="com.tenma.mobile.SKSApplication"/>
<meta-data android:name="debug.port" android:value="0"/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name="com.tenma.mobile.common.NotificationActivity"
android:parentActivityName="javafxports.android.FXActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="javafxports.android.FXActivity"/>
</activity>
<!--start-->
<receiver
android:name="com.google.android.gms.gcm.GcmReceiver"
android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE"/>
<!-- for Gingerbread GSF backward compat -->
<!--<action android:name="com.google.android.c2dm.intent.REGISTRATION"/>-->
<category android:name="com.tenma.mobile"/>
</intent-filter>
</receiver>
<!--end-->
<service
android:name="com.tenma.mobile.common.MyGCMListenerService"
android:exported="false">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE"/>
</intent-filter>
</service>
</application>
</manifest>
Class NotificationActivity extends Activity
public class NotificationActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent launchIntent = getIntent();
it working, but no view is visible
SKSApplication.getInstance().switchView(SKSApplication.DIRECT_MESSAGE_VIEW);
perhaps using this line below, but how to set Gluon View setContentView?
or retreive view ID and setContentView?
DirectMessageView directMessageView = new DirectMessageView();
Parent v = directMessageView.getView();
FXActivity.getInstance().setContentView(?????????);
}
}
Any help would be appreciated
Thank you in advance
Assuming you have the application running, this works for me:
Once you send the message back to the View, you need to close the notification activity by calling finish():
public class NotificationActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((SKSApplication) MobileApplication.getInstance()).doRefreshMessageUI("Hi from NotificationActivity");
finish();
}
}
Note you don't need to create a singleton instance of SKSApplication, you can retrieve an instance of it at any time by calling MobileApplication.getInstance().
Passing the message to the view can be done in SKSApplication by using Platform.runLater(), since you are not running on the JavaFX thread.
And to retrieve the corresponding view, just switch to that view:
public void doRefreshMessageUI(String msg) {
Platform.runLater(() -> {
messagesView.messageProperty().set(msg);
switchView(DIRECT_MESSAGE_VIEW);
});
}
providing the view has a StringProperty, that is bound to any of its controls text property.
private StringProperty message = new SimpleStringProperty();
public StringProperty messageProperty() {
return message;
}