android - instant notification from another mobile phone. how? - android

hi i have this awesome app that can work both for clients and business owners, so lets say a business owner is connected on one end and a client on another , the client can place an order of some kind (not revealing too much :D ) and what i want is the business owner to know the client just placed a new order, considering that both have the application. can someone point me on how to ?
i am working with Firebase Real Time Database i handle it pretty good , but what happens when the app is closed and the client just placed an order , it needs to pop up on the business owner side , i haven't tried notifications yet but i suppose its not to hard to understand i just want the business owner app to be triggered when an order is placed.
Thank you for your time.

You can use a background service, I actually did something like you need with Firebase, we need to inform some users that other user pressed a panic button. So here's a sample code of what I did, you'll need a sticky service and a firebase listener, I don't know if the current version works the same as I did but here's what you could use, or at least I can give you an idea of how to do what you need.
public class DummyService extends Service implements ValueEventListener, ChildEventListener {
private Query mRef;
public static final String FIREBASE_URL = "https://your-firebase-url.firebaseio.com/";
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
// some stuff else you need to do
Firebase.setAndroidContext(this);
// this will be your main ref, it'll hear everything on your firebase db
Firebase mFirebaseRoot = new Firebase(DummyService.FIREBASE_URL);
mRef = mFirebaseRoot;
// if you want to hear an specific query you could use something like I used
// you can read the docs at firebase web
//mRef = mFirebaseRoot.child("your_child);
mRef.addListenerForSingleValueEvent(this);
mRef.addChildEventListener(this);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
// read the docs of how this listeners work
if (something) {
notifyUser();
}
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {}
#Override
public void onDataChange(DataSnapshot dataSnapshot) {}
#Override
public void onCancelled(FirebaseError firebaseError) {}
}
And you could use a BroadcastReceiver to catch when the device starts and fire your service, so this way user won't need to open the app and it'll always be running.
Hope this will be useful, sorry for the code sample indents, I don't why looks like this way haha.

Related

Restart scheduled notifications (that require Firebase data) after device reboot

My app currently schedules notifications for tasks at specific times (chosen by the user). The task's title and reminder date is saved in the Firebase Realtime database.
The point is, when the device reboots, all of that is lost. So it is required to restart all the relevant notifications that were scheduled in the past.
Therefore, I have a BroadcastReceiver that can get the BOOT_COMPLETED intent.
However, I can't access Firebase DB, because no user is currently logged in - and I also can't start Firebase Authentication (startActivityForResult isn't recognized and importing it causes more errors). I assume that's because the BroadcastReceiver isn't an activity.
I'm wondering if there is a way around that.
I currently tried setting an intent to start the MainActivity (where the user is authenticated) and then perform my relevant tasks, however that did not work.
I'm wondering if there is a way around that, to get the authenticated user (or authenticate him on device reboot) and then get the data from Firebase.
AlarmReceiver code (extends BroadcastReceiver) - it's a bit of a mess but it helps get context.
public class AlarmReceiver extends BroadcastReceiver {
private ChildEventListener mChildEventListener;
private FirebaseDatabase mFirebaseDatabase;
private DatabaseReference mTaskDatabaseReference;
private FirebaseAuth mFirebaseAuth;
private FirebaseAuth.AuthStateListener mAuthStateListener;
private int i;
#Override
public void onReceive(final Context context, Intent intent) {
mFirebaseDatabase = FirebaseDatabase.getInstance();
mFirebaseAuth = FirebaseAuth.getInstance();
if (intent.getAction() != null && context != null) {
if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
TaskInfoFragment.showReminderNotification(context, MainActivity.class, "hoooo",1000);
onSignedOutCleanup(context);
mTaskDatabaseReference=mFirebaseDatabase.getReference().child("users").child(MainActivity.getCurrentUserId());
attachDatabaseReadListener(context);
//TODO - HANDLE DEVICE REBOOT
}
}
//Trigger the notification
Bundle extras = intent.getExtras();
String taskTitle = "Error, no task title!";
int taskIntId=-1;
if (extras != null) {
taskTitle = extras.getString("taskTitle");
taskIntId=extras.getInt("taskIntId");
}
TaskInfoFragment.showReminderNotification(context, MainActivity.class, taskTitle,taskIntId);
}
private void attachDatabaseReadListener(final Context context) {
i=0;
mChildEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
i++;
TaskList task = dataSnapshot.getValue(TaskList.class);
TaskInfoFragment.showReminderNotification(context, MainActivity.class, task.getTitle(),i);
Log.d("here is another task","title: "+task.getTitle());
}
public void onChildChanged(DataSnapshot dataSnapshot, String s) {}
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
public void onChildMoved(DataSnapshot dataSnapshot, String s) {}
public void onCancelled(DatabaseError databaseError) {}
};
mTaskDatabaseReference.addChildEventListener(mChildEventListener);
}
private void onSignedInInitialize(final String userId,Context context) {
//Get reference for the task list for the logged in user and attach the database listener
mTaskDatabaseReference=mFirebaseDatabase.getReference().child("users").child(userId);
attachDatabaseReadListener(context);
}
private void onSignedOutCleanup(Context context) {
Intent taskIntent = new Intent(context,MainActivity.class);
// Send the intent to launch a new activity
context.startActivity(taskIntent);
}
private void detachDatabaseReadListener() {
if (mChildEventListener != null) {
mTaskDatabaseReference.removeEventListener(mChildEventListener);
mChildEventListener = null;
}
}
If you want to display these notifications even before the user has logged in, then clearly they are not meant to be associated with the user. I'd associate them with another value, probably one that identifies the device. The InstanceIdToken that James commented about may be a good solution for that, since it identifies a specific app on a specific device.
But definitely also check out Firebase Cloud Messaging, which allows you to deliver notifications to a device right after it has booted. Since FCM messages are handled by a receiver in Google Play Services, this is typically a more reliable way to deliver such messages, than by trying to load them from a remote database in your own receiver.
Not coincidentally: FCM relies on Instance ID tokens to identify the device/app to deliver a message to.
So in conclusion, I saved the current logged in user using SharedPreferences.
(It works because the notifications are user related, so we can safely assume the last user that logged in is the relevant one).
Then I changed my Firebase DB tree to allow me to access the Tasks using the current logged in user ID. A little more coding and bug fixing and it's a go.
Thanks for all the help!

