Sending DataMap in a background service android - java

I'm trying to send the content of a DataMap from an Android device to a wearable. It works fine when the app is in the foreground on my app but once I lock the mobile device it gets stuck at the pendingResult.await() and the wearable doesn't receive any data where as it normal would if I keep the app open.
public void send(final DataMap dataMap) {
new Thread(new Runnable() {
#Override
public void run() {
PutDataMapRequest putDMR = PutDataMapRequest.create(WEARABLE_DATA_PATH);
putDMR.getDataMap().putAll(dataMap);
PendingResult<DataApi.DataItemResult> pendingResult = Wearable.DataApi.putDataItem(googleClient, request);
DataApi.DataItemResult result = pendingResult.await();
if(result.getStatus().isSuccess()) {
Log.d("qwe", "Data item set: " + result.getDataItem().getUri());
}
}
}).start();
}
This method is in a class which extends WearableListenerService and I have added the XML in the AndroidMainfest for the service also. Am I doing something completely wrong or missing something?
Thanks

try to check google api client status for each send.
use blockingConnect when google api client is not connected.

Found out I was doing googleClient.disconnect() in my main activity onStop() which was causing it to hang as googleClient wasn't connected once my app was in the background.

Related

Unable to send firebase fcm push notiication to android device from Java server

Unable to send firebase fcm push notiication to android device.
When I send test message from firebase console, it goes to device. So Android side code is working fine.
For Backend Java server, I am using firebase sdk
public void sendNotification2(String notification, String title, String to) {
String registrationToken = to;
Message message = Message.builder().putData("score", "850").putData("time", "2:45")
.setToken(registrationToken).build();
String response;
response = FirebaseMessaging.getInstance().send(message);
System.out.println("Successfully sent message: " + response);
}
Code outputs: Successfully sent message: projects/myappname/messages/0:1590590793114288%9cceaf10deffd7abc
But the device doesn't receive notification.
It would be important to clarify if your app is in background or foreground while sending the message from backend, in fcm this is important to consider.
In this case, you send a data message, not a notification message.
The difference is explained in the doc:
https://firebase.google.com/docs/cloud-messaging/android/receive
That means, you should receive this message in the overriden onMessageReceived(...)-method when the app is in the foreground and in the background.
(You can control this with a debug log command.)
If you use data messages, you can define custom key-value pairs and implement the notification process on your own, when the app is in the foreground and background. With firebase console in contrast, you send notification messages, they are processed in the background of your app automaticly, in the foreground the overriden onMessageReceived(...) is triggered.
Here is an example of building a notification message (with different priorities which should not confuse you):
private Message createFCMMessageWithNormalPriority(String notificationMessage) {
return Message.builder().setNotification(createFCMNotification(notificationMessage)).setToken(registrationToken)
.setAndroidConfig(createAndroidConfigWithNormalMessagePriority()).build();
}
private Message createFCMMessageWithHighPriority(String notificationMessage) {
return Message.builder().setNotification(createFCMNotification(notificationMessage)).setToken(registrationToken)
.setAndroidConfig(createAndroidConfigWithHighMessagePriority()).build();
}
private AndroidConfig createAndroidConfigWithHighMessagePriority() {
return createAndroidConfigWithPriority(Priority.HIGH);
}
private AndroidConfig createAndroidConfigWithNormalMessagePriority() {
return createAndroidConfigWithPriority(Priority.NORMAL);
}
private AndroidConfig createAndroidConfigWithPriority(Priority priority) {
return AndroidConfig.builder().setPriority(priority).build();
}
private Notification createFCMNotification(String notificationMessage) {
return Notification.builder().setBody(notificationMessage).setTitle(NOTIFICATION_CONTENT_TITLE).build();
}

Android webRTC video call inside a Background Service

