Nearby Message API Does not subscribe to Beacon Message. What am I supposed to do?
I finished testing whether "Nearby Message" works in Android 7.0 and Android 9.0. And I referenced this document(https://developers.google.com/nearby/messages/android/get-beacon-messages) but, it is still not working.
listener = object: MessageListener() {
override fun onFound(msg: Message) {
Log.i(TAG, "Found: $msg")
}
override fun onLost(msg: Message) {
Log.i(TAG, "Lost: $msg")
}
}
val options = SubscribeOptions.Builder()
.setStrategy(Strategy.BLE_ONLY)
.build()
Nearby.getMessagesClient(this).subscribe(listener, options)
Nearby Message is subscribed to, but beacon message is not.
For messages, it needs both to be in Nearby with Manifest ...
<meta-data
android:name="com.google.android.nearby.messages.API_KEY"
android:value="Your-Key-from-Google Developer Console." />
Please follow this NearbyDevices getting-started .
or another from the same GitHub repo
Here, API Key is the "key"-thing !
e.g. Your-Key-from-Google Developer Console
Related
I'm developing one app that uses Twilio for video calls and chats. The problem I'm having is that onParticipantAdded is never triggered, even though I successfully register ConversationListener. I know that I've successfully registered ConversationListener, because my onMessageAdded is always triggered when expected, but onParticipantAdded (from the same listener) is never called.
From the code perspective, there is not much to share, I have ConversationListener defined as
private val conversationListener: ConversationListener = object : ConversationListener {
override fun onMessageAdded(message: Message?) {
Timber.d("Message added")
}
override fun onMessageUpdated(message: Message, updateReason: Message.UpdateReason?) {
Timber.d("Message updated: " + message.messageBody)
}
override fun onMessageDeleted(message: Message?) {
Timber.d("Message deleted")
}
override fun onParticipantAdded(participant: Participant) {
Timber.d("Participant added: " + participant.identity)
}
...
I register this listener with
conversation.addListener(conversationListener)
In my logs, I see "Message added" every time someone sends a message, but "Participant added" is never printed in logs, even though participants are leaving and entering the conversation.
Any ideas of what can be the problem here?
Expected behavior
I want to have a callback to listen to every topic I subscribe to just once per message sent. I mean, I want to subscribe to a topic 1000 times, but when a message is received, I want to listen to it just one time.
IDK if there is something I am doing wrong (I guess).
Actual behavior
I am developing a home security camera app.
I have a list of cameras that I own.
For every camera on the list, I subscribe to a topic.
Every 30s, I update the screen, and again I subscribe to a topic for every camera. This means a TOPIC could be subscribed many times.
Every time I receive a message on a topic, the callback fires messages about how many times the same topi was subscribed.
To Reproduce
Steps
haven a topic camera/123
subscribe the topic N times with the below method called subscribeWith
Send a message over camera/123
You will receive the message N times because the N time you subscribed to the topic
Reproducer code
Just variables
private var mqtt: Mqtt5AsyncClient? = null
private var username: String? = null
private var password: String? = null
private val serverHost: String,
private val serverPort: Int = 1883
Build the MQTT
private fun build() {
if (mqtt != null) return
mqtt = Mqtt5Client.builder()
.identifier(identifier())
.serverHost(serverHost)
.serverPort(serverPort)
.automaticReconnect()
.applyAutomaticReconnect()
.addConnectedListener { Timber.d("On Connected") }
.addDisconnectedListener { onMQTTDisconnected(it) }
.buildAsync()
}
Connecting the MQTT
fun connect(username: String, password: String) {
build()
this.username = username
this.password = password
mqtt?.connectWith()
?.keepAlive(30)
?.sessionExpiryInterval(7200)
?.cleanStart(false)
?.simpleAuth()
?.username("abc")
?.password("123".toByteArray())
?.applySimpleAuth()
?.send()
}
And then, subscribing a topic
Every time I subscribe a topic I use these fun
fun subscribeWith(topic: String) {
mqtt?.subscribeWith()
?.topicFilter(topic)
?.qos(MqttQos.AT_MOST_ONCE)
?.callback { t -> onConsumingTopic(t) } <- I THINK THIS IS THE IMPORTANT THING
?.send()
?.whenComplete { ack, error -> onTopicConnected(ack, error, topic) }
}
As mentioned in the comments, the only solution at the moment is to keep a list of the subscribed topics outside the MQTT client library and check it before subscribing to new topics.
I found the correct answer.
There is no need to register a callback for every subscribe call nor using a global array handling the registered topics as this:
mqtt?.subscribeWith()
?.callback { t -> onConsumingTopic(t) } <- This is not needed
Instead you could register one "global" callback for all messages, for example:
client.publishes(MqttGlobalPublishFilter.SUBSCRIBED, publish -> { ... } );
and then you can subscribe without providing a callback.
client.subscribeWith().topicFilter("test").qos(MqttQos.AT_LEAST_ONCE).send();
Complete example:
Building the MQTT
mqtt = Mqtt5Client.builder()
.identifier(identifier())
.serverHost(serverHost)
.serverPort(serverPort)
.automaticReconnect()
.applyAutomaticReconnect()
.addConnectedListener { onMQTTConnected(it) }
.addDisconnectedListener { onMQTTDisconnected(it) }
.buildAsync()
mqtt?.publishes(MqttGlobalPublishFilter.SUBSCRIBED) { onConsumingTopic(it) }
Connecting the MQTT
mqtt?.connectWith()
?.keepAlive(30)
?.sessionExpiryInterval(7200)
?.cleanStart(false)
?.simpleAuth()
?.username(context.getString(R.string.mqtt_user))
?.password(context.getString(R.string.mqtt_pw).toByteArray())
?.applySimpleAuth()
?.send()
Subscribing the topic
mqtt?.subscribeWith()
?.topicFilter(topic)
?.qos(MqttQos.AT_LEAST_ONCE)
?.send()
?.whenComplete { ack, error -> onTopicSubscribed(ack, error, topic) }
I am trying to test google nearby connections API by writing a simple, single class android program. Unfortunately I am not able to connect to physical devices, after starting discovering and advertising.
All code is located within single class. App is written with Kotlin by almost directly copying the code from API website and translating it from Java to Kotlin.
I don't know where to look for a problem. Logs doesn't seem to show any obvious results.
I kept one phone advertising for more than a couple of minutes and the other one discovering meanwhile. Bluetooth is on, and both devices are in one WiFi network with Internet access. I use Star Strategy.
EndpointDiscoveryCallback is never called.
Here are my implementations of startDiscovery and endpointCallback
private fun startClient() {
connClient.startDiscovery(deviceId, endpointDiscoveryCallback, discOptions)
.addOnSuccessListener {
Toast.makeText(
applicationContext,
"Discovering started",
Toast.LENGTH_SHORT
).show()
currentStateTextView.text = "discovering"
}.addOnFailureListener { exception ->
Toast.makeText(
applicationContext,
"Discovery failed, exception thrown: ${exception.message}.",
Toast.LENGTH_SHORT
).show()
}
}
private val endpointDiscoveryCallback = object : EndpointDiscoveryCallback() {
override fun onEndpointFound(endpointId: String, info: DiscoveredEndpointInfo) {
connClient.requestConnection(deviceId, endpointId, connectionLifecycleCallback)
.addOnSuccessListener {
}
.addOnFailureListener {
}
}
override fun onEndpointLost(endpointId: String) {
Toast.makeText(applicationContext, "Endpoint: $endpointId lost!", Toast.LENGTH_SHORT)
.show()
}
}
I succeeded to connect the same decives using sample app "Rock, Paper, Scissors" which is based on the same API. Unfortunately, till now i cannot find the clue why the devices wont find each other with my code, and where to look for a hint.
I have been stuck for two days in a very strange issue in Watson Assistant
I have made some code to be able to use it in my app, it keeps giving me 401 authentications error
these are the three services that I have configured in my IBM cloud account
and here is my code to initialize the three service
private fun initSpeechToTextService(): SpeechToText {
var options = IamOptions.Builder()
.apiKey(getString(R.string.speech_text_iam_apikey))
.url(getString(R.string.speech_text_url))
.build()
return SpeechToText(options)
}
private fun initTextToSpeechService(): TextToSpeech {
var options = IamOptions.Builder()
.apiKey(getString(R.string.text_speech_iam_apikey))
.url(getString(R.string.text_speech_url))
.build()
return TextToSpeech(options)
}
private fun initAssistantService(): Assistant {
var options = IamOptions.Builder()
.apiKey(getString(R.string.watson_assistant_iam_apikey))
.url(getString(R.string.watson_assistant_url))
.build()
var service = Assistant("2019-07-04", options)
service.endPoint = getString(R.string.watson_assistant_url)
return service
}
my problem now that I always get status: 401, error: Unauthorized
can anyone please tell me what is missing?
EDIT
here are the urls that I am using
https://gateway-lon.watsonplatform.net/assistant/api
https://gateway-lon.watsonplatform.net/speech-to-text/api
https://gateway-lon.watsonplatform.net/text-to-speech/api
EDIT2
here is a screenshot of my debugger windows
The service version for your Assistant is set to 2019-07-04 which is invalid. Check here for the latest and previous versions. Does this work?
var service = Assistant("2019-02-28", options)
service.endPoint = getString(R.string.watson_assistant_url)
I am setting sharedPreferences value on my fragment (Kotlin) , then I would like to use this value on my FirebaseMessagingService (Java). When I set value and destroy app and open again, there isn't any problem on my fragment. I can see the set value. So I am sure that sharedPreferences value updated by my fragment. But when I try to use that value on FirebaseMessagingService, I am getting always default value.
Here how I am setting on kotlin class:
sharedPref = activity?.getSharedPreferences("com.xxx.xxx.xxx",Context.MODE_PRIVATE)!!
private fun sharedPrefWrite(boolean: Boolean){
with (sharedPref?.edit()) {
this!!.putBoolean("notf", boolean)
apply()
}
}
This working good.
And here is how I am getting this data on FirebaseMessagingService:
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
preferences = getApplicationContext().getSharedPreferences("com.xxx.xxx.xxx",Context.MODE_PRIVATE);
if(preferences.getBoolean("notf",true))
sendNotification(remoteMessage.getNotification().getBody());
}
and always service sending notification.
And I didn't start this service on my activity, It is only under Application tag on Manifest.xml like that;
<service android:name="com.xxx.xxx.xxx.newsFirebaseMessagingService">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
Any suggestion about why I couldn't get this value ? Thanks
EDIT:
I just did couple debug and when application on resume (on foreground), firebase service getting value correctly.. But If application on background (onPause) or if application destroyed, service can't fetch correct data from SharedPreferences.
EDIT 2
I removed onMessageReceived function from my FirebaseMessagingService, and re-install that app to my device, and when App on destroyed, I got the notification even there is no 'onMessageReceived ' ...
LAST EDIT
Solution below
I had a similar problem, with not everything from Android api working correctly in my FirebaseMessagingService.
I figured this can have something to do with limitations of running background services from Oreo and higher, and FCM messages being of exception to those
https://developer.android.com/about/versions/oreo/background#services
I guess you only got this problem on Android>=8, right?
So what I did now, is two pieces of code, for pre-Oreo and Oreo-and-newer respectively, running from my onMessageReceived
#Override
public void onMessageReceived(RemoteMessage remoteMessage) {
val body = remoteMessage.getNotification().getBody()
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
scheduleJobService(body);
} else {
runIntentService(body);
}
}
then in runIntentService I just start an Intent service (I guess you know how do do that), but this can work only pre-Oreo, because of the mentioned limitations
on Android 8 and higher you would need to schedule a JobService or implement your own custom BroadcastReceiver...
I choose JobService, because it was easier for me, and I didn't mind waiting sometimes a bit for Android to schedule my Job, what I did is:
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun scheduleNotificationJobService(body: String) {
val serviceName = ComponentName(getPackageName(),
NotificationJobService::class.java.name)
val scheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
val notificationInfoBundle = PersistableBundle()
notificationInfoBundle.putString(Constants.EXTRA_NOTIF_BODY, body)
val builder = JobInfo.Builder(JOB_ID, serviceName)
.setOverrideDeadline(0)
.setExtras(notificationInfoBundle)
val notificationInfo = builder.build()
scheduler.schedule(notificationInfo)
}
then your JobService would kook like this:
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
class NotificationJobService : JobService() {
override fun onStopJob(params: JobParameters?): Boolean {}
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun onStartJob(params: JobParameters?): Boolean {
params?.extras?.let {
val body = it.getString(Constants.EXTRA_NOTIF_BODY)
val preferences = getApplicationContext().getSharedPreferences("com.xxx.xxx.xxx",Context.MODE_PRIVATE)
if(preferences.getBoolean("notf",true)){
sendNotification(body);
}
}
return false
}
}
remember do declare it in AndroidManifest.xml
<service android:name=".push.NotificationJobService"
android:permission="android.permission.BIND_JOB_SERVICE">
</service>
let me know pls if that works for you too :)
cheers
I solved the problem finally..
There are two types of messages in FCM (Firebase Cloud Messaging):
Display Messages: These messages trigger the onMessageReceived() callback only when your app is in foreground
Data Messages: Theses messages trigger the onMessageReceived() callback even if your app is in foreground/background/killed
So I have to post Data message for my app. Normally it is done something like that;
POST https://fcm.googleapis.com/fcm/send
Headers
Key: Content-Type, Value: application/json
Key: Authorization, Value: key=<your-server-key>
Body using topics
{
"to": "/topics/my_topic",
"data": {
"my_custom_key": "my_custom_value",
"my_custom_key2": true
}
}
BUT This no longer works (Error 401).. There will be Auth problem for this case beacuse FCM now using OAUTH 2
So I read firebase documentation and according to documentation new way to post data message is;
POST: https://fcm.googleapis.com/v1/projects/YOUR_FIREBASEDB_ID/messages:send
Headers
Key: Content-Type, Value: application/json
Auth
Bearer YOUR_TOKEN
Body
{
"message":{
"topic" : "xxx",
"data" : {
"body" : "This is a Firebase Cloud Messaging Topic Message!",
"title" : "FCM Message"
}
}
}
In the url there is Database Id which you can find it on your firebase console. (Go project setttings)
And now lets take our token (It will valid only 1 hr):
First in the Firebase console, open Settings > Service Accounts.
Click Generate New Private Key, securely store the JSON file containing the key. Iwas need this JSON file to authorize server requests manually. I downloaded it.
Then I create a node.js project and used this function to get my token;
var PROJECT_ID = 'YOUR_PROJECT_ID';
var HOST = 'fcm.googleapis.com';
var PATH = '/v1/projects/' + PROJECT_ID + '/messages:send';
var MESSAGING_SCOPE = 'https://www.googleapis.com/auth/firebase.messaging';
var SCOPES = [MESSAGING_SCOPE];
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
getAccessToken().then(function(accessToken) {
console.log("TOKEN: "+accessToken)
})
});
function getAccessToken() {
return new Promise(function(resolve, reject) {
var key = require('./YOUR_DOWNLOADED_JSON_FILE.json');
var jwtClient = new google.auth.JWT(
key.client_email,
null,
key.private_key,
SCOPES,
null
);
jwtClient.authorize(function(err, tokens) {
if (err) {
reject(err);
return;
}
resolve(tokens.access_token);
});
});
}
Now I an use this token in my post request. Then I post my data message, and it is now handled by my apps onMessageReceived function.