I am creating a BloodBank app in which, when the user requests for the blood, It takes the requested Blood group and search the same in the database. It displays list of all the users who can donate to that blood group.
In the list, I have already implemented an option to message and call the user. Additionally, I want the App to send a notification to all users who have the same blood group.
For achieving this I have subscribed the user to a topic at successful login and sent him a notification but I have done this through the console.
What I want to achieve is, as a user requests the blood and while showing him the list of all users who can donate, App should also send a notification to all the users who have subscribed to that topic.
So is there any possible way I can programmatically send FCM to all the users subscribed to the same topic.
Here I'm subscribing user to a topic at successful Login:
firebaseAuth.signInWithEmailAndPassword(email, password).addOnCompleteListener(new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
progressDialog.dismiss();
topicSubscription();
} else {
progressDialog.dismiss();
String exception = task.getException().getMessage();
HelperClass.showSnakbarMsg(rootView, exception);
}
}
});
}
private void topicSubscription() {
FirebaseMessaging.getInstance().subscribeToTopic("Blood")
.addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
String msg = getString(R.string.msg_subscribed);
if (!task.isSuccessful()) {
msg = getString(R.string.msg_subscribe_failed);
} else {
startActivity(new Intent(LoginActivity.this, MainActivity.class));
finish();
}
Log.d("log", msg);
Toast.makeText(LoginActivity.this, msg, Toast.LENGTH_SHORT).show();
}
});
This is my Firebase messaging class:
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private static final String TAG = "MyFirebaseMsgService";
/**
* Called when message is received.
*
* #param remoteMessage Object representing the message received from Firebase Cloud Messaging.
*/
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
// TODO(developer): Handle FCM messages here.
Log.d(TAG, "From: " + remoteMessage.getFrom());
if (remoteMessage.getData().size() > 0) {
Log.d(TAG, "Message data payload: " + remoteMessage.getData());
}
if (remoteMessage.getNotification() != null) {
Log.d(TAG, "Message Notification Body: " + remoteMessage.getNotification().getBody());
}
}
I have read about it and many have said to hit an API for this to send FCM programmatically. But I am creating the whole app in firebase so my DataBase is also in firebase and as my database is in firebase I can't use API for one notification only like I have to manage some table in DB for that notification and for only one table I have to manage a separate DB.
So is there any way that I can send FCM programmatically to all users who have subscribed to the same topic, on successful loading of the donor list which shows the user who can donate to the requested blood group.
Thanks
You can directly send push notification directly from android, to all the devices subscribed to the topic, check out the following link how to send msgs directly from android, but in this example user is sending message one to one, to send fcm message to user subscribed to a topic, you need to change the message format as specified by fcm documentation
User App
private void latLngNotification() {
Location loc1 = new Location("");
loc1.setLatitude(Double.parseDouble(userLat));
//loc1.setLongitude();
Location loc2 = new Location("");
loc2.setLatitude(Double.parseDouble(attendanceLat));
//loc2.setLongitude();
float distanceInMeters = loc1.distanceTo(loc2);
if (distanceInMeters > 50) {
//Toast.makeText(this, "distance: " + distanceInMeters, Toast.LENGTH_SHORT).show();
sendNotification();
} else {
//Toast.makeText(this, "distance: " + distanceInMeters, Toast.LENGTH_SHORT).show();
Toast.makeText(this, "You are in home...", Toast.LENGTH_SHORT).show();
}
}
private void sendNotification() {
String TOPIC = "/topics/admin_app"; //topic has to match what the receiver subscribed to
JSONObject notification = new JSONObject();
JSONObject notifcationBody = new JSONObject();
String title = "Quarantine outside";
String message = mobileno + " User is out of his area";
try {
notifcationBody.put("title", title);
notifcationBody.put("message", message);
notification.put("to", TOPIC);
notification.put("priority", "high");
notification.put("data", notifcationBody);
} catch (JSONException e) {
Log.e(TAG, "onCreate: " + e.getMessage());
}
Notification(notification);
}
private void Notification(JSONObject notification) {
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest("https://fcm.googleapis.com/fcm/send", notification,
response -> Log.i(TAG, "onResponse: " + response.toString()),
error -> {
Toast.makeText(GetLocationActivity.this, "Request error", Toast.LENGTH_LONG).show();
Log.i(TAG, "onErrorResponse: Didn't work");
}) {
#Override
public Map<String, String> getHeaders() {
Map<String, String> params = new HashMap<>();
params.put("Authorization", "key=AAAAwxBqu5A:APA91bERpf-1rh02jLciILt1rsLv7HRrFVulMTEAJXJ5l_JGrSHf96qXvLQV0bIROob9e3xLK4VN8tWo-zBPUL39HjxyW4MsX5nKW_NiQlZGgLDCySVwHXADlg16mpLUjgASj--bk-_W");
params.put("Content-Type", "application/json");
return params;
}
};
MySingleton.getInstance(getApplicationContext()).addToRequestQueue(jsonObjectRequest);
}
Admin App
FirebaseMessaging.getInstance().subscribeToTopic("admin_app");
Intent intent = getIntent();
if (intent != null) {
String userPhone = intent.getStringExtra("message");
//Toast.makeText(this, userPhone, Toast.LENGTH_SHORT).show();
message_txt.setVisibility(View.VISIBLE);
message_txt.setText(userPhone);
} else {
message_txt.setVisibility(View.GONE);
//Toast.makeText(this, "no data", Toast.LENGTH_SHORT).show();
}
Related
I am building an app that requires users to fill in the details for the sign-up screen.. once they fill in all the details and verification of details then I allow them to enter their email ID. once the email id is validated (1. blank email 2. invalid email id 3. existing email id check through Firebase) then the app will CREATE email for the user (using email password).. CREATION OF EMAIL has 3 phases.
A. Email creation itself and
B. Sending verification email to the user.
C. Waiting until the user verifies the email.
upon verification of email, I am allowing the user to enter the mobile number. On filling the mobile validation of it will happen (1. blank mobile 2. invalid mobile No 3. existing mobile check through Firebase). if validation turns positive I am asking them to verify the mobile through OTP (new activity). after OTP verification, Finally, I am storing all the user's data into firebase (except the password).
My actual problem lies here. I want users to log in through the phone OTP process or email password process. currently, I am unable to link the email and phone of the user as firebase is considering as 2 accounts. on google, I came to know that I had to link using the LinkWithCredential option. however, it's not working...
Here is my VerifyOTP codes where phone authentication happening...
public class VerifyOTP extends AppCompatActivity {
#BindView(R.id.PinViewUser)
PinView PinViewUser;
#BindView(R.id.btnVerify)
Button btnVerify;
#BindView(R.id.btnGetOTP)
Button btnGetOTP;
#BindView(R.id.btnResend)
Button btnResend;
#BindView(R.id.txtCodeVerificationText)
TextView txtCodeVerificationText;
#BindView(R.id.Verificationlabel)
TextView Verificationlabel;
#BindView(R.id.txtNoteToUser)
TextView txtNoteToUser;
FirebaseAuth mAuth;
AuthCredential emailCredential;
String UserPhoneNo, CodeBySystem, PhNoThroughSignUp, PhNoThroughForgotPassword, ForwardEmailToNextClass, GetCredEmail, GetCredPass;
PhoneAuthProvider.ForceResendingToken mResendToken;
private static final String TAG = "AnonymousAuth";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
RunAppOnFullScreenMode();
setContentView(R.layout.activity_verify_o_t_p);
ButterKnife.bind(this);
//get Ph No & Other Details from SignupLayout
PhNoThroughSignUp = getIntent().getStringExtra(ReUsuableCode.CustomMobileNo);
GetCredEmail = getIntent().getStringExtra("CredEmail");
GetCredPass = getIntent().getStringExtra("CredPass");
emailCredential = EmailAuthProvider.getCredential(GetCredEmail, GetCredPass);
//get Ph No from ForgotPassword
PhNoThroughForgotPassword = getIntent().getStringExtra("FPMobile");
if (PhNoThroughSignUp == null) {
UserPhoneNo = PhNoThroughForgotPassword;
} else {
UserPhoneNo = PhNoThroughSignUp;
}
Toast.makeText(this, "Email: " + GetCredEmail + "Password: " + GetCredPass + "Phone: " + UserPhoneNo, Toast.LENGTH_LONG).show();// this is working
txtCodeVerificationText.setText("Click On Get OTP Button to Get the OTP on the Mentioned Phone No\n\n Note: Standard SMS charges May Apply");
Log.d(TAG, "DetailsCollected");
// Initialize Firebase Auth
mAuth = FirebaseAuth.getInstance();
//Send OTPCode to User
btnGetOTP.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
SendVerificationCodeToUser(UserPhoneNo);
Log.d(TAG, "Verification Code Sent");
txtNoteToUser.setVisibility(View.VISIBLE);
txtCodeVerificationText.setText("Enter the One Time Password received on\n " + UserPhoneNo);
Toast.makeText(VerifyOTP.this, "OTP Initiated!! Wait For Auto Verification of OTP", Toast.LENGTH_SHORT).show();
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
btnVerify.setVisibility(View.VISIBLE);
}
}, 10000); // where 1000 is equal to 1 sec (1 * 1000)
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
btnResend.setVisibility(View.VISIBLE);
}
}, 60000); // where 1000 is equal to 1 sec (1 * 1000)
}
});
//ReSend OTPCode to User
btnResend.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
resendVerificationCode(UserPhoneNo, mResendToken);
txtCodeVerificationText.setText("Resent verification code to " + UserPhoneNo + "\n\n Note: Standard SMS charges May Apply");
btnResend.setVisibility(View.INVISIBLE);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
btnResend.setVisibility(View.VISIBLE);
}
}, 60000); // where 1000 is equal to 1 sec (1 * 1000)
}
});
//Manual Click on VerifyButton
btnVerify.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String code = PinViewUser.getText().toString();
if (ReUsuableCode.fieldisEmpty(code)) {
PinViewUser.setError("Field is Empty");
} else {
VerifyCode(CodeBySystem, code);
}
}
});
}
//======================================= Other Private Codes==================
private void SendVerificationCodeToUser(String phNo) {
PhoneAuthOptions options =
PhoneAuthOptions.newBuilder(mAuth)
.setPhoneNumber(phNo) // Phone number to verify
.setTimeout(60L, TimeUnit.SECONDS) // Timeout and unit
.setActivity(this) // Activity (for callback binding)
.setCallbacks(mCallbacks) // OnVerificationStateChangedCallbacks
.build();
PhoneAuthProvider.verifyPhoneNumber(options);
}
public void linkCredential(AuthCredential credential) {
mAuth.getCurrentUser().linkWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "linkWithCredential:success");
FirebaseUser user = task.getResult().getUser();
Toast.makeText(VerifyOTP.this, "linkWithCredential:success", Toast.LENGTH_SHORT).show();
if (PhNoThroughSignUp == null) {
Intent GoToAnotherLayout1 = new Intent(VerifyOTP.this, SetPassword.class);
ForwardEmailToNextClass = getIntent().getStringExtra("FEmail");
GoToAnotherLayout1.putExtra("FEmail", ForwardEmailToNextClass);
startActivity(GoToAnotherLayout1);
finish();
} else {
Intent GoToAnotherLayout1 = new Intent(VerifyOTP.this, Retailer_SignUP.class);
GoToAnotherLayout1.putExtra(ReUsuableCode.MobileRegistration, "Mobile Validation Successful");
setResult(RESULT_OK, GoToAnotherLayout1);
finish();
}
} else {
Log.w(TAG, "linkWithCredential:failure", task.getException());
Toast.makeText(VerifyOTP.this, "linkWithCredential:failure" + task.getException().toString(), Toast.LENGTH_SHORT).show();
}
}
});
}
private PhoneAuthProvider.OnVerificationStateChangedCallbacks mCallbacks =
new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
#Override
public void onCodeSent(#NonNull String s, #NonNull PhoneAuthProvider.ForceResendingToken forceResendingToken) {
super.onCodeSent(s, forceResendingToken);
// Save verification ID and resending token so we can use them later
CodeBySystem = s;
mResendToken = forceResendingToken;
Log.d(TAG, "OnCode Completed");
}
#Override
public void onVerificationCompleted(#NonNull PhoneAuthCredential phoneAuthCredential) {
Log.d(TAG, "On Verification start");
Toast.makeText(VerifyOTP.this, "OTP Verification Successful", Toast.LENGTH_SHORT).show();
String code = phoneAuthCredential.getSmsCode();
if (code != null) {
PinViewUser.setText(code);
VerifyCode(CodeBySystem, code);
Log.d(TAG, "Pre Link State");
linkCredential(emailCredential);
Log.d(TAG, "Post Link State");
}
}
#Override
public void onVerificationFailed(#NonNull FirebaseException e) {
Log.d(TAG, "Verification Failed");
if (e instanceof FirebaseAuthInvalidCredentialsException) {
Toast.makeText(VerifyOTP.this, "Failed to Verify!! Try After Some Time...", Toast.LENGTH_SHORT).show();
return;
} else if (e instanceof FirebaseTooManyRequestsException) {
Toast.makeText(VerifyOTP.this, "Too Many Attempts!! Try After Some Time...", Toast.LENGTH_SHORT).show();
return;
} else {
Toast.makeText(VerifyOTP.this, "Some Other Error Occured...", Toast.LENGTH_SHORT).show();
return;
}
}
};
private void VerifyCode(String verificationId, String code) {
PhoneAuthCredential Credential = PhoneAuthProvider.getCredential(verificationId, code);
Log.d(TAG, "Verified Code");
}
// [START resend_verification]
private void resendVerificationCode(String phoneNumber,
PhoneAuthProvider.ForceResendingToken token) {
PhoneAuthOptions options =
PhoneAuthOptions.newBuilder(mAuth)
.setPhoneNumber(phoneNumber) // Phone number to verify
.setTimeout(60L, TimeUnit.SECONDS) // Timeout and unit
.setActivity(this) // Activity (for callback binding)
.setCallbacks(mCallbacks) // OnVerificationStateChangedCallbacks
.setForceResendingToken(token) // ForceResendingToken from callbacks
.build();
PhoneAuthProvider.verifyPhoneNumber(options);
}
here are my error logs:
Please do guide me where I am missing. How to do I make a single account for the user?
I was able to get an answer to my code. Here is the website which I referred to fix the code: Learning Path for Linking Phone and Email
Steps to Link with 2nd Auth Provider of Firebase:
As per my scenario, I am first making the user enter an email ID and hence I make the user to first SIGN IN using email ID and password, which is nothing but,
Creation of Account with Email and Password
Sending Verification email (Optional step)
Waiting until User validates email ID (Optional Step)
Signing in the User using first Auth Provider (EmailAuthProvider).
After the signing process is complete. I made the user enter a mobile number so that he can proceed to validate the Ph No. In this follow the below process
Send a verification code to the user's phone
In the callbacks method of onVerificationCompleted. instead of regular signing-in with phAuth provider you have to use Linkwithcredentials(phcredentials)
which is:
a)
PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, code);
b)
linkCredential(PhCredential);
If you are using the manual verification of OTP then link the 2a and 2b step to verifybtn as well.
public void linkCredential(AuthCredential credential) {
mAuth.getCurrentUser().linkWithCredential(credential)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
Log.d(TAG, "linkWithCredential:success");
} else {
Log.w(TAG, "linkWithCredential:failure", task.getException());
Toast.makeText(VerifyOTP.this, "linkWithCredential:failure" + task.getException().toString(), Toast.LENGTH_SHORT).show();
}
}
});
I have two apps that use FCM, Client and Worker app. Client is sending the message:
String jsonLatLng = new Gson().toJson(new LatLng(Common.placeLatLng.latitude, Common.placeLatLng.longitude));
String clientToken = FirebaseInstanceId.getInstance().getToken();
Notification notification = new Notification(clientToken, jsonLatLng);
Sender content = new Sender(tokenId, notification);
mFCMService.sendMessage(content)
.enqueue(new Callback<FCMResponse>() {
#Override
public void onResponse(Call<FCMResponse> call, Response<FCMResponse> response) {
if(response.body().success == 1) {
Toast.makeText(HomeActivity.this, "Request sent.", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(HomeActivity.this, "Request not sent.", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<FCMResponse> call, Throwable t) {
}
});
Wherein Notification.java is
public class Notification {
public String title;
public String body;
...
}
Sender.java is
public class Sender {
public String to;
public Notification notification;
...
}
And with the Worker app, it receives:
public class MyFirebaseMessaging extends FirebaseMessagingService {
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
//Convert lat lng
LatLng clientLocation = new Gson().fromJson(remoteMessage.getNotification().getBody(), LatLng.class);
Intent intent = new Intent(getBaseContext(), NotificationActivity.class);
intent.putExtra("lat", clientLocation.latitude);
intent.putExtra("lng", clientLocation.longitude);
intent.putExtra("client", remoteMessage.getNotification().getTitle());
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
}
These codes work perfectly fine, however, I need to add more details, specifically, I want to send data from 2 String variables, serviceFee & serviceType over to the Worker app. I tried modifying the body of the Notification wherein I created a class called Body with three variables (jsonLatLng, serviceFee, serviceType), but I can't figure out how the worker will be able to get the data of Body or if that's even possible. Please help. Thank you! :)
FCM Service. NotificationDatabaseHandler is helper class. Saving message title and current time.
public class ApplicationFCMService extends FirebaseMessagingService {
private static final String TAG = ApplicationFCMService.class.getName();
private NotificationUtils notificationUtils;
private NotificationDatabaseHandler databaseHandler;
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
super.onMessageReceived(remoteMessage);
Log.e(TAG, "From: " + remoteMessage.getFrom());
databaseHandler = new NotificationDatabaseHandler(getApplicationContext());
if (remoteMessage.getNotification() != null) {
Log.e(TAG, "Notification Body: " + remoteMessage.getNotification().getBody());
handleNotification(remoteMessage.getFrom(), remoteMessage.getNotification().getBody());
databaseHandler.addNotification(remoteMessage.getNotification().getBody(), getDate());
// Log.d("FCM", messagesSet.toString());
Log.d("FCM", remoteMessage.getNotification().getBody());
}
// Check if message contains a data payload.
if (remoteMessage.getData().size() > 0) {
Log.e(TAG, "Data Payload: " + remoteMessage.getData().toString());
try {
JSONObject json = new JSONObject(remoteMessage.getData().toString());
handleDataMessage(json);
} catch (Exception e) {
Log.e(TAG, "Exception: " + e.getMessage());
}
}
}
I've used postman to send data notifications(for testing). On reception of data message onMessageReceived() method is executed.
Now I can save notifications in database even if the app is in background.
When sending upstream message most of the times the message does not get to my server, and even when the message received to the server the onMessageSent(String msgId) function isn't called (the onMessageReceived(RemoteMessage fcmMessage) work very well).
Why the function isn't called and why do I need to send 10 upstream messages to get response from the firebase cloud messaging to my server?
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String sendTo = SENDER_ID + "#gcm.googleapis.com";
RemoteMessage.Builder data = new RemoteMessage.Builder(sendTo);
data.addData("Hello", "World");
try {
for (int i = 0; i < 10; i++) {
Thread.sleep(1000);
String messageID = getRandomString();
data.setMessageId(messageID);
Logger.d(TAG, "messageID: " + messageID);
FirebaseMessaging.getInstance().send(data.build());
}
} catch (Exception e) {
Logger.e(TAG, "Error sending upstream message: " + e.getMessage());
return "Error sending upstream message:" + e.getMessage();
}
return null;
}
#Override
protected void onPostExecute(String result) {
if (result != null) {
Logger.e(TAG, "send message failed: " + result);
}
}
}.execute(null, null, null);
}
Found out the problem!!!
The problem was on the server side.
Every time I send a message to the app (android), I started a new connection to the gcm server, when maintaining a continuous connection it worked great.
For the problem with the onMessageSent not called it was because, before you send the message you need to set time to live (setTtl(Time_in_seconds)) for the message.
RemoteMessage.Builder data = new RemoteMessage.Builder(mSendTo);
data.setMessageId(messageID);
data.setTtl(120);
data.addData("Hello", "World");
FirebaseMessaging.getInstance().send(data.build());
buttonUpstreamEcho.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Log.d(TAG, "Echo Upstream message logic");
String message = editTextEcho.getText().toString();
Log.d(TAG, "Message: " + message + ", recipient: " + token);
FirebaseMessaging.getInstance().send(new RemoteMessage.Builder(FCM_PROJECT_SENDER_ID + FCM_SERVER_CONNECTION)
.setMessageId(Integer.toString(RANDOM.nextInt()))
.addData("message", message)
.addData("action", BACKEND_ACTION_ECHO)
.build());
// To send a message to other device through the XMPP Server, you should add the
// receiverId and change the action name to BACKEND_ACTION_MESSAGE in the data
}
});
This is a sample Android project to showcase the Firebase Cloud Messaging (FCM) to manage upstream and downstream messages.
https://github.com/carlosCharz/FCMTest
This is the video in youtube that explains what it does.
https://www.youtube.com/watch?v=SEzOKSoAMG0
Hope you find it useful.
Using the builder pattern - it is always best to chain your calls to the setter methods. So my suggestion, and based on some working examples such as this one here, would be to change your code into something like this (note that I got rid of the for-loop - you can put it back if you need it, I don't see why - perhaps you were testing out?:
new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String sendTo = SENDER_ID + "#gcm.googleapis.com";
String messageID = getRandomString();
try {
FirebaseMessaging.getInstance().send(new RemoteMessage.Builder(sendTo)
.setMessageId(messageID)
.addData("my_message", "Hello, World")
.build());
} catch (Exception e) {
Logger.e(TAG, "Error sending upstream message: " + e.getMessage());
return "Error sending upstream message:" + e.getMessage();
}
return null;
}
#Override
protected void onPostExecute(String result) {
if (result != null) {
Logger.e(TAG, "send message failed: " + result);
}
}
}.execute(null, null, null);
}
I hope this helps - try it out and let me know if it works or what errors you are getting.
I have created an application that implements app to app calling using Sinch. It works only when the caller knows the name of the recipient.
To overcome this Sinch suggested to use PubNub to get the user state. They also have a tutorial here. The problem is that tutorial is old and PubNub has updated their API since. I tried to implement the functionality using their new API on my own using their docs, but it is not working or more accurately I don't know how to do it.
My current code is:
public class LoggedUsers extends Activity {
private PubNub pubNub;
String name;
private ArrayList users;
private JSONArray loggedUserList;
ListView UserList;
TextView allUsers;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.user_list);
allUsers = (TextView) findViewById(R.id.JSONFromPubNub);
SharedPreferences sp = getSharedPreferences("User_Details", MODE_APPEND);
try {
name = sp.getString("UserName", "");
} catch (NullPointerException e) {
}
final PNConfiguration pnc = new PNConfiguration();
pnc.setPublishKey("publish key");
pnc.setSubscribeKey("subscribe key");
pnc.setUuid(name);
pubNub = new PubNub(pnc);
users = new ArrayList<String>();
UserList = (ListView) findViewById(R.id.listView);
String user = getUserStatus();
allUsers.setText(user);
final ArrayAdapter adapter = new ArrayAdapter(getApplicationContext(), R.layout.single_item_list, users);
UserList.setAdapter(adapter);
pubNub.addListener(new SubscribeCallback() {
#Override
public void status(PubNub pubnub, PNStatus status) {
if (status.getCategory() == PNStatusCategory.PNUnexpectedDisconnectCategory) {
// This event happens when radio / connectivity is lost
HashMap <String,String> map = new HashMap();
map.put("State","Offline");
pubNub.setPresenceState().channels(Arrays.asList("CallingChannel1")).state(map).uuid(pnc.getUuid());
} else if (status.getCategory() == PNStatusCategory.PNConnectedCategory) {
// Connect event. You can do stuff like publish, and know you'll get it.
// Or just use the connected event to confirm you are subscribed for
// UI / internal notifications, etc
HashMap <String,String> map = new HashMap();
map.put("State","Online");
pubNub.setPresenceState().channels(Arrays.asList("CallingChannel1")).state(map).uuid(pnc.getUuid());
/* if (status.getCategory() == PNStatusCategory.PNConnectedCategory) {
pubnub.publish().channel("awesomeChannel").message("hello!!").async(new PNCallback<PNPublishResult>() {
#Override
public void onResponse(PNPublishResult result, PNStatus status) {
// Check whether request successfully completed or not.
if (!status.isError()) {
// Message successfully published to specified channel.
}
// Request processing failed.
else {
// Handle message publish error. Check 'category' property to find out possible issue
// because of which request did fail.
//
// Request can be resent using: [status retry];
}
}
});
}*/
} else if (status.getCategory() == PNStatusCategory.PNReconnectedCategory) {
HashMap <String,String> map = new HashMap();
map.put("State","Online");
pubNub.setPresenceState().channels(Arrays.asList("CallingChannel1")).state(map).uuid(pnc.getUuid());
// Happens as part of our regular operation. This event happens when
// radio / connectivity is lost, then regained.
} else if (status.getCategory() == PNStatusCategory.PNDecryptionErrorCategory) {
// Handle messsage decryption error. Probably client configured to
// encrypt messages and on live data feed it received plain text.
}
}
#Override
public void message(PubNub pubnub, PNMessageResult message) {
}
#Override
public void presence(PubNub pubnub, PNPresenceEventResult presence) {
}
});
}
public String getUserStatus(){
final StringBuilder allUsers = new StringBuilder();
pubNub.subscribe().channels(Arrays.asList("CallingChannel1")).withPresence().execute();
pubNub.hereNow()
// tailor the next two lines to example
.channels(Arrays.asList("CallingChannel1"))
.includeState(true)
.includeUUIDs(true)
.async(new PNCallback<PNHereNowResult>() {
#Override
public void onResponse(PNHereNowResult result, PNStatus status) {
if (status.isError()) {
// handle error
return;
}
for (PNHereNowChannelData channelData : result.getChannels().values()) {
allUsers.append("---");
allUsers.append("channel:" + channelData.getChannelName());
allUsers.append("occoupancy: " + channelData.getOccupancy());
allUsers.append("occupants:");
for (PNHereNowOccupantData occupant : channelData.getOccupants()) {
allUsers.append("uuid: " + occupant.getUuid() + " state: " + occupant.getState());
}
}
}
});
return allUsers.toString();
}
#Override
protected void onResume() {
super.onResume();
}
}
Here are my problems:
I am trying to display all the data that I receive in a textview (later it will arranged in a listview or a recycler view) but I am getting a blank screen so I am getting null from the server.
The user status should be constantly updated to know if the user changes state (online -> offline) but there seems to be no async calls made in the code so I think it will be executed only once and then the dataset is not being changed.
How can I solve my problems?
PubNub Presence
You can monitor online and state changes using PubNub Presence. When you subscribe, subscribe with presence enabled and you will get state-change, join, leave & timeout events in the presence callback.
Callback callback = new Callback() {
#Override
public void successCallback(String channel, Object message) {
System.out.println(channel + " : "
+ message.getClass() + " : " + message.toString());
// take action on the presence events here
}
#Override
public void connectCallback(String channel, Object message) {
System.out.println("CONNECT on channel:" + channel
+ " : " + message.getClass() + " : "
+ message.toString());
}
#Override
public void disconnectCallback(String channel, Object message) {
System.out.println("DISCONNECT on channel:" + channel
+ " : " + message.getClass() + " : "
+ message.toString());
}
#Override
public void reconnectCallback(String channel, Object message) {
System.out.println("RECONNECT on channel:" + channel
+ " : " + message.getClass() + " : "
+ message.toString());
}
#Override
public void errorCallback(String channel, PubnubError error) {
System.out.println("ERROR on channel " + channel
+ " : " + error.toString());
}
};
try {
pubnub.presence("my_channel", callback);
}
catch (PubnubException e) {
System.out.println(e.toString());
}
It appears Sinch is using a rather old version of the PubNub Android SDK. I would think you could still use PubNub Android SDK v4 to do what you need to do outside of Sinch SDK unless there is some explicit requirements by Sinch to use the same version of the SDK.