Firebase Cloud Function : Send notification to all user using sendToDevice() - android

I am already using Cloud Messaging feature in Firebase and utilized the sendToTopic() legacy method of sending notification to all subscribers but I've seen a limitation using topic in my app. Now I want to manage my way of sending and receiving notification by sending notification to each device using the registered device token in user's document which stored as map object. I will iterate to each device token and use sendToDevice() to send notification to each device.
I have now a function lets call it new_added that triggers whenever new document is added in a collection. Now every time new_added function gets called, this will iterate to each document in Users collection and write a new document under Notification collection. The structure would be this Users (collection) > uid (document) > Notifications > doc. Every new added item under Notification collection will trigger a function in server. This operation is too heavy specially if there is a million number of users, does this kind of operation can be perform in server side using Cloud Function within 540 seconds which is said to be the maximum runtime of a function after gets trigger? I really want it to work this way. Is there any tool that will help to minimize the operation?

If sounds like you're trying to implement your own FCM messaging fan-out, sending the same message to many users. This is similar to what FCM topic messaging already does, to I'd definitely consider using that for the moment.
But if you want to implement it yourself, consider how to optimize it for the underlying system. Since you're storing the tokens in Firestore, the number of documents you read is a key factor in the cost.
A way to reduce the number of documents you read could be to store the tokens for a group of devices into a single document. For example, you could create a document for each "topic", and add tokens to that doc as they are written to the database. Then when you need to send a message to all devices for a topic, you simply read that topic-document, and get all tokens in one go. This becomes especially simple if you name the document after the topic for which it contains the tokens, e.g. mytopic-tokens.
The main problem with this approach is that a document can be no bigger than 1Mb. Say that tokens are at most 256 bytes (they seem to be 152-162 characters), you can store 4000 tokens in a document. If you have more tokens, you will need to create multiple documents. A simple naming scheme can go a long way here, such as mytopic-tokens-1, mytopic-tokens-2, etc. You can get all these documents with a single range-query in Firestore.

Related

I want to be notified when data is added in Firebase Realtime Database

I have an application with two different package names. There are separate databases for these two applications. If a user adds new data, I want to receive a message. (For example: Added new data to app A or new data added to app B) I don't want to constantly check. How can I do that?
I can receive notifications as Mail, or I can make a different application and an application that controls the content of these two applications. But I don't know how to make notifications.
To display notification to a user about a change in the data while they are not actively using the app, you'll typically use Firebase Cloud Message or some other out-of-band mechanism to deliver the message.
To detect the relevant data change and send that message, you'll want to run code in a trusted environment that is guaranteed to continue to run all the time. An example of such an environment is Cloud Functions, which also has an example of notifying the user when something interesting happens.
You can use cloud functions to trigger events when there are document changes.
Docs are here : https://firebase.google.com/docs/functions/firestore-events.
Then you can tie those triggers with FCM to get notified when doc changes.

Send notifications to user in android studio