Proper way of handling messages in chat

I have recently added a chat feature to my app.
I am storing the messages and their timestamps in Firebase database.
I am trying to figure out a way of displaying (in the current chat room) only the last (let's say) 60 messages. This would mean retrieving only the last 60 items of the current conversation from Firebase using limitToLast().
Furthermore I would like to add a Load more button that would appear only when the 60 messages limit has been reached by swiping up and it should load another 60.
Is there a proper way of handling the message archive as I stated above?
Using the code below I actually retrieve the whole specific message archive. I find this ineffective when the specific chat archive has thousands of messages.
mChildEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
ChatMessage chatMessage = dataSnapshot.getValue(ChatMessage.class);
mMessageAdapter.add(chatMessage);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
mDatabaseReference.child("chat_messages").child(chatId).addChildEventListener(mChildEventListener);
To achieve this, I recomand you using the following logic:
pageEndOffset = 0;
pageLimit = 60;
pageEndOffset += pageLimit;
Query query = mDatabaseReference.child("chat_messages")
.orderByChild("chatId").limitToFirst(pageLimit)
.startAt(pageEndOffset);
query.addValueEventListener(YourActivity.this);
Hope it helps.

Pairing in two player game with firebase in android?

I am trying to implement a two player game in android using firebase(for realtime pairing).
On firebase, i have set up a node representing active players. And on each client i have a childEventListener to listen to any changes on the players node.
Suppose
Initially there is only one player (Player A).
Then 3 more players(B,C and D) got added (At the SAME TIMESTAMP).
Then, on client side
1. Each of 4 players will get notified about the changes in the node through childEventListener.
Now, What i want to achieve is "UNIQUE PAIRING" i.e I should be able to generate 2 pairs from these 4 players. (Of course, one player can't be in both the pairs)
I have written code to pair two players in a transaction block so that no two players get paired with the same player.
private void attachActivePlayersEventListener() {
Log.i(TAG, "attachActivePlayersEventListener");
if (activePlayersEventListener == null) {
activePlayersEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
searchPlayer(dataSnapshot);
Log.i("PLAYER_ADDED ", dataSnapshot.getValue(Player.class).getName());
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
searchPlayer(dataSnapshot);
Log.i("PLAYER_CHANGED ", dataSnapshot.getValue(Player.class).getName());
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
mActivePlayersDbRef.addChildEventListener(activePlayersEventListener);
}
}
private void searchPlayer(DataSnapshot dataSnapshot) {
if(mPlayer.getState().equals(PlayerState.ACTIVE)) { // if the current user is active
final String oppKey = dataSnapshot.getKey();
Player oppPlayer = dataSnapshot.getValue(Player.class);
if (oppPlayer.getState().equals(PlayerState.ACTIVE)
&& !oppKey.equals(pushId)) { // if the opponent chosen is not the current user
DatabaseReference oppRef = mActivePlayersDbRef.child(oppKey);
oppRef.runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(MutableData mutableData) {
Player player2 = mutableData.getValue(Player.class);
if (player2 == null) {
return Transaction.success(mutableData);
}
mActivePlayersDbRef.child(pushId).child("state").setValue(PlayerState.PLAYING);
mActivePlayersDbRef.child(oppKey).child("state").setValue(PlayerState.PLAYING);
return Transaction.success(mutableData);
}
#Override
public void onComplete(DatabaseError databaseError, boolean b, DataSnapshot dataSnapshot) {
Log.d("PAIRING", "pairing:onComplete:" + databaseError);
}
});
}
}
}
My DOUBT is
Is this the correct way of pairing?
What happens when PlayerA tries to pair up with PlayerB, PlayerB tries to pair up with PlayerC and PlayerC tries to pair up with PlayerD and finally PlayerD tries to pair up with PlayerA(In a CYCLIC way). How to make sure this case doesn't happen?
Note that in Doubt2, I am not taking care of the condition where two players try to pair up with the same player. (As transaction block will ensure that it won't happen, I guess :/ ).
What you are trying to do is difficult to do correctly and safely. It's better to let a backend service do this matching so that the clients don't all have to figure out how to agree with each other somehow.
You can use Cloud Functions for Firebase to write a database trigger that responds to changes in your database. One strategy would be for clients to push data into a location in the database to indicate their intent to be matched. Then, when a function triggers on those writes, it can check to see if there are other suitable players to be matched, and write more data into the database to set up the game. The clients will also need a way to listen for the game starting up after they have been matched. This is still all very much non-trivial, but a lot easier than putting the logic in the clients.

Social network - Follow functionality using firebase database

I am building a social network like Instagram. I have worked on many social networks before, using mySQL. I am new to firebase server.
I want to search users via name and want to show the follow/following button on the list view. But I am little confused to run android firebase queries in a standard format. I do not want to run unnecessary loops. Below is my database structure for the follow table.
Node name is follow and follower_id refers to the user who is following the user and user who gets followed is referred as followed_id.
How to write a simple query using android firebase to show all the users with the name starting (e.g "an") and with the status that I am already following him/her or not.
FYI: I am not using firebase rest API's.
How to write a simple query using android firebase to show all the users with the name starting (e.g "an") and with the status that I am already following him/her or not.
I think you can't do it in a single query.
You could try to do it with nested queries, something like this:
ChildEventListener childEventListener = new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
// Retrieve the userId
User user = dataSnapshot.getValue(User.class);
ValueEventListener valueEventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot usersFollowedDataSnapshot) {
for ( DataSnapshot userFollowedDataSnapshot : usersFollowedDataSnapshot.getChildren() ) {
// Retrieve the follower structure
Follow follow = userFollowedDataSnapshot.getValue(Follow.class);
if ( myUserId == follow.getFollowerId() ) {
// This is a user that I'm following and which name starts with "am"
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
Query secondQuery = mFirebaseDatabaseReference.child("follow").orderByChild("followedId").equalTo(user.getId());
secondQuery.addListenerForSingleValueEvent(valueEventListener);
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
};
Query firstQuery = mFirebaseDatabaseReference.child("users").orderByChild("name").startAt("am").endAt("am\uf8ff");
firstQuery.addChildEventListener(childEventListener);
I know it's not very straightforward and looks disapponting. It could be also slow, but it worth trying.
Alternatively, you should consider to structure your database in a way to simplify the firebase queries.
See more on firebase queries for example here and here.

Update recyclerview item ui in client to indicate that Firebase Database on server got updated

I'm building a chat app using Firebase Realtime Database. I want to implement the functionality like WhatsApp, where the user can send the message without internet (Add the message object on RecyclerView) but will have the clock icon (To show that the message was not sent yet).
When the Firebase Database receives the message (The user device connects to the internet and Firebase sends the message), I want to update the added element on the recyclerview(change the clock icon for a positive icon).
What have I tried? I have that
mFirebaseDatabaseReference = FirebaseDatabase.getInstance().getReference();
mFirebaseAdapter = new ChatFirebaseAdapter(mFirebaseDatabaseReference.child("chat_id"), user.getId());
recyclerChat.setLayoutManager(mLinearLayoutManager);
recyclerChat.setAdapter(mFirebaseAdapter);
My adapter extends FirebaseRecyclerAdapter from firebase-ui-database
I tried to add a addChildEventListener on my DatabaseReference where I get a dataSnapshot of the new element, but I don't know how to get the right position on recyclerview.
mFirebaseDatabaseReference.addChildEventListener(new ChildEventListener() {
#Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
UPDATE RECYCLERVIEW ITEM
}
#Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
#Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
I added an onCompletionListener when I add a new message, but I don't know how get the right position again.
mFirebaseDatabaseReference.push().setValue(message, new DatabaseReference.CompletionListener() {
#Override
public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
if (databaseError == null) {
UPDATE RECYCLERVIEW ITEM
}
}
});
Does someone have an idea why I cant accomplish that? Thanks. I search a lot before asked, but I didn't find the right answer.
The solution to your problem lies in adding a delivered boolean which can be set to true and once you receive successful push id from your code in onCompletionListener, then all you need to do is set this boolean to that particular push id.
Now on your android client in your firebase ui you can check if this boolean is set to true and change the state of recycler view items accordingly. This solution will work because firebase database is real-time.
//setting boolean onComplete
mFirebaseDatabaseReference.push().setValue(message, new DatabaseReference.CompletionListener() { #Override public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) { if (databaseError == null) { databaseReference.child("delivered").setValue(true); } } });
For chat apps like whatsapp, messenger etc you would take another messaging route if not using firebase as backened. Which I am not discussing here as it is out of scope of asked question.

Categories

Resources