I have probably a simple question but has me stumped. For my Android Wear application, I have two sensors working (step counter and heartrate).The wear app then sends these values back to the mobile application. I am sending them using the Message API. My stepcount sendMessage() and heartrate sendMessage() method look the same. Here is my heartrate sendMessage method.
private void sendMessageToHandheld(final String message) {
if (mGoogleApiClient == null)
return;
Log.d(LOG_TAG,"sending a message to handheld: "+message);
// use the api client to send the heartbeat value to our handheld
final PendingResult<NodeApi.GetConnectedNodesResult> nodes = Wearable.NodeApi.getConnectedNodes(mGoogleApiClient);
nodes.setResultCallback(new ResultCallback<NodeApi.GetConnectedNodesResult>() {
#Override
public void onResult(NodeApi.GetConnectedNodesResult result) {
final List<Node> nodes = result.getNodes();
if (nodes != null) {
for (int i=0; i<nodes.size(); i++) {
final Node node = nodes.get(i);
Wearable.MessageApi.sendMessage(mGoogleApiClient, node.getId(), message, bytes);
}
}
}
});
}
Problem is I am only using one messageReceived method on the mobile. So I cant differentiate from the step value coming in or the heartrate value coming in.
#Override
public void onMessageReceived(MessageEvent messageEvent) {
super.onMessageReceived(messageEvent);
Log.d(LOG_TAG, "received a message from wear: " + messageEvent.getPath());
if (messageEvent.getPath().contains("HEARTBEAT")){
// save the new heartbeat value
currentValue = Integer.parseInt(messageEvent.getPath());
if(handler!=null) {
// if a handler is registered, send the value as new message
Log.d(LOG_TAG, "received a heartbeat message from wear: " + messageEvent.getPath());
handler.sendEmptyMessage(currentValue);
}
}
else {
// save the new steps value
currentStepValue = Integer.parseInt(messageEvent.getPath());
if (handler != null) {
// if a handler is registered, send the value as new message
Log.d(LOG_TAG, "received a step message from wear: " + messageEvent.getPath());
handler.sendEmptyMessage(currentStepValue);
}
}
I tried passing in a byte array into the Heartrate sendMessage() and other strings as flags so that I could tell the values apart but no luck. Anyone know what the best way to go about this would be?
Any help would be appreciated.
Cheers
It seems you are sending the data inside the path attribute. This is not the correct use of this parameter.
Let's take a look at the MessageApi.sendMessage(GoogleApiClient client, String nodeId, String path, byte[] data method.
What you want to do is use String path to provide identifier for your message, for example in your case it would be step_counter and heartbeat. This way you can identify it on the other side, when you receive the message.
The sensor data should go into data field. You can put it raw there, but a better way is to create a DataMap and then serialize it into byte[]. This way you can enrich the data later easily.
Related
I have created an android application that listens for incoming sms. The issue i am encountering is that it also reads previous sms. The goal of the app was to grab sms from a specific originating address and store it in a database.
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
Log.i(TAG, "Intent Received: "+intent.getAction());
if (intent.getAction()==SMS_RECEIVED){
Bundle dataBundle = intent.getExtras();
if(dataBundle != null){
//creating PDU protocol Data unit object which is a protocol for transferring message
Object[] mypdu = (Object[])dataBundle.get("pdus");
final SmsMessage[] message = new SmsMessage[mypdu.length];
for(int i =0; i< mypdu.length; i++){
//for build version >= API
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
String format = dataBundle.getString("format");
//From PDU we get all object and smsMessage using following line of code
message[i] = SmsMessage.createFromPdu((byte[])mypdu[i],format);
}else{
message[i] = SmsMessage.createFromPdu((byte[]) mypdu[i]);
}
msg += message[i].getMessageBody().toString().replace("null","");
originatingAddress = message[i].getOriginatingAddress();
}
msg = msg.replace("null","");
if(originatingAddress.trim().equals("MPESA")) {
Toast.makeText(context.getApplicationContext(), "message: " + msg, Toast.LENGTH_SHORT).show();
}
}
}
// throw new UnsupportedOperationException("Not yet implemented");
}
}
Please try with below way
(1)The Simple way you can achieve to store the unique of record on every SMS is timestamp is only unique in this case whenever you get SMS store timestamp of every Sms as a Unique or primary key as you want in database, like system.currentTimeMillisecond to get time of current SMS and store as LONG type Column in your database,
(2) you can also check with unique time on every SMS get but it is complex to check with every existing records
Hope this process will help in your way with prevent of duplicate record store in database
I have a wearable app. The app after it finishes has data like time/date, UUID, Geo location, parameters selected displayed in front of me like a Data Report or Log in several TextViews underneath each other. Like a list. I want this data to be transferred from my wearable device to my android phone.
Now I have to ask does the WearOS app the pairs the phone with the watch enables such a thing? Like can the data be sent through it? OR what exactly can I do? I read about Sync data items with the Data Layer API in the documentation, but I'm not sure if the code snippets provided would help achieve what I want.
public class MainActivity extends Activity {
private static final String COUNT_KEY = "com.example.key.count";
private DataClient dataClient;
private int count = 0;
...
// Create a data map and put data in it
private void increaseCounter() {
PutDataMapRequest putDataMapReq = PutDataMapRequest.create("/count");
putDataMapReq.getDataMap().putInt(COUNT_KEY, count++);
PutDataRequest putDataReq = putDataMapReq.asPutDataRequest();
Task<DataItem> putDataTask = dataClient.putDataItem(putDataReq);
}
...
}
The data I display in the textviews are called through methods that I call things like: getLocation, getUUID, getDateTime, getSelections, etc... when I click a button I call them in the setOnClickListener. I want this data in the TextViews to be placed in a file or something like that and send them over to the mobile phone from the watch when they're generated.
private void getDateTime()
{
SimpleDateFormat sdf_date = new SimpleDateFormat("dd/MM/yyyy");
SimpleDateFormat sdf_time = new SimpleDateFormat("HH:mm:ss z");
String currentDate= sdf_date.format(new Date());
String currentTime= sdf_time.format(new Date());
textView_date_time.setText("Date: "+currentDate+"\n"+"Time: "+currentTime);
}
#SuppressLint("SetTextI18n")
private void getUUID()
{
// Retrieving the value using its keys the file name
// must be same in both saving and retrieving the data
#SuppressLint("WrongConstant") SharedPreferences sh = getSharedPreferences("UUID_File", MODE_APPEND);
// The value will be default as empty string because for
// the very first time when the app is opened, there is nothing to show
String theUUID = sh.getString(PREF_UNIQUE_ID, uniqueID);
// We can then use the data
textView_UUID.setText("UUID: "+theUUID);
}
#SuppressLint("SetTextI18n")
private void getSelections()
{
textView_data_selected.setText("Tool No.: "+c.getToolNo()+
"\nTool Size: " +c.getToolSizeStr()+
"\nFrom Mode: " +c.getCurrentModeStr()+
"\nGoto Mode: " +c.getModeStr()+
"\nMethod: " +c.getMethodStr()+
"\nBit Duration: " +c.getBitDuration()+
"\nUpper bound" +c.getUpStageValue()+
"\nLower bound: "+c.getDownStageValue());
}
The above are examples of the methods I use to get the data. then I call them here:
gps_btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (Build.VERSION.SDK_INT >= 26) {
getLocation();
getDateTime();
getUUID();
getSelections();
}
else
{
//ActivityCompat.requestPermissions(get_location.this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
Toast.makeText(get_location.this,"Build SDK too low",Toast.LENGTH_SHORT);
}
}
});
Now how do I take all this and send it over from my device to the the phone?
Note: The data report I want to send as a file, I want it done subtly like something done in the background. I don't know what else to do or where to look.
You have two options if you want to use the Data Layer, one is to use the MessageClient API to bundle your data up in a message and send it directly to the handheld. The easiest here would be to create an arbitrary JSONObject and serialize your data as a JSON string you can stuff into a message. For example:
try {
final JSONObject object = new JSONObject();
object.put("heart_rate", (int) event.values[0]);
object.put("timestamp", Instant.now().toString());
new MessageSender("/MessageChannel", object.toString(), getApplicationContext()).start();
} catch (JSONException e) {
Log.e(TAG, "Failed to create JSON object");
}
In my case, I do this in my onSensorChanged implementation, but you can insert this wherever you are updating your text.
MessageSender is just a threaded wrapper around the MessageClient:
import java.util.List;
class MessageSender extends Thread {
private static final String TAG = "MessageSender";
String path;
String message;
Context context;
MessageSender(String path, String message, Context context) {
this.path = path;
this.message = message;
this.context = context;
}
public void run() {
try {
Task<List<Node>> nodeListTask = Wearable.getNodeClient(context.getApplicationContext()).getConnectedNodes();
List<Node> nodes = Tasks.await(nodeListTask);
byte[] payload = message.getBytes();
for (Node node : nodes) {
String nodeId = node.getId();
Task<Integer> sendMessageTask = Wearable.getMessageClient(context).sendMessage(nodeId, this.path, payload);
try {
Tasks.await(sendMessageTask);
} catch (Exception exception) {
// TODO: Implement exception handling
Log.e(TAG, "Exception thrown");
}
}
} catch (Exception exception) {
Log.e(TAG, exception.getMessage());
}
}
}
The other option is to create a nested hierarchy of data items in the Data Layer and implement DataClient.OnDataChangedListener on both sides, such that changes that are written in on one side are automatically synchronized with the other. You can find a good walkthrough on how to do that here.
For your specific case, just packing it in a JSON object would probably be the simplest. The writing out to your preferred file format you can then implement on the handheld side without needing to involve the wear side.
I am creating an instant messaging app in android using smack library and openfire as a server but i cannot implement the feature of the person with whom the current user is talking to. i.e. like when user read the message or when he starts typing.
Is there any way of achieving this using smack or other ?
For knowing which user is current you must implement your own in your logic. You must use Roster (contact list in xmpp servers) to get contacts of current user and save them in database or somewhere. Then create an activity to show contacts in a list. Each contact has a unique jid that can be distinguished from others with it. So with click on each contact, send it's object(include jid) to chat-activity. In chat-activity you must get previous messages from database or MAM(archived messages in server) and you can send a message to current contact(set contact jid as To).
To achieving delivery of message you must use this link. you can set request of it with this code:
Message message = … //make your stanza
DeliveryReceiptRequest.addTo(message); //add delivery request to message
connection.sendStanza(message); //send message
then you can be notified of delivery with this code:
private void setDelRecListener() {
DeliveryReceiptManager d = DeliveryReceiptManager.getInstanceFor(connection);
d.addReceiptReceivedListener(new ReceiptReceivedListener() {
#Override
public void onReceiptReceived(Jid fromJid, Jid toJid, String receiptId, Stanza receipt) {
Msg msg = F.getMsgBySid(receiptId);
if (msg == null)
return;
Boolean isUpdated = F.setMsgDelivered(msg);
Log.i("m/serv/UpdateDelivery", "for: " + receiptId + (isUpdated ? " Founded&Updated" : " NotFounded"));
if (isUpdated) {
BCTool.notifyPMDelivered(msg.id, msg.conv.frnd.getBareJid());
}
}
});
}
Keep in mind that every stanza has a sid(stanza id) and you must save each corresponding sid to message model in database when send is successful. This way you can detect which message delivery you got.
- For sending chat states like composing you can use this method:
public void sendChatState(String _jid, ChatState chatState) {
try {
Message msg = new Message();
msg.addExtension(new ChatStateExtension(chatState));
msg.setTo(JidCreate.bareFrom(_jid));
msg.setType(Message.Type.chat);
connection.sendStanza(msg);
Log.e("m/service", "ChatStateSent");
} catch (SmackException.NotConnectedException | InterruptedException | XmppStringprepException e) {
Log.e("m/service", "ChatState Not Sent: " + e.getMessage());
e.printStackTrace();
}
}
You must set a timer to prevent send composing in next 5Sec and reset timer when a character typed.
Consider reading this: ChatStateNotifications
Hello we are working on an android application in which GCM plays very important role in such as marketing purpose, push some important information to users etc.
It's working fine in 60-70% cases but other 30-40% it does not work. So rest of users never receive any notification which is useful for only to them.
This is the reason we are loosing users everyday. Below is my code to get the registration ID of GCM.
String msg = "";
int exceptionOccurRetry = 0;
while (exceptionOccurRetry < 5) {
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
int retry = 0;
while (retry < 5 && regid.length() == 0) {
regid = gcm.register(SENDER_ID);
++retry;
}
msg = "Device registered, registration ID=" + regid;
if (!regid.equals("")) {
// You should send the registration ID to your
// server
// over HTTP, so it
// can use GCM/HTTP or CCS to send messages to your
// app.
sendRegistrationIdToBackend();
}
break;
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
exceptionOccurRetry++;
}
}
We are looking what are the reasons such that GCM id is not available for some users.
We know only one reason that If user device doesn't have a Google Play Services installed on user phone then it does not work.
We are looking some more reasons to solve this problem.
One of the most common reasons why it is not available is that the user does not have google play services installed or is using a blocker.
You should also note that the GCM id should be refreshed if your application version has changed. You should be saving a unique device id to SharedPreferences and always check if it is the same, otherwise you should initiate the registration process again.
It is also a good idea to refresh the id from time to time.
Our team members are trying to send the notification, if it fails, they wait about 60 miliseconds or seconds (i'm not sure) for this push notification to be send again, if it still does not work, they wait twice the time, and so on ...
And you have to evaluate the response from google, there is a error string under:
std::string error = response["results"][0]["error"].asString();
Which gives you the information if a users account has been moved to, if so you can use:
Json::Value newRegistrationId = response["results"][0]["registration_id"];
to get the new ID.
if gcm id is null try to start a background task and get the id.check the below code
private void registerInBackground() {
new AsyncTask() {
#Override
protected String doInBackground(Object[] params) {
String msg = "";
try {
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(context);
}
regid = gcm.register(SENDER_ID);
msg = "Device registered, registration ID=" + regid;
sendRegistrationIdToBackend();
// Persist the regID - no need to register again.
storeRegistrationId(context, regid);
} catch (IOException ex) {
msg = "Error :" + ex.getMessage();
}
return msg;
}
#Override
protected void onPostExecute(Object msg) {
// mDisplay.append(msg + "\n");
}
}.execute(null, null, null);
}
Check if app was updated; if so, it must clear the registration ID since the existing registration ID is not guaranteed to work with the new app version.
When the application version changes, the registration id should change too. so you should save the last registration id to backend size. Maybe your problem is this.
If your question all focus on the id registration you can ignore my answer..
Sorry my answer may be the wrong answer to your question.
I just want to show there may be some other 'factor' influence the message delivery..
Do your send all message on only single recipients?
One of the most useful features in GCM is support for up to 1,000
recipients for a single message.
http://developer.android.com/training/cloudsync/gcm.html#
Sometimes our PHP also lose message sending to the registered device...
The message must less than 4K(do your GCM contains Pictures?)
You may have already read this..
Implementing GCM Client on Android
http://developer.android.com/google/gcm/client.html
Is that piece of code inside an asyncTask if not that might be your error in certain version (dont remember which) gcm registration gives NetworkOnMainThreadException for some reason, they updated that later but I had that same problem some time ago, this is the piece of code I have used hope it helps you out:
private void performRegisterGCM(){
//Check for GCM availability
if(checkPlayServices(this)){
// If this check succeeds, proceed with normal processing.
// Otherwise, prompt user to get valid Play Services APK.
gcm = GoogleCloudMessaging.getInstance(this);
String regId = mPreferences.getGcmRegistrationId();
if (regId.isEmpty()){
RegisterGCM();
}else{
log.d("regId: "+regId);
}
} else {
// Status is a random integer
GooglePlayServicesUtil.getErrorDialog(status, this, RQS_GooglePlayServices).show();
}
}
public static boolean checkPlayServices(Activity mActivity){
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(mActivity);
return resultCode == ConnectionResult.SUCCESS
}
private void RegisterGCM(){
new AsyncTask<Void,Void,String>() {
#Override
protected String doInBackground(Void... params) {
String regid = "";
try{
if (gcm == null) {
gcm = GoogleCloudMessaging.getInstance(mContext);
}
regid = gcm.register(Util.SENDER_ID);
}catch(Exception e){
log.e(e.getMessage());
}
return regid;
}
#Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
mPreferences.setGcmRegistrationId(s);
//TODO send regid to server with all the other info
sendGCMIDtoBackend(s);
}
}.execute(null, null, null);
}
Note that the checkPlayServices also gave me a lot of problems I had it like this:
public static boolean checkPlayServices(Activity mActivity){
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(mActivity);
if(resultCode!= ConnectionResult.SUCCESS) {
if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)){
GooglePlayServicesUtil.getErrorDialog(
resultCode,
mActivity,
PLAY_SERVICES_RESOLUTION_REQUEST
).show();
} else{
log.d("DEVICE NOT SUPPORTED");
exit(true);
}
return false;
}
return true;
}
Then changed it as you can see in the first piece of code, because for some reason when it falls in isUserRecoverableError(result) it gives a lot of headaches... Everything here is from an actual working project and the code snippets were obtained in http://developer.android.com/google/gcm/client.html and modified to work correctly. Hope this helps you out, Good Luck...
How about the backend? If you delete the ID from the server's database, the user will never receive a notification unless you update the app version?
I'm wondering if anybody can help me figure out what is causing the data I am sending to become corrupt.
My setup is currently an Arduino pro mini with a HM-10 bluetooth module connected (I have also tried HM-11 Module too) and an Android application to receive the bluetooth data.
Module setup: http://letsmakerobots.com/node/38009
If I send data with big enough intervals then the data is fine, but if I send the data continuously I see messages getting mixed up and lost. To test this I send "$0.1,0.2,0.3,0.4,0.5" to the Android application from the Arduino, sometimes the stream of data appears to send fine but other times it is really quite scrambled. Please see the below graphs that demonstrate this:
Good case:
Bad case:
Arduino code:
String inputString = ""; //Hold the incoming data.
boolean stringComplete = false; //Determines if the string is complete.
boolean realtime = false;
void setup()
{
Serial.begin(9600);
delay(500);
Serial.print("AT+START");
delay(500);
}
void loop()
{
if(stringComplete)
{
if(inputString.equals("rStart"))
{
Serial.println("$startACK");
realtime = true;
}
else if(inputString.equals("stop"))
{
Serial.println("$stopACK");
realtime = false;
}
else{
Serial.print(inputString);
}
inputString = "";
stringComplete = false;
}
if(realtime)
{
Serial.println("$0.1,0.2,0.3,0.4,0.5,0.6");
delay(10);
}
}
void serialEvent() {
while (Serial.available())
{
// get the new byte:
char inChar = (char)Serial.read();
if (inChar == '\n')
{
stringComplete = true;
}
else
{
inputString += inChar;
}
}
}
The Android side just receives the data and then parses it in an IntentService:
#Override
protected void onHandleIntent(Intent intent) {
//Incoming command.
String rawData = intent.getStringExtra(DataProcessingIntentService.REQUEST);
//Append our new data to our data helper.
Log.i(this.getClass().getName(), "Previous Raw: (" + DataProcessingHelper.getInstance().getData() + ")");
DataProcessingHelper.getInstance().appendData(rawData);
Log.i(this.getClass().getName(), "New Raw: (" + DataProcessingHelper.getInstance().getData() + ")");
commandStartIndex = DataProcessingHelper.getInstance().getData().indexOf("$");
commandEndIndex = DataProcessingHelper.getInstance().getData().indexOf("\n");
//Set this as the data starting point.
if(commandStartIndex != -1){
DataProcessingHelper.getInstance().offsetData(commandStartIndex);
}
//Ensure that a command has been found and that the end index is after the starting index.
if(commandStartIndex != -1 && commandEndIndex > commandStartIndex){
//Remove the command structure from the command.
command = DataProcessingHelper.getInstance().getData().substring(commandStartIndex+1, commandEndIndex-1); //Remove the \r\n end command.
DataProcessingHelper.getInstance().offsetData(commandEndIndex+1);
if(command.length() > 1){
//Split the data out of the comand.
splitData = command.split(",");
Log.i(this.getClass().getName(), "Broadcasting the processed data. (" + command + ")");
//Broadcast data.
Intent broadcastIntent = new Intent();
broadcastIntent.setAction(DataProcessingIntentService.RESPONSE);
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
broadcastIntent.putExtra(DataProcessingIntentService.RESPONSE, splitData);
sendBroadcast(broadcastIntent);
}else{
Log.e(this.getClass().getName(), "Command is less than 1 character long!");
}
}
}
Thank you for any help!
I have now figured out what was causing this problem. It appears that BLE only supports a maximum of 20 bytes per a transaction. The time between these transactions is different depending on what you are using. I'm currently using notifications which means that I can send 20 bytes every 7.5 milliseconds maximum. I have opted for 10 milliseconds to be safe. I will now need to look into breaking up packets into 20 bytes maximum to ensure no data corruption.