I am working on a Messaging Android App. Users should be notified with a push notification.
At the time I have this code:
public void notifyUser(String user_id, String message) {
try {
OneSignal.postNotification(new JSONObject("{'contents': {'en':['" + message + "']}, " +
"'include_player_ids': ['" + user_id + "'], " +
"'headings': {'en': 'Tag sub Title HI {{user_name}}'}, " +
"'data': {'openURL': 'https://imgur.com'}," +
"'buttons':[{'id': 'id1', 'text': 'Go to GreenActivity'}, {'id':'id2', 'text': 'Go to MainActivity'}]}"),
new OneSignal.PostNotificationResponseHandler() {
#Override
public void onSuccess(JSONObject response) {
Log.d("LOL", "postNotification Success: " + response.toString());
}
#Override
public void onFailure(JSONObject response) {
Log.d("LOL", "postNotification Failure: " + response.toString());
}
});
} catch (JSONException e) {
e.printStackTrace();
}
}
But I get an Error in Log:
D/LOL: postNotification Failure: {"errors":["Notification contents for each language must be a string"]}
I searched threw the Internet but didn't find a solution for my Problem.
I hope you can help me...
Thanks in advance.
I believe the error you're seeing is because you're sending up the message inside an array when it should just be a string.
"{'contents': {'en':['" + message + "']} should be "{'contents': {'en':'" + message + "'} (removed the brackets).
Related
My serser uses Java with SpringBoot and my client is an expo react native app which uses typescript.
I am really blocked by this feature: I want to sens push notifications. I tried a lot of methods, but I didn't succeed.
On the client side I am using the method described in expo documentation: https://docs.expo.dev/push-notifications/overview/.
When I use their tool to send test notifications(https://expo.dev/notifications), I receive them on my device.
I didn't manage to send notifications from my client app. I tried all I found on the Inthernet. When I used FirebaseMessagingService and I used the server key from the firebase project as token, I received the SENDER_ID_MISMATCH error.
#Service
public class FirebaseMessagingService {
private final FirebaseMessaging firebaseMessaging;
public FirebaseMessagingService(FirebaseMessaging firebaseMessaging) {
this.firebaseMessaging = firebaseMessaging;
}
public String sendNotification(Note note, String topic) throws FirebaseMessagingException {
Notification notification = Notification
.builder()
.setTitle(note.getSubject())
.setBody(note.getContent())
.setImage(note.getImage())
.build();
Message message = Message
.builder()
.setNotification(notification)
.putAllData(note.getData())
.setToken(topic)
.build();
return firebaseMessaging.send(message);
}
}
I also found the expo-server-sdk-java but I couldn't manage to integrate it.
Any heeeeelp, pleaseeee?
not sure if it's the best practice but it works fine for me.
My pom
<dependency>
<groupId>io.github.jav</groupId>
<artifactId>expo-server-sdk</artifactId>
<version>1.1.0</version>
</dependency>
Then in the java class
private static void sendPushNotification(String token, String titulo, String mensaje, Map<String, Object> data) throws PushClientException {
if (!PushClient.isExponentPushToken(token)) throw new Error("Token:" + token + " is not a valid token.");
ExpoPushMessage expoPushMessage = new ExpoPushMessage();
expoPushMessage.getTo().add(token);
expoPushMessage.setTitle(titulo);
expoPushMessage.setBody(mensaje);
expoPushMessage.setData(data);
List<ExpoPushMessage> expoPushMessages = new ArrayList<>();
expoPushMessages.add(expoPushMessage);
PushClient client = new PushClient();
List<List<ExpoPushMessage>> chunks = client.chunkPushNotifications(expoPushMessages);
List<CompletableFuture<List<ExpoPushTicket>>> messageRepliesFutures = new ArrayList<>();
for (List<ExpoPushMessage> chunk : chunks) {
messageRepliesFutures.add(client.sendPushNotificationsAsync(chunk));
}
// Wait for each completable future to finish
List<ExpoPushTicket> allTickets = new ArrayList<>();
for (CompletableFuture<List<ExpoPushTicket>> messageReplyFuture : messageRepliesFutures) {
try {
allTickets.addAll(messageReplyFuture.get());
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
List<ExpoPushMessageTicketPair<ExpoPushMessage>> zippedMessagesTickets = client.zipMessagesTickets(expoPushMessages, allTickets);
List<ExpoPushMessageTicketPair<ExpoPushMessage>> okTicketMessages = client.filterAllSuccessfulMessages(zippedMessagesTickets);
String okTicketMessagesString = okTicketMessages.stream().map(p -> "Title: " + p.message.getTitle() + ", Id:" + p.ticket.getId()).collect(Collectors.joining(","));
LOGGER.info("Recieved OK ticket for " + okTicketMessages.size() + " messages: " + okTicketMessagesString);
List<ExpoPushMessageTicketPair<ExpoPushMessage>> errorTicketMessages = client.filterAllMessagesWithError(zippedMessagesTickets);
String errorTicketMessagesString = errorTicketMessages.stream().map(p -> "Title: " + p.message.getTitle() + ", Error: " + p.ticket.getDetails().getError()).collect(Collectors.joining(","));
LOGGER.error("Recieved ERROR ticket for " + errorTicketMessages.size() + " messages: " + errorTicketMessagesString);
/**
// Countdown 30s
int wait = 30;
for (int i = wait; i >= 0; i--) {
System.out.print("Waiting for " + wait + " seconds. " + i + "s\r");
Thread.sleep(1000);
}
System.out.println("Fetching reciepts...");
List<String> ticketIds = (client.getTicketIdsFromPairs(okTicketMessages));
CompletableFuture<List<ExpoPushReceipt>> receiptFutures = client.getPushNotificationReceiptsAsync(ticketIds);
List<ExpoPushReceipt> receipts = new ArrayList<>();
try {
receipts = receiptFutures.get();
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
}
System.out.println("Recieved " + receipts.size() + " receipts:");
for (ExpoPushReceipt reciept : receipts) {
System.out.println("Receipt for id: " + reciept.getId() + " had status: " + reciept.getStatus());
}
*/
}
In the App.js from react native project with EXPO 44 i take expo token in this way
async function registerForPushNotificationsAsync() {
let token;
if (isDevice) {
const { status: existingStatus } =
await Notifications.getPermissionsAsync();
let finalStatus = existingStatus;
if (existingStatus !== "granted") {
const { status } = await Notifications.requestPermissionsAsync();
finalStatus = status;
}
if (finalStatus !== "granted") {
alert("Failed to get push token for push notification!");
return;
}
token = (await Notifications.getExpoPushTokenAsync()).data;
}
if (Platform.OS === "android") {
Notifications.setNotificationChannelAsync("default", {
name: "default",
importance: Notifications.AndroidImportance.MAX,
vibrationPattern: [0, 250, 250, 250],
lightColor: "#FF231F7C",
});
}
return token;
}
effect from App.js
useEffect(() => {
initFirebaseApp();
registerForPushNotificationsAsync().then(async (token) => {
//store in some place token
});
// This listener is fired whenever a notification is received while the app is foregrounded
notificationListener.current =
Notifications.addNotificationReceivedListener(handleNotification);
// This listener is fired whenever a user taps on or interacts with a notification (works when app is foregrounded, backgrounded, or killed)
responseListener.current =
Notifications.addNotificationResponseReceivedListener(
handleNotificationResponse
);
return () => {
Notifications.removeNotificationSubscription(
notificationListener.current
);
Notifications.removeNotificationSubscription(responseListener.current);
};
}, []);
notification handlers in App.js
const handleNotification = (response) => {
// console.log(response);
};
const handleNotificationResponse = (response) => {
// console.log(response)
};
I hope this helps you
Docs
Expo SDK documentation
Expo docs reference
I am trying to migrate new Auto Complete Place Picker I have updated library from "com.google.android.gms:play-services" to new "com.google.android.libraries.places:places-compat:2.0.0" and implemented SupportPlaceAutocompleteFragment.
All works fine however the error I am getting is:
"Place.Field" Cannot resolve symbol 'Field'
Below is my code:
// Initialize the AutocompleteSupportFragment.
SupportPlaceAutocompleteFragment supportPlaceAutocompleteFragment = (SupportPlaceAutocompleteFragment)getSupportFragmentManager().findFragmentById(R.id.autocomplete_fragment);
//List<Place.Field> placeFields = Arrays.asList(Place.Field.ID, Place.Field.NAME);
// Specify the types of place data to return.
supportPlaceAutocompleteFragment.setPlaceFields(Arrays.asList(Place.Field.ID, Place.Field.NAME));
// Set up a PlaceSelectionListener to handle the response.
supportPlaceAutocompleteFragment.setOnPlaceSelectedListener(new PlaceSelectionListener() {
#Override
public void onPlaceSelected(Place place) {
// TODO: Get info about the selected place.
Toast.makeText(MapsActivity.this, "Place: " + place.getName() + ", " + place.getId(), Toast.LENGTH_LONG).show();
Log.i("", "Place: " + place.getName() + ", " + place.getId());
}
#Override
public void onError(Status status) {
}
});
supportPlaceAutocompleteFragment.setPlaceFields(Arrays.asList(Place.Field.ID, Place.Field.NAME));
Can you help me solve this error:
"Place.Field" Cannot resolve symbol 'Field'
Ensure that you import the correct Place class.
import com.google.android.libraries.places.api.model.Place;
Hope this helps.
The SupportPlaceAutocompleteFragment class is deprecated. Please use the AutocompleteSupportFragment class instead. Refer to Google's migration guide and the Place Autocomplete's guide.
// Initialize the AutocompleteSupportFragment.
AutocompleteSupportFragment supportPlaceAutocompleteFragment = (AutocompleteSupportFragment) getSupportFragmentManager().findFragmentById(R.id.autocomplete_fragment);
//List<Place.Field> placeFields = Arrays.asList(Place.Field.ID, Place.Field.NAME);
// Specify the types of place data to return.
supportPlaceAutocompleteFragment.setPlaceFields(Arrays.asList(Place.Field.ID, Place.Field.NAME));
// Set up a PlaceSelectionListener to handle the response.
supportPlaceAutocompleteFragment.setOnPlaceSelectedListener(new PlaceSelectionListener() {
#Override
public void onPlaceSelected(Place place) {
Toast.makeText(MapsActivity.this, "Place: " + place.getName() + ", " + place.getId(), Toast.LENGTH_LONG).show();
Log.i("", "Place: " + place.getName() + ", " + place.getId());
}
#Override
public void onError(Status status) {
}
});
supportPlaceAutocompleteFragment.setPlaceFields(Arrays.asList(Place.Field.ID, Place.Field.NAME));
Hope this helps!
Following instructions here to save data to an object by objectId thru android app and getting errmsg com.parse.ParseRequest$ParseRequestException: (without anything following semicolon). I have confirmed objectId is as shown in parse dashboard. Can someone please help me figure out what I'm doing incorrectly? Thanks
final String parseRequest = "Request";
final String parseRequestDriverLocation = "driverLocation";
private void saveDriverLocation(String objectId, Location driverLocation) {
Log.i("saveDriverLocation", "objectId=" + objectId + ", driverLocation=" + driverLocation.toString());
ParseGeoPoint locationDriverParse = new ParseGeoPoint(driverLocation.getLatitude(), driverLocation.getLongitude());
ParseObject acceptedRequest = ParseObject.createWithoutData(parseRequest, objectId);
Log.i("saveDriverLocation", "acceptedRequest=" + acceptedRequest.toString() + "\nlocationDriverParse=" + locationDriverParse.toString());
acceptedRequest.put(parseRequestDriverLocation, locationDriverParse);
acceptedRequest.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
Log.i("saveDriverLocation", "driver location saved successfully");
} else {
Log.i("saveDriverLocation", "saving driver location failed... " + e.toString());
}
}
});
}
The logcat:
I/saveDriverLocation: objectId=Ddm73yXhPC, driverLocation=Location[gps 41.677770,-80.385998 acc=20 et=+1d7h59m59s883ms alt=18.0 {Bundle[mParcelledData.dataSize=40]}]
I/saveDriverLocation: acceptedRequest=com.parse.ParseObject#ddeb183
locationDriverParse=ParseGeoPoint[41.677770,-80.385998]
I/saveDriverLocation: saving driver location failed... com.parse.ParseRequest$ParseRequestException:
Apparently a Parse class can only have one ParseGeoPoint column! Here is reference.
So in my app I have the following functionality for receiving bitcoins
kit.wallet().addCoinsReceivedEventListener(new WalletCoinsReceivedEventListener() {
#Override
public void onCoinsReceived(Wallet wallet, Transaction tx, Coin prevBalance, Coin newBalance) {
txtLog.append("-----> coins resceived: " + tx.getHashAsString() + "\n");
txtLog.append("received: " + tx.getValue(wallet) + "\n");
Futures.addCallback(tx.getConfidence().getDepthFuture(1), new FutureCallback<TransactionConfidence>() {
#Override
public void onSuccess(TransactionConfidence result) {
txtLog.append("\nSuccess! Recieved: " + tx.getValue(wallet) + "\n");
//Find address of sender here
}
#Override
public void onFailure(Throwable t) {
throw new RuntimeException(t);
}
});
}
});
This works great, OnSuccess triggers properly once a transaction is confirmed and added to my wallet. txtLog is just a textArea in my java frame which displays some text output for me. What I need to do now is find the address of the sender at this point, can i do this with the Transaction object tx? Any help would be appreciated.
Found the solution! Unfortunately it uses a depreciated method. I just added the following in the appropriate spot.
String address = "";
for (TransactionInput txin : tx.getInputs()){
address = txin.getFromAddress().toString();
}
I'm having serious issues trying to find the correct syntax to push a document into an elasticsearch index by type.
My code im using to do this is :
String url = "https://my_url/my_index_name";
StringRequest postRequest = new StringRequest(Request.Method.POST, url,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
try {
JSONObject jsonResponse = new JSONObject(response).getJSONObject("form");
String site = jsonResponse.getString("site"),
network = jsonResponse.getString("network");
Log.d("Site: ", site + "\nNetwork: "+network);
} catch (JSONException e) {
e.printStackTrace();
Log.e("JSON EXCEPTION", e.toString());
}
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
}
}
)
{
#Override
protected Map<String, String> getParams()
{
Map<String, String> params = new HashMap<>();
// the POST parameters:
params.put("type", "type_name");;
params.put("field1", field1_value);
params.put("field2", field2_value);
params.put("field3", field3_value);
params.put("field4", field4_value);
params.put("field5", field5_value);
return params;
}
};
Volley.newRequestQueue(this).add(postRequest);
It's hitting the server, as I'm getting 400 requests, but I just cant get it to go in using this.
12-14 16:19:04.533 12642-13089/? E/Volley﹕ [41831]
BasicNetwork.performRequest: Unexpected response code 400 for URL
Can some enlighten me as to what the correct syntax is, using java code, or tell me what I'm doing wrong?
I assumed the correct syntax was:
http:// url | index_name | type | body ?
EDIT: I've searched SO, elastic java api's, google groups and all over the internet for 3 days now, with no luck! Anything at all is greatly appreciated !
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
error.printStackTrace();
Log.e("VOLLEY ERROR", "" + error.toString());
}
}
Results in : VOLLEY ERROR﹕ com.android.volley.ServerError
Any ideas?
Ok, so I managed to fix it.
There was two issues:
The first issue was with the URL which was:
String url = "https://my_url/my_index_name";
It should have been:
String url = "https://my_url/my_index_name/type%20-d";
The second issue was with the actual arguments:
params.put("type", "type_name");;
params.put("field1", field1_value);
params.put("field2", field2_value);
params.put("field3", field3_value);
params.put("field4", field4_value);
params.put("field5", field5_value);
return params;
I swapped this out for :
String urlParameters =
"{" +
" \"field\": \" + field_value \"," +
" \"field\": \" + field_value \"," +
" \"field\": \" + field_value \"," +
" \"field\": \" + field_value \"," +
" \"field\": \" + field_value \"" +
"}";
And post by:
DataOutputStream wr = new DataOutputStream(httpConn.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
Everything is working as I want now, thanks for the help.