Forgive me if this question was already asked, I couldn't find an answer for my case.
So, I have an Android app with Voice & Video call feature. I used webRTC for this.
I was able to make both Voice and Video call working perfectly inside an Activity, but now I want to keep the call running while the user exit the CallActivity and go back to the ChatActivity (to send a file/link/photo for example).
I managed to make the Voice call run perfectly inside a Background Service, but video call won't work as expected.
The remote video won't be displayed even though the audio from the video track is playing.
here is my Background Service code :
#Override
public void onAddStream(MediaStream mediaStream) {
if (mediaStream.videoTracks.size() > Constants.ONE || mediaStream.audioTracks.size() > Constants.ONE) {
return;
}
//check for video track, means this is a video call
if (!isAudioCall && mediaStream.videoTracks.size() > Constants.ZERO) {
remoteVideoTrack = mediaStream.videoTracks.get(Constants.ZERO);
CallActivityNew.remoteVideoTrack = remoteVideoTrack;
try {
localAudioTrack.setEnabled(true);
//Now ask the UI to display the video track
sendOrderToActivity(Constants.START_REMOTE_VIDEO, null);
} catch (Exception ignored) {}
} else if (mediaStream.audioTracks.size() > Constants.ZERO) {
//Means this is a Voice call, only audio tracks available
remoteAudioTrack = mediaStream.audioTracks.get(Constants.ZERO);
try {
localAudioTrack.setEnabled(true);
remoteAudioTrack.setEnabled(true);
} catch (Exception ignored) {}
}
}
and below my CallActivity code :
case Constants.START_REMOTE_VIDEO: {
if (remoteVideoView == null) {
remoteVideoView = findViewById(R.id.remote_gl_surface_view);
}
remoteVideoView.init(eglBaseContext, null);
remoteVideoView.setEnableHardwareScaler(true);
remoteVideoView.setMirror(true);
remoteVideoView.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);
remoteVideoView.setZOrderMediaOverlay(true);
//Apply video track to the Surface View in order to display it
remoteVideoTrack.addSink(remoteVideoView);
//now enable local video track
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
//now enable local video track
remoteVideoTrack.setEnabled(true);
}
}, Constants.TIME_THREE_HUNDRED_MILLIS);
setSpeakerphoneOn(false);
break;
}
I am sending orders from Service to Activity, the "case Constants.START_REMOTE_VIDEO" work after receiving the order from Service.
I don't see where the problem, why am I only hearing sound but the remote video won't start display !!
Thank you in advance for helping.
After testing for long hours, I found that my code works just fine, I just forget to change the view visibility from "GONE" to "VISIBLE".
Yeah that was the solution, i swear xD

Discovering all services on the network using jmDNS on Android

I'm using the jmdns.jar from this project https://github.com/twitwi/AndroidDnssdDemo in my Android project.
I'm currently trying to find all services on my network. I can't use Android NSD, so please avoid suggesting it as a solution.
protected Object doInBackground(Object[] params) {
try {
WifiManager wifi = (WifiManager) getActivity().getSystemService(Context.WIFI_SERVICE);
final InetAddress deviceIpAddress = InetAddress.getByName(Formatter.formatIpAddress(wifi.getConnectionInfo().getIpAddress()));
multicastLock = wifi.createMulticastLock(getClass().getName());
multicastLock.setReferenceCounted(true);
multicastLock.acquire();
jmDNS = JmDNS.create(deviceIpAddress, "Android Device Discovery");
jmDNS.addServiceListener("_http._tcp.local.", new ServiceListener() {//_services._dns-sd._udp _http._tcp.local. _workstation._tcp.local.
#Override
public void serviceAdded(ServiceEvent serviceEvent) {
jmDNS.requestServiceInfo("", "", 1000);
}
#Override
public void serviceRemoved(ServiceEvent serviceEvent) {
}
#Override
public void serviceResolved(ServiceEvent serviceEvent) {
System.out.println(serviceEvent.getInfo().getHostAddress());
System.out.println(serviceEvent.getInfo().getName());
}
});
}catch(IOException e) {
Log.e(TAG, e.getMessage(), e);
}
}
The above code gives me the address and name of a printer on my network. That's great. What I would like is a TYPE that will catch all the services being broadcasted on my network. Android NSD had a _services._dns-sd._udpthat could be used for the type of service and find all services on the network. This doesn't work with jmDNS. I can't find anything in the limited documentation about this.
Do I need to go through and add all the service types myself? That is not a very clean solution.
I have the proper perms in my AndroidManifest.
Try adding the dot at the end, my jmdns app crashed without it..
Also try adding the .local. by yourself with the service type.