I want to build a notification system in my Android app where I can send a notification to someone upon an event. For example, my app is about a social system where it uses Firestore database (Firebase) to save its users (which means I have a 'users' collection in there), and I have an 'Add as Friend' feature.
Everything works just fine, but I would like to add a feature where when user1 adds user2 as a friend, I would like to send user2 a notification about that.
I have searched in google and saw this post: https://www.geeksforgeeks.org/how-to-push-notification-in-android-using-firebase-cloud-messaging/
Now, from what I understand, this wouldn't help me much cause it only sends the notification from FCM by me (and not to a specific user).
The problem is, I don't really know what to search for (or what to type in google) in order to find what I'm looking for.
Some ideas I thought:
to make a collection in firestore called 'Notifications' where it holds usernames as documentIDs
when user1 sends a friend request to user2, I would add data (like title/message) to 'Notifications/user2/'
But I'm kind of stuck here, I would like to know ideas or if you guys have threads on how do I implement this, I really don't know what should I look for.
Thank you very much and sorry for the long post.
Firebase creates a userID for every user and a usertoken which is a temporary identifier for the device which is connected to this userID. You need to save the usertoken when there is a new one being created in a database(firestore) along with the user with whom you identify this person.
When someone adds this person as a friend you can add a notification in the database that there is a request from person1 to person2.
This change in the database can trigger a cloud function that reacts to changes in the database.
The triggered cloud function sends an FCM to the person which is being added as a friend. This FCM can either be a pure notification or you send data and let your app handle how this data is being handled(saved in a local database or displayed as a custom notification).
Keywords are: FCM, Notification, Firestore, cloud functions, and firebase token
Edit (Answer to 1. comment): Firebase userID and usertoken are created on the device. In the tutorial you implement FCM(I didn't read it complete) and a class called FirebaseMessagingService, from which you can inherit. This class is called, when a message is sent to the device and contains a method called onNewToken. This message is called, when the Firebase Server creates a token for your device and sends it to the device. In this method you might want to send the new token to your database and save it as an identifier for the user of this device. The Tokens are used by firebase to identify devices, to which messages are being sent(You can read about FCM in detail)
Cloud functions are something you can deploy along your firebase project, which get triggered when an event occurs. For example a http call, or a specific change in your database. Cloud functions are written in JS and need to be deployed for your firebase project. (Cloud functions are not for free and you need to pay for each call, but you have some thousand uses for free each month.)
You send a FCM to a specific person by creating a firebase message which is directed to a specific token, tokens or user groups. To identify the person to which you want to send a message you need to have the token for this user, that is why you are saving the tokens from the 1. part in your database and always update the token, when the token changes on the device.
Keep in mind, that a user might use multiple devices. You should have an identifier for you, such as username and along this username you save multiple firebaseIds and Firebase tokens. A single FCM can be directed to up to 100 tokens.

Firebase push notifications for users with specific firestore fields

I'm currently making a chatroom-like app that works with firestore to connect users to a host's session. I ran into a problem where client devices lose connection / stop listening for firestore changes when locked/backgrounded for too long (to my knowledge it's because the device tries to save power by killing long-living processes). I learned that a workaround would be to send a FCM push notification to devices prior to changing the firestore doc so it'd reawaken the app and have the app listen for the firestore doc change. I'm not sure how to implement push notifications, but I learned that it requires a user specific token (that must be protected). Ignoring the fact that I'm not sure how to implement push notifications (noob-friendly pointers would be appreciated), would uploading the user token to firestore as a field be a viable approach? I'm thinking that I can use my helper function (which gets all non-hosts of a specific session) to also send push notifications to each person.
_vibrateAllDevices() async {
var lobbies = await lobby
.where('lobbyCode', isEqualTo: _lobbyCode)
.where('userRole', isEqualTo: 2)
.get();
lobbies.docs.forEach(
(document) async {
document.reference.update(<String, dynamic>{'vibrate': true});
},
);
}
Each lobby is expected to have anywhere from 2-30 people so looping per person doesn't seem to be that big of an issue. Does this seem like a viable approach or is storing tokens to firestore an unsafe? I should also mention that each lobby is expected to only be opened for a short amount of time (~3 hours max) and all information is deleted right after.
Firebase through notification if another subscribe to your own topic make sure that another person subscribes the topic when Firestore field is created. When you trigger notification then automatically receive notification in another person in FirebaseMessages service class in which method onmessagereceive.
There is the same way to trigger a notification in firebase and Firestore because server key always same.
I recommended you to read this documentation:
https://medium.com/mindorks/send-device-to-device-push-notification-using-firebase-cloud-messaging-without-using-external-769476c79ffd

Firestore document sharing without scanning entire collection

I am learning my basics for Firestore and trying to build an app which allows user1 to share a document with user2/3/4 etc.
For billing purposes, every query which results in a document read counts towards the cost. So, I do not want to follow the approach of adding the user2/3/4 etc emails to a 'sharedWith' variable to type: array or map type structure as I believe every user will then have to scan the entire collection and pick the documents where their email appears.
Is there any other approach to this where user1 can programmatically give access to user2/3/4 of one specific document?
For billing purposes, every query which results in a document read counts towards the cost.
That's correct and according to the official documentation regarding Cloud Firestore billing:
There is a minimum charge of one document read for each query that you perform, even if the query returns no results.
So you're also charged with one document read, even if your query does not return any results.
I believe every user will then have to scan the entire collection and pick the documents where there email appears.
That's also correct. So let's assume the email address that you are looking for exist in a document that is appart of 10k collection of documents. So if you query the database only for that particular document, you'll be charged with only one document read and not for those 10k. So you are charged according to the number of items you get back and not to the number of items your request them from. And this is available for the first request, when you get the data from the Firebase servers. If in the meanwhile nothing has changed, second time you get the data from the cache since Firestore has the offline persistence enabled by default. Which means you aren't charged for any other document reads.
Is there any other approach to this where user1 can programatically give access to user2/3/4 of one specific document?
Without writing the data to database, there is not. So you should add the ids or email addresses to the desired documents and perfom a query according to it.

Synchronize Android client and REST server

REST Server
I created a Rails server that contains :users and associated :comments. It is used as a backend API for an Android client. The exchange format to load and store data at the server is JSON. Here are the relevant migrations.
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name
t.timestamps
end
end
end
...
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.references :users
t.string :subject
t.text :message
t.timestamps
end
end
end
All users have already been imported. Therefore, only read access is configured for the :users resource. Thus, for :comments it should be possible to add new entries. Here are the available routes.
user_comments GET /users/:user_id/comments(.:format) comments#index
POST /users/:user_id/comments(.:format) comments#create
new_user_comment GET /users/:user_id/comments/new(.:format) comments#new
user_comment GET /users/:user_id/comments/:id(.:format) comments#show
users GET /users(.:format) users#index
user GET /users/:id(.:format) users#show
Android client
On the client side I use a Service with AsyncTasks to download, parse and store users into a local SQLite database. A ContentProvider delivers cached users to the UI. The user object downloaded from the server contains the unique id of the users table. This should be useful when a new comment gets created on the client.
Scenario 1: Read comments
Users are displayed in a list view on the Android client.
A user item gets selected.
The list activity creates an Intent which contains the user specific URI, e.g. content://com.example.myapp.provider/users/23.
A user activity displays detail information about the user and associated comments.
Cached comments get loaded via a CursorLoader. (1)
A synchronization process loads comments from the remote server. (2)
Scenario 2: Write comments
A comment can be created from the user activity.
The comment gets stored into the local database. (3)
Stored comments are sychronized with the remote server. (2)
Headache questions
I marked the scenario steps that are associated with the following questions.
How do I create a content URI for the comments being used with a CursorLoader in the user activity? Please mind, I only know the user URI at this point.
Can someone describe how I create a synchronization process? I am not sure if a SyncAdapter works here (never used it). Is the the synchronization process just a Service that on the one hand starts tasks to download, parse and store comments on the client and on the other hand loads, encodes and sends out comments to the server?
How does the content URI for a new comment look like? Is the ContentProvider for comments the same as for users? Is there only one SQLiteOpenHelper in the application?
The main problem I am struggling with is how to design the application? If you know of a better solution on how I should synchronize the users and their associated comments, you are very welcome.
Answers
Answers to question 1. and 3.
I extended the REST model as follows: The JSON hash returned for a comment now includes the id of the associated user. The same id is also included in the JSON hash for the user. Both objects are stored into the local database on the Android device. This allows me request comments for a specific user. I simply pass the server user id as WHERE clause. The content URI for comments is not cascaded as I implied with my question. It is similar to the user content URI:
content://com.example.myapp.provider.commentsprovider/comments
Note, that I changed the authority part of the string. I decided to create separate content provider for users and comments.
One simple architecture is to always update things in the server first, sending posted comments to the server right away and from there pushing a notification through GCM to users that should request the updated comments list. The flow would look like:
When the app is open send GCM registration id to your push notification server (say uniqush-push, or your own server using a gem to handle the GCM logic), this way you can use it to send push notifications to the user telling the app to update the comments from the server
Build your initial cache as you want it
Whenever a user posts a comment, send it to the server and make the server respond with the data for the created comment, so the app can use that and already cache it if it wants, using the returned id and whatever else
On the server, when a comment is posted, loop through all the concerned users and using the GCM registration id send it a push notification, it could be as simple as having just "update_comments": "1"
On the app when the push notification is tapped by the user, update the comments cache with a request to the server

Categories

Resources