I am trying to send push notifications through the firebase admin sdk, but the image somehow is not displayed in the push notification.
What's weird is that when I use an invalid key in the notifications object (like image) I get an error. So I assume I got the right keys specified. Documentation for the Notification can be found here.
The following code successfully sends a push notification but there is no image displayed on the users phone:
const admin = require('firebase-admin');
const app = admin.initializeApp({...}); // authenticated with credentials json file
await app.messaging().sendMulticast({
notification: {
title: "hello User",
body: "This is a push notification with an image",
imageUrl: "https://example.com/myPublicImage.png",
},
tokens: ["device_token_1", "device_token_2","..."]
});
Change imageUrl key to image in notification being sent by Firebase Admin SDK. I checked with imageUrl key, it does not work, rather,
it gives null to remoteMessage.getNotification().getImageUrl() in app.
In node.js server, you can create post request to send the Firebase message using Firebase Admin SDK:
Request.post({
"headers": {"Authorization":auth_key_string, "content-type": "application/json" },
"url": "https://fcm.googleapis.com/fcm/send",
"body": JSON.stringify({
"registration_ids" :receiver_token ,
"notification" : {
"title": title,
"body" : message,
"image":imageUrlVal
}
})
}, (error, response, body) => {
if(error) {
return console.dir(error);
}
console.dir(JSON.parse(body));
});
Now, handle this message from FirebaseActivity in Android App code.
In onMessageReceived method add this lines.
if (remoteMessage.getNotification() != null) {
// Since the notification is received directly from
// FCM, the title and the body can be fetched
// directly as below.
Log.d(TAG, "Message Received: " + "YES");
Bitmap bitmap = null;
try {
bitmap = getBitmapfromUrl(remoteMessage.getNotification().getImageUrl().toString());
} catch (Exception e) {
e.printStackTrace(); }
try {
showNotification(
remoteMessage.getNotification().getTitle(),
remoteMessage.getNotification().getBody(),bitmap );
} catch (IOException e) {
e.printStackTrace();
}
}
Define getBitmapfromUrl() as below:
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();
return BitmapFactory.decodeStream(input);
} catch (Exception e) {
Log.e("awesome", "Error in getting notification image: " + e.getLocalizedMessage());
return null;
}
}
showNotification() can be defined as:
public void showNotification(String title,
String message, Bitmap bitmap) throws IOException {
Intent intent
= new Intent(this, NextPageActivity.class);
// Assign channel ID
String channel_id = "notification_channel";
// Here FLAG_ACTIVITY_CLEAR_TOP flag is set to clear
// the activities present in the activity stack,
// on the top of the Activity that is to be launched
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
// Pass the intent to PendingIntent to start the
// next Activity
PendingIntent pendingIntent
= PendingIntent.getActivity(
this, 0, intent,
PendingIntent.FLAG_ONE_SHOT);
NotificationCompat.Builder builder
= new NotificationCompat
.Builder(getApplicationContext(), channel_id)
.setSmallIcon(R.drawable.app_icon)
.setAutoCancel(true)
.setVibrate(new long[]{1000, 1000, 1000, 1000, 1000})
.setOnlyAlertOnce(true)
.setContentIntent(pendingIntent);
// A customized design for the notification can be
// set only for Android versions 4.1 and above. Thus
// condition for the same is checked here.
if (Build.VERSION.SDK_INT
>= Build.VERSION_CODES.JELLY_BEAN) {
Log.d(TAG, "Higher Version: ");
builder = builder.setContent(
getCustomDesign(title, message));
if (bitmap != null) {
builder.setLargeIcon(bitmap)
.setStyle(
new NotificationCompat.BigPictureStyle()
.bigPicture(bitmap)
.bigLargeIcon(null)
.setBigContentTitle(title)
.setSummaryText(message)
);
}
} // If Android Version is lower than Jelly Beans,
// customized layout cannot be used and thus the
// layout is set as follows
else {
Log.d(TAG, "Lower Version: ");
builder = builder.setContentTitle(title)
.setContentText(message)
.setSmallIcon(R.drawable.app_icon);
}
// Create an object of NotificationManager class to
// notify the
// user of events that happen in the background.
NotificationManager notificationManager
= (NotificationManager) getSystemService(
Context.NOTIFICATION_SERVICE);
// Check if the Android Version is greater than Oreo
if (Build.VERSION.SDK_INT
>= Build.VERSION_CODES.O) {
NotificationChannel notificationChannel
= new NotificationChannel(
channel_id, "web_app",
NotificationManager.IMPORTANCE_HIGH);
notificationManager.createNotificationChannel(
notificationChannel);
}
notificationManager.notify(0, builder.build());
}
getCustomDesign() can be defined as:
private RemoteViews getCustomDesign(String title,
String message) {
#SuppressLint("RemoteViewLayout") RemoteViews remoteViews =
new RemoteViews(getApplicationContext().getPackageName(),
R.layout.notification);
remoteViews.setTextViewText(R.id.title, title);
remoteViews.setTextViewText(R.id.message, message);
remoteViews.setTextViewText(R.id.note_button, "Reply");
remoteViews.setImageViewResource(R.id.icon, R.drawable.app_icon);
//remoteViews.setImageViewResource(R.id.message_image, R.drawable.app_icon);
return remoteViews;
}
Related
References :
https://azure.microsoft.com/en-us/blog/iot-hub-message-routing-now-with-routing-on-message-body/
How to send a json object instead of a string with Azure Client SDK
I can send IOT messages successfully and store these messages in Azure storage.
On reviewing the messages, the body is BASE64 encoded. I want the JSON within the body in the message to be shown as a string so I can process the contents in ADF.
I have added to my message the properties as below, as suggested by the reference posts above:
mes.ContentEncoding = "utf-8";
mes.ContentType = "application/json";
On doing this, I get the message below when I try and send the message. The app compiles ok, it's only when my app sends the message.
Method not found: 'Void Microsoft.Azure.Devices.Client.Message.set_ContentEncoding(System.String)'.
Comment the lines out, and it works fine again.
What am I doing wrong?
private async void SendDeviceToCloudMessagesAsyncDcbIot(DcbIotPayload dataPayload)
{
if (string.IsNullOrWhiteSpace(dataPayload.did))
{
DpmIotLog.LogToFile("no deviceId in data payload", DpmIotLog.LogCondition.High);
return;
}
dataPayload.t = DateTime.Now.ToString();
var messageString = JsonConvert.SerializeObject(dataPayload);
var messageSize = Encoding.ASCII.GetByteCount(messageString);
DpmIotLog.LogToFile($"Message Len {messageSize}", DpmIotLog.LogCondition.Verbose);
DpmIotLog.LogToFile($"Message {messageString}", DpmIotLog.LogCondition.Verbose);
byte[] messageBytes = Encoding.UTF8.GetBytes(messageString);
using (var mes = new Message(messageBytes))
{
// Set message body type and content encoding.
mes.ContentEncoding = "utf-8";
mes.ContentType = "application/json";
try
{
if (messageSize > 7000)
{
var ex = new Exception("PayloadSizeException");
DpmIotLog.LogToFile("payload over 7000 bytes", ex, DpmIotLog.LogCondition.High);
throw ex;
}
DpmIotLog.LogToFile($"Submitting Message to HUB", DpmIotLog.LogCondition.Verbose);
Task t = deviceClient.SendEventAsync(mes);
if (await Task.WhenAny(t, Task.Delay(10000)) == t)
{
MessageSent?.Invoke(true);
DpmIotLog.LogToFile($"Message Sent", DpmIotLog.LogCondition.Verbose);
deviceClient.Dispose();
}
else
{
MessageSent?.Invoke(false);
DpmIotLog.LogToFile($"Message Fail", DpmIotLog.LogCondition.Verbose);
deviceClient.Dispose();
}
}
catch (DeviceNotFoundException)
{
DpmIotLog.LogToFile($"invalid or disabled device ID : {dataPayload.did}", DpmIotLog.LogCondition.Critical);
}
catch (DeviceDisabledException)
{
DpmIotLog.LogToFile($"IOT disabled device ID : {dataPayload.did}", DpmIotLog.LogCondition.Critical);
}
catch (Exception ex)
{
DpmIotLog.LogToFile("task to upload payload to server failure", ex, DpmIotLog.LogCondition.Critical);
}
}
}
I created an entirely new project and updated all the dependences to the latest versions. This resolved the issue. (I was using a old IOT project as a starting point)
I need to create a form in which the user has to fill it and to send it. So i have to create a submit button that calls another method but i couldn't find the link between the submit action and the call to another method.
The script of my form is :
public Attachment CreateAdaptiveCardwithEntry()
{
var submitActionData = JObject.Parse("{ \"Type\": \"SaveFunction\" }");
var card = new AdaptiveCard()
{
Body = new List<CardElement>()
{
// Hotels Search form
new TextBlock() { Text = "Titre de la note des frais" },
new TextInput()
{
Id = "titre",
Speak = "<s>Veuillez saisir le titre</s>",
Placeholder = "Veuillez saisir le titre",
Style = TextInputStyle.Text
},
},
Actions = new List<ActionBase>()
{
new SubmitAction()
{
DataJson = submitActionData.ToString()
}
}
};
The script of my card is :
var replyMessage = context.MakeMessage();
replyMessage.Attachments = new List<Attachment> { FraisDialog.CreateAdaptiveCardwithEntry() };
await context.PostAsync(replyMessage, CancellationToken.None);
context.Wait(MessageReceived);
the script in MessageReceivedAsync is :
public virtual async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> result)
{
var message = await result;
if (message.Value != null)
{
// Got an Action Submit
dynamic value = message.Value;
string submitType = value.Type.ToString();
switch (submitType)
{
case "SaveFunction":
await context.PostAsync("Please complete all the search parameters:\n");
return;
}
}
}
In this example i need to send the information with the Id = "titre" and pprocess it afterwards, i don't know how to send it(DataJson ?) and where(MessageReceivedAsync ?). Can someone help me ? do i need to create another dialog just for the card ?
Ps : all this code is in rootDialog.
i'm not getting the message 'Please complete all the search parameters'
If all of your code is in RootDialog then please use context.Wait(MessageReceivedAsync); after sending your attachment.
i need to send the information with the Id = "titre" and process it afterwards
When clicking the submit button, the form data is send to MessageReceived method as usual. If you want to just access the fields in the adaptive card you can access the dynamic variable value. Here is an example.
var message = await result;
if (message.Value != null)
{
// Got an Action Submit
dynamic value = message.Value;
string submitType = value.Type.ToString();
switch (submitType)
{
case "SaveFunction":
if(value.titre == "")
{
await context.PostAsync("Please complete all the search parameters:\n");
}
else
{
await context.PostAsync($"You entered {value.titre}");
}
return;
}
}
I'm trying to create an app that can use the camera for Windows Phone 8.1, using the Windows RT/XAML development model.
When I try to call either of the capture methods off of the MediaCapture class I get an ArgumentException with the message "The parameter is incorrect." Here is my code
private async Task Initialize()
{
if (!DesignMode.DesignModeEnabled)
{
await _mediaCaptureMgr.InitializeAsync();
ViewFinder.Source = _mediaCaptureMgr;
await _mediaCaptureMgr.StartPreviewAsync();
}
}
private async void ViewFinder_OnTapped(object sender, TappedRoutedEventArgs e)
{
ImageEncodingProperties imageProperties = ImageEncodingProperties.CreateJpeg();
var stream = new InMemoryRandomAccessStream();
await _mediaCaptureMgr.CapturePhotoToStreamAsync(imageProperties, stream);
_bitmap = new WriteableBitmap((int) ViewFinder.ActualWidth, (int) ViewFinder.ActualHeight);
stream.Seek(0);
await _bitmap.SetSourceAsync(stream);
PreviewImage.Source = _bitmap;
PreviewElements.Visibility = Visibility.Visible;
ViewFinder.Visibility = Visibility.Collapsed;
Buttons.Visibility = Visibility.Visible;
Message.Visibility = Visibility.Collapsed;
stream.Seek(0);
var buffer = new global::Windows.Storage.Streams.Buffer((uint) stream.Size);
stream.ReadAsync(buffer, (uint) stream.Size, InputStreamOptions.None);
DataContext = buffer.ToArray();
if (PhotoCaptured != null)
PhotoCaptured(this, null);
}
The initialize method is called on page load, and the viewfinder_ontapped is called when they tap the CaptureElement I have in the xaml. The error is thrown on
await _mediaCaptureMgr.CapturePhotoToStreamAsync(imageProperties, stream);
What's really bizarre is that I downloaded the latest source for the winrt xaml toolkit http://winrtxamltoolkit.codeplex.com/ and tried their sample camera app, which uses similar code. It throws the same error on MediaCapture.CapturePhotoToStorageFileAsync(). Can anyone help me identify why?
I am working on a Windows Universal App. I Want to get the Data from a Bluetooth Device to the Windows Phone. I am Using the Concept of RFCommCommunicationTrigger for this Purpose.
Here's the code Snippet I am Using
var rfTrigger = new RfcommConnectionTrigger();
// Specify what the service ID is
rfTrigger.InboundConnection.LocalServiceId = RfcommServiceId.FromUuid(new Guid("<some_base_guid>"));
//Register RFComm trigger
var rfReg = RegisterTaskOnce(
"HWRFCommTrigger",
"BackgroundLibrary.RFBackgroundTask",
rfTrigger, null
);
SetCompletedOnce(rfReg, OnTaskCompleted);
Here the Function of RegisterTaskOnce
static private IBackgroundTaskRegistration RegisterTaskOnce(string taskName, string entryPoint, IBackgroundTrigger trigger, params IBackgroundCondition[] conditions)
{
// Validate
if (string.IsNullOrEmpty(taskName)) throw new ArgumentException("taskName");
if (string.IsNullOrEmpty(entryPoint)) throw new ArgumentException("entryPoint");
if (trigger == null) throw new ArgumentNullException("trigger");
// Look to see if the name is already registered
var existingReg = (from reg in BackgroundTaskRegistration.AllTasks
where reg.Value.Name == taskName
select reg.Value).FirstOrDefault();
Debug.WriteLine("Background task "+ taskName+" is already running in the Background");
// If already registered, just return the existing registration
if (existingReg != null)
{
return existingReg;
}
// Create the builder
var builder = new BackgroundTaskBuilder();
builder.TaskEntryPoint = entryPoint;
builder.Name = taskName;
builder.SetTrigger(trigger);
// Conditions?
if (conditions != null)
{
foreach (var condition in conditions)
{
builder.AddCondition(condition);
}
}
// Register
return builder.Register();
}
Here's the code for SetCompletedOnce this will add a Handler only once
static private void SetCompletedOnce(IBackgroundTaskRegistration reg, BackgroundTaskCompletedEventHandler handler)
{
// Validate
if (reg == null) throw new ArgumentNullException("reg");
if (handler == null) throw new ArgumentNullException("handler");
// Unsubscribe in case already subscribed
reg.Completed -= handler;
// Subscribe
reg.Completed += handler;
}
I have also Written the BackgroundLibrary.RFBackgroundTask.cs
public sealed class RFBackgroundTask : IBackgroundTask
{
public async void Run(IBackgroundTaskInstance taskInstance)
{
BackgroundTaskDeferral deferral = taskInstance.GetDeferral();
try
{
Debug.WriteLine(taskInstance.TriggerDetails.GetType());
taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);
Debug.WriteLine("RFComm Task Running");
Debug.WriteLine(taskInstance.TriggerDetails.GetType().ToString());
}
catch (System.Exception e)
{
Debug.WriteLine("RFComm Task Error: {0}", e.Message);
}
deferral.Complete();
}
}
The Run Method is Invoked Every Time The Device tries to Open the Connection.
The type of the Trigger that is obtained (the type I am debugging in the run method of the RFBackgroundTask.cs) is printed as
Windows.Devices.Bluetooth.Background.RfcommConnectionTriggerDetails
But I am Unable use that because I dont have this Class in the BackgroundLibrary project.
The Documentation says that this Provides information about the Bluetooth device that caused this trigger to fire.
It has Variables like Socket,RemoteDevice etc.
I think I am Missing something very simple
Can you please help me out .
Once your background task is launched, simply cast the TriggerDetails object to an RfcommConnectionTriggerDetails object:
public sealed class RFBackgroundTask : IBackgroundTask
{
public async void Run(IBackgroundTaskInstance taskInstance)
{
BackgroundTaskDeferral deferral = taskInstance.GetDeferral();
try
{
taskInstance.Canceled += new BackgroundTaskCanceledEventHandler(OnCanceled);
RfcommConnectionTriggerDetails details = (RfcommConnectionTriggerDetails)taskInstance.TriggerDetails;
StreamSocket = details.Socket; // Rfcomm Socket
// Access other properties...
}
catch (System.Exception e)
{
Debug.WriteLine("RFComm Task Error: {0}", e.Message);
}
deferral.Complete();
}
}
I am using notification hub to register my windows phone device for push notification service.Once if I register my device and again if I register my device for push notification I am getting two notifications means a single device is registering for two time.Can anyone please tell me how to prevent a user to register for more than once.
My code is as:
public static async Task SetupPushNotifications()
{
await RegisterWithNotificationHub();
}
private static HttpNotificationChannel CreateHttpNotificationChannel(string channelName)
{
var httpChannel = HttpNotificationChannel.Find(channelName);
#endregion
return httpChannel;
}
private static async Task RegisterWithNotificationHub()
{
try
{
// requesting a channel from MPNS
App.NotificationChannel = CreateHttpNotificationChannel("");
App.ClientHub = new NotificationHub(
"",""
);
var storedTagsForUser = await GetRegistrationsTagsFromBackEnd();
await RegisterTemplateNotificationWithNotificationHub(storedTagsForUser);
}
catch (Exception exc)
{
Debug.WriteLine(exc);
}
}
private static async Task RegisterTemplateNotificationWithNotificationHub(IEnumerable<string> tags)
{
var toastMessageTemplate =
"<?xml version=\"1.0\" encoding=\"utf-8\"?>" +
"<wp:Notification xmlns:wp=\"WPNotification\">" +
"<wp:Toast>" +
"<wp:Text1>$(oppTitleValue)</wp:Text1>" +
"<wp:Text2>$(myToastMessage)</wp:Text2>" +
"<wp:Param>$(pageToOpen)</wp:Param>" +
"</wp:Toast>" +
"</wp:Notification>";
try
{
await App.ClientHub.RegisterTemplateAsync(
App.NotificationChannel.ChannelUri.AbsoluteUri,
xmlTemplate: toastMessageTemplate,
templateName: TemplateRegistrationName,
tags: tags);
}
catch (Exception exc)
{
Debug.WriteLine("Error registering template notification with notification hubs: " + exc);
}
}
You can check if the channel already exists by calling HttpNotificationChannel.Find(channelName). It will return null if it doesn't.
So you would only want to create a channel if it doesnt't already exist. For example
private void RegisterPushChannel()
{
HttpNotificationChannel currentChannel = HttpNotificationChannel.Find("MyPushChannel");
if (currentChannel == null)
{
currentChannel = new HttpNotificationChannel("MyPushChannel");
currentChannel.Open();
currentChannel.BindToShellTile();
currentChannel.BindToShellToast();
}
}