Android HTTP Requests Working In Simulator But Not On Wear Device

I am making a simple Android Wear app to control my thermostats, and I'm sending POST requests with Volley to control them. Everything works great in the Android Wear simulator (the request works), but, while the app does load on my Moto 360, the volley request gets called but invariably times out.
Why could my volley request be failing on my watch but working on the simulator? Other apps' requests succeed on my watch (for example, the built-in weather app can load up weather data in about 3 seconds). And, the weirdest part: I had the app working (successfully making volley requests) on my watch, and, about a day after I installed it to my watch from Android Studio, it suddenly stopped loading data for no apparent reason.
What I've tried so far:
I have requested the Internet permission in my manifest.xml.
I have increased the timeout to 30 seconds (see my code below), which didn't change anything.
I have tried tethering my computer and the simulator to my phone's connection via Bluetooth (to replicate the Bluetooth connection my physical watch has to my phone), and the simulator made the request successfully still (albeit with a two-second delay), ruling out the possibility of Bluetooth being too slow.
I made sure the API level is low enough for my Marshmallow-running watch (my watch and the app are both API level 23).
I tried doing a quick test request to Google before the request to the company's servers with my thermostat data, and while the Google request returns the site's HTML code in the simulator, it times out on my watch (thirty seconds after the request is initiated).
I tried putting some dummy data into the recycler view data should be loaded into, and the dummy data indeed showed up, ruling out that the recycler view is broken.
I deleted the app from my watch and reinstalled it, and deleted the companion from my phone, reinstalled it, and deleted it again, all to no avail.
A lengthy chat with Google Support did not produce anything meaningful.
Here's my code (from my main view's adapter):
public void refreshThermostatsRecyclerView(RequestQueue queue) {
String url = "https://mobile.skyport.io:9090/login"; // login call to the thermostats server Skyport
Log.w("myApp", "Starting /login call to Skyport"); // this gets called on simulator and watch
// Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.POST, url,
Response.Listener<String>() {
#Override
public void onResponse(String response) {
// Display the response string.
Log.w("myApp", "Response is: " + response); // this gets called on the simulator but not the watch
try {
// there's some code to parse the data.
} catch (JSONException e) {
Log.w("myApp", "catching an error parsing the json."); // never gets called.
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.w("myApp", "Skyport request didn't work! " + error); // this always gets called on the watch, with the error being a timeout error (com.Android.Volley.timeouterror) but never gets called in the simulator
}
}) {
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> m = new HashMap<>();
m.put("Referer", "app:/VenstarCloud.swf");
// here I put some more headers
return m;
}
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> m = new HashMap<>();
m.put("version", "3.0.5");
m.put("email", userEmail);
m.put("password", userToken);
return m;
}
};
// Add the request to the RequestQueue.
int socketTimeout1 = 30000; // times out 30 seconds after the request starts on the watch
RetryPolicy policy1 = new DefaultRetryPolicy(socketTimeout1, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT);
stringRequest.setRetryPolicy(policy1);
queue.add(stringRequest);
}
Which is called from the onCreate() method in my Main Activity with this code:
RequestQueue queue = Volley.newRequestQueue(this);
refreshThermostatsRecyclerView(queue);
If you'd like to view the logs created by running this in the simulator and on the watch, they're on Google Drive here.
Edit 1: A reboot of my watch fixes the issue temporarily and allows the watch to make HTTP Requests again, but it breaks again once the watch disconnects from Bluetooth, connects to WiFi, disconnects from WiFi, and reconnects to Bluetooth (so it breaks every time I go across my apartment without my phone and then return).
Edit 2: I switched the volley requests all over to HTTPURLConnection Requests in an Async thread, and the same issues occur as with volley.
tl;dr: My app's Volley requests are working in the simulator but not on my Android Wear watch anymore (though Play Store-downloaded apps' similar requests work), how can I get a volley request to work again on my app on the watch?
As per these two conversations below, it seems that WiFi connectivity only allows Android Wear to connect to a phone over WiFi and not directly to the Internet. However, Android Wear 2.0 lets you use regular network APIs.
Direct internet connection on Android Wear?
Does Android Wear support direct access to the Internet?
So, for Android Wear 2.0+ Volley requests from wearable app should work.
If you want to use Android Wear <2.0, then:
On Wearable, in onCreate() add a key that indicates whether the phone should start collecting data.
PutDataMapRequest putDataMapReq = PutDataMapRequest.create("/shouldStart");
putDataMapReq.getDataMap().putBoolean(SHOULD_START_KEY, true);
PutDataRequest putDataReq = putDataMapReq.asPutDataRequest();
PendingResult pendingResult = Wearable.DataApi.putDataItem(mGoogleApiClient, putDataReq);
On phone, in onDataChanged, check if wearable wants to start collecting data. If yes, start Volley request.
for (DataEvent event : dataEvents) {
if (event.getType() == DataEvent.TYPE_CHANGED) {
// DataItem changed
DataItem item = event.getDataItem();
if (item.getUri().getPath().compareTo("/shouldStart") == 0) {
DataMap dataMap = DataMapItem.fromDataItem(item).getDataMap();
boolean shouldStart = dataMap.getBoolean(SHOULD_START_KEY));
if(shouldStart) {
Volley.newRequestQueue(this).add(request);
}
}
} else if (event.getType() == DataEvent.TYPE_DELETED) {
// DataItem deleted
}
}
Then, your Volley request's onResponse should pass data back to Wearable.
public void onResponse(String response) {
PutDataMapRequest putDataMapReq = PutDataMapRequest.create("/data");
putDataMapReq.getDataMap().putString(DATA_KEY, true);
PutDataRequest putDataReq = putDataMapReq.asPutDataRequest();
PendingResult pendingResult = Wearable.DataApi.putDataItem(mGoogleApiClient, putDataReq);
}
Finally, you can access data in your Wearable using onDataChanged and store it in your model for passing it onto adapter:
for (DataEvent event : dataEvents) {
if (event.getType() == DataEvent.TYPE_CHANGED) {
// DataItem changed
DataItem item = event.getDataItem();
if (item.getUri().getPath().compareTo("/data") == 0) {
DataMap dataMap = DataMapItem.fromDataItem(item).getDataMap();
parseAndpassToAdapter(dataMap.getString(DATA_KEY));
}
} else if (event.getType() == DataEvent.TYPE_DELETED) {
// DataItem deleted
}
}
You'll need Wearable.API to implement this and your class should implement DataApi.DataListener. For more information getting started, refer to Accessing the Wearable Data Layer and Syncing Data Items
Hope this helps.
I am also using volley on an Android wear app I built and I am running it on a Moto 360, I have run into the same problem a couple o times. Try restarting the device. Go to Settings > Restart. It sounds silly but it has worked for me.
You could try an alternative to volley if you can rule out the connection as the problem:
compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.android.support:support-v4:23.1.0'
compile 'com.android.support:design:23.1.0'
compile 'com.google.code.gson:gson:2.2.4'
compile 'com.google.api-client:google-api-client:1.20.0'
The versions are important.
Then to your request:
Map<String, String> contentParams = new HashMap<>();
InputStream is = null;
NetHttpTransport transport = null;
HttpRequest request = null;
HttpResponse resp = null;
HttpHeaders headers = new HttpHeaders();
JSONObject json = null;
try {
transport = new NetHttpTransport();
HttpRequestFactory factory = transport.createRequestFactory();
request = factory.buildPostRequest(new GenericUrl(url), null);
contentParams = getContentParameters();
headers.putAll(getHeaderParameters());
request.setHeaders(headers);
request.getUrl().putAll(contentParams);
resp = request.execute();
is = resp.getContent();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
string = getJSONFromInputStream(is);
json = new JSONObject(string);
}
} catch (Exception e) {
e.printStackTrace();
}
}
transport.shutdown();
protected Map<String, String> getContentParameters() {
Map<String, String> m = new HashMap<>();
m.put("version", "3.0.5");
m.put("email", userEmail);
m.put("password", userToken);
return m;
}
protected Map<String, String> getHeaderParameters() {
Map<String, String> m = new HashMap<>();
m.put("Referer", "app:/VenstarCloud.swf");
return m;
}
protected String getJSONFromInputStream(InputStream is) {
if (is == null)
throw new NullPointerException();
//instantiates a reader with max size
BufferedReader reader = new BufferedReader(new InputStreamReader(is), 8 * 1024);
StringBuilder sb = new StringBuilder();
try {
//reads the response line by line (and separates by a line-break)
String line;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//closes the inputStream
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return sb.toString();
}
Then just execute your code from a thread/asynctask/have it delay your frontend slightly
Edit:
Just in case there is a problem with appending a map:
for (Entry<String, String> entry : getHeaderParameters()) {
headers.put(entry.getKey(), entry.getValue());
}
for (Entry<String, String> entry : getContentParameters()) {
request.getUrl().put(entry.getKey(), entry.getValue());
}
Also as another note, make sure to change the return type from void on both those methods to Map
Is this not just the case of when the watch is connected to the phone via bluetooth the internet will not work, as wifi is turned off. If the watch is using wifi to connect to the phone then it will work.
I'm working on wear 2.0 app and just turn blueooth off on my phone for my watch to get internet connection.

AWS Android Chat App

I am trying to make an android chat application. I am thinking about making it with aws. But the problem is that I am unable to find any good tutorial for doing this and I have no idea how to do it.
So could anyone please suggest some tutorial for sending push notification or on how to make a chat application?
Firebase is well suited to this due to its "realtime database" feature. Here's a few tutorials I found by Googling
https://code.tutsplus.com/tutorials/how-to-create-an-android-chat-app-using-firebase--cms-27397
http://myapptemplates.com/simple-android-chat-app-tutorial-firebase-integration/
https://codelabs.developers.google.com/codelabs/firebase-android/#0
Check Socket.IO for android. ( https://github.com/socketio/socket.io-client-java )
Its really easy to write a chat application. But you need a server side.
Easy to write a simple server for this chat app.
Server reveice the all message from clients and broadcast the message, to all.
Gradle:
compile 'com.github.nkzawa:socket.io-client:0.5.1'
Android manifest:
<uses-permission android:name="android.permission.INTERNET" />
Java
public static Socket mSocket;
try {
mSocket = IO.socket("http://192.168.1.104:4444");
mSocket.connect();
} catch (URISyntaxException e) {
e.printStackTrace();
}
Send messsage to server:
MainActivity.mSocket.emit("message","Text here...");
Create a listener for another message:
MainActivity.mSocket.on("newMessage", onMessageArrive); // Oncreate
private Emitter.Listener onMessageArrive = new Emitter.Listener() {
#Override
public void call(final Object... args) {
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
String data = (String)args[0];
// Here is all message. add it to list :) Or Push notif
}
});
}
};
// Server side:
var http = require('http');
var express = require('express'),
app = module.exports.app = express();
var io = require('socket.io').listen(app.listen(4444));
io.on('connection', function (socket) {
socket.on("message",function(msg){
io.sockets.emit('newMessage', msg);
});
});
Run:
npm install express
npm install socket.io
node filename.js
Just dont forget to check you IP! :)
Done! You have a Real Time Chat!!

Categories