I'm currently working on an Android-App and my task was to integrate Push-functionality with Parse.
When the user launches the app for the first time, he subscribes to the broadcast-channel by default.
if (!push_firstTime) {
ParsePush.subscribeInBackground("", new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
pushFirstTimePreferences.edit().putBoolean("push_enable", true).commit();
pushFirstTimePreferences.edit().putBoolean("push_firsttime", false).commit();
Log.d("com.parse.push", "successfully subscribed to the broadcast channel.");
} else {
Log.e("com.parse.push", "failed to subscribe for push", e);
}
}
});
}
But when I look at the Installation-Objects in the Parse Dashboard, I can see, that almost all channels are set to (undefined) and just about 5-10% of all objects have [""], respectively [] after unsubscribing.
Why is the majority of the channels set to (undefined)?
Any help is welcome! Thanks
I seem to have the same problem. Yesterday only 3 of 15 installations had a correct channel instead of undefined. Today this number has increased to 4. Maybe it is caused by app installs vs app runs? Automatic updates would install the app but not run it. Device registration could be part of an Android service (started automatically by the OS), while channel registration part of an activity (started manually by the user).
I'm also very interested if you happen to have solved this issue.
Related
I implemented an In-App Purchase with the Play Billing Library 1.0 according to Google's tutorial.
I have only 1 item for purchase and when it gets unlocked, I show a Toast message with the length Toast.LENGTH_SHORT. However, the Toast stays there for like 10 seconds, so I assume it gets called multiple times. It does NOT happen when I unlock it via queryPurchases (if someone bought it earlier and reinstalled the app in the meantime).
Anyone have an idea why the Toast stays so long / why it gets called multiple times?
Inside my BillingManager class:
#Override
public void onPurchasesUpdated(int responseCode, #Nullable List<Purchase> purchases) {
if (responseCode == BillingClient.BillingResponse.OK) {
for (Purchase purchase : purchases) {
handlePurchases(purchase);
}
mBillingUpdatesListener.onPurchasesUpdated(mPurchases);
} else if (responseCode == BillingClient.BillingResponse.USER_CANCELED) {
} else {
}
}
public void handlePurchases(Purchase purchase) {
//here could be validation on own server
mPurchases.add(purchase);
}
Main Activity implements BillingUpdatesListener:
#Override
public void onPurchasesUpdated(List<Purchase> purchases) {
for (Purchase purchase : purchases) {
switch (purchase.getSku()) {
case "premium":
unlockPremium();
break;
}
}
}
public void unlockPremium() {
mPremiumUnlocked = true;
savePremiumUnlocked();
Toast.makeText(this, getResources().getString(R.string.premium_congrats), Toast.LENGTH_SHORT).show();
mAdView.setVisibility(GONE);
}
If I understand your correctly, you say that when you first purchase the in-app product, you are getting multiple Toasts?
In the current version (1.0) of the Billing library, this happens because multiple broadcasts are being made by the system.
For example, if you look at or breakpoint onPurchaseFinishedReceiver at line 120 in BillingClientImpl.java within the library, this is called at least twice after making a purchase. Both times, the in-app purchase data is attached but I noticed that the intent Action was different for each broadcast.
In the first broadcast, the Action was com.android.vending.billing.PURCHASES_UPDATED but in the second it was proxy_activity_response_intent_action. The library does not filter out the Action values and so all of these broadcasts result in your purchasesUpdatedListener being called.
I didn't investigate further but I think what we can take from this is that SOME sort of change occurred and it was deemed necessary to broadcast that change.
To avoid your multiple toasts, just do not display the toast unless your Premium functionality is unlocked. i.e. If it is already unlocked, simply ignore the change notification.
By the way, it is totally possible to debug the purchase flow in Android Studio. Just sign your debug apk with your release key and make sure the apk version is not higher than the one in Play Store.
buildTypes {
debug {
minifyEnabled false
debuggable true
signingConfig signingConfigs.release
}
release {
minifyEnabled false
signingConfig signingConfigs.release
}
}
I don't know if this applies to your exact scenario, but we are experiencing the same thing and it is a bug on Google's end.
See https://issuetracker.google.com/issues/66054158 for more information.
Edit: I just saw that #goRGon posted the same thing :)
The example of multiple people in Spain isn't the same situation as what's described above. In the Spain scenario, the users are actually purchasing two copies of the IAP, so they are two separate receipts and the users should be rewarded with two copies of whatever they purchased. In the bug scenario, one single receipt is presented to the user twice so duplicates can actually be caught. But either way, back-end validation systems need to accommodate hackers/bugs in code that might cause the same receipt to be sent twice in a row.
If your subscription activity was closed and reopened multiple times within the same application process, then onPurchasesUpdated may be called multiple times if the PurchasesUpdatedListener somehow stays attached to the previous instances of billingClient and if the previous connections are still alive. I noticed that the number of times I closed and reopened the subscription activity, onPurchasesUpdated was called for the same amount after a successful launchBillingFlow.
To fix this I needed to end the connection when the activity is destroying, like this -
#Override
protected void onDestroy() {
super.onDestroy();
if (billingClient!= null) {
billingClient.endConnection();
}
}
Great answer and looking deep by #Kuffs!
Google will fix multiple calls soon: https://issuetracker.google.com/issues/66054158
However, your integration with billing flow should work even when onPurchasesUpdate was triggered multiple times, since it could happen anyway. For example, if somebody was buying in parallel on another device with the same #gmail account. And people in some countries (e.g. Spain) do share their #gmail accounts rather frequently with many friends and family members.
Please, check TrivialDrive_v2 implementation to get an idea, how handle such situations gracefully.
I hope It will help you
_purchaseUpdatedSubscription =
FlutterInappPurchase.purchaseUpdated.listen((productItem) {
print('purchase-updated: ${productItem}');
getDetails(productItem);
});
String orderId = '';
getDetails(PurchasedItem purchasedItem) {
if (purchasedItem != null) {
if (orderId != purchasedItem.orderId) {
orderId = purchasedItem.orderId;
print('productItem.transactionReceipt : ${purchasedItem.transactionReceipt}');
var decodedData = jsonDecode(purchasedItem.transactionReceipt);
print('purchaseState : ${decodedData['purchaseTime']}');
if (decodedData['purchaseState'] == 0) {
if(purchasedItem.productId == selected_package) {
print("Purchased successfully");
onPurchased(purchasedItem);
}
} else {
ShowMsg('Transaction Failed !, Something went wrong.');
}
}
}
}
I am trying to send a simple message from my Android wear app to my phone app using the Wearable.MessageApi.
This is my onConnected callback from GoogleApiClient on the Wear device.
final PendingResult<Status> status = Wearable.DataApi.addListener(googleApiClient, this);
status.setResultCallback(new ResultCallback<Status>() {
#Override
public void onResult(Status status) {
if (!status.isSuccess()) {
return;
}
NodeApi.GetConnectedNodesResult nodes =
Wearable.NodeApi.getConnectedNodes(googleApiClient).await();
for (Node node : nodes.getNodes()) {
System.out.println("Sending message: " + node.getDisplayName());
final MessageApi.SendMessageResult result =
Wearable.MessageApi.sendMessage(googleApiClient, node.getId(),
"request", "12345".getBytes())
.await();
System.out.println("sent: " + result.getStatus().isSuccess());
}
}
});
And this is displaying the following when ran
Sending message: Nexus 6P
sent: true
And this is my registered service on my app:
public class MyWearableListenerService extends WearableListenerService {
#Override
public void onMessageReceived(MessageEvent messageEvent) {
Toast.makeText(this, "Received message", Toast.LENGTH_LONG).show();
}
#Override
public void onPeerConnected(Node peer) {
Toast.makeText(this, "Peer connected", Toast.LENGTH_LONG).show();
}
}
I properly verified that the Peer connected toast is showing up when the emulator is connected to my device. I properly did the port forwarding to debug on wear emulator. I checked that my applicationId and package names are consistent across my app and wear app. However, I never get the onMessageReceived callback on my device.
Any suggestions are greatly appreciated! I've been debugging this for a whole day now :(
Oh, good god.. I figured out my problem. I THOUGHT the applicationId was the same, but it turned out that I never set up build flavors on the wear module, so the two applicationIds were actually com.example.android and com.example.android.dev..
Hope this helps other people who ran into the same problem as me :\
I was having the same issue, due to (very) poor and outdated documentation on the Android Developer website. I was adding a Wear app to an existing app that's been around for years. As such, I have been using a custom debug.keystore for years now in my main app.
When I made the Wear app, I did not update the build.gradle to use the same debug.keystore file as the regular app - once I did that, I started receiving messages from the Watch -> Phone!
Here is a checklist to review if you're having the same issue as me and the OP:
Wear and Phone apps need same applicationId
Same versionNumber and versionName between apps
Signed by the same key (this was what fixed my issue)
I just copied the "signingConfigs" section from my app's build.gradle to the wear app's build.gradle
signingConfigs {
debug {
storeFile file('../app/debug.keystore')
}
}
This issue cost me an entire day, hopefully someone else finds this useful.
Another possible trap, in your WearableListenerService, don't forget the call to super:
#Override
public void onCreate() {
super.onCreate(); // <-- don't forget this
...
}
Push notifications from parse.com is not consistently working. Randomly push notifications will fail, resulting in a GCM - MISMATCH SENDER ID" error. It is my understanding that programmatically we do not have to do anything with the GCM because parse.com sends the objectId to GCM. In either case, I have not been able to pinpoint any specific reason why this error occurs sometimes and other times it doesn't. Additionally, I am using Parse version, 1.10.2.
My Application class has the following
Parse.initialize(this, APPLICATION_ID_DEBUG, CLIENT_KEY_DEBUG);
Parse.setLogLevel(Parse.LOG_LEVEL_VERBOSE);
ParsePush.subscribeInBackground(Constants.CHANNEL, new SaveCallback() {
#Override
public void done(ParseException e) {
if (Utils.checkIfNull(e)) {
// subscribed to channel
} else {
// failed to subscribe to channel
}
}
});
After the user logs into my app I attach a channel to them. The channel data I save is just the user's unique id I am getting from server.
List<String> arryChannel = new ArrayList<>();
arryChannel.add(uniqueUserId);
final ParseInstallation parseInstallation = ParseInstallation.getCurrentInstallation();
parseInstallation.put(Constants.CHANNEL, arryChannel);
parseInstallation.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (Utils.checkIfNull(e)) {
// update channel with user's unique id
} else {
// failed to update channel with user unique id
}
}
});
Finally, when the user logs out I unsubscribe them from their channel. I added unsubscribe to try and prevent any one device from receiving multiple push notifications because they have logged in as multiple users into the app and subscribed to multiple channels. The following is how my code looks when you log out.
ParsePush.unsubscribeInBackground(Constants.CHANNEL, new SaveCallback() {
#Override
public void done(ParseException e) {
if (Utils.checkIfNull(e)) {
// successfully unsubscribed to channel
// save the updated (unsubscribed) parse installation
final ParseInstallation parseInstallation = ParseInstallation.getCurrentInstallation();
parseInstallation.put(Constants.CHANNEL, new ArrayList<String>());
parseInstallation.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (Utils.checkIfNull(e)) {
// add whatever logs here to check for any issues with unsubscribing
} else {
// failed to update channel
}
}
});
} else {
Logger.e("PARSE", "failed to unsubscribed to channel: " + e.getMessage());
}
}
});
The result of this implementation is that when push notifications are not working, it will continue to fail for about 50-100 times. Then it will start working for about 150-200 times. Then it goes back to not working. It is not a work, not-work type back and forth. It is more of a fail, fail, fail multiples times and then success, success, success multiple times. Any help on what I am missing in my implementation is appreciated. Thanks in advance.
I have finally figured out the answer to this question! The issue had nothing to do with my implementation. For anyone else experiencing this same conflict, please look for any other 3rd party services that also are using push notifications. For me, Mixpanel was the culprit. When I deleted mixpanel.initPushHandling() from my codebase, all started working. And this makes sense, because when you initialize push notifications for mixpanel, you pass in a value that is used for GCMSenderID. Parse push notifications work differently. With parse.com, you do not have to send a GCMSenderID, because parse will automatically send in an objectId to perform their push notifications. Between the two, this causes a GCM-MISMATCH-SENDER error.
So the solution is, remove any sort of services that may be conflicting with parse.com. And feel free to use my implementation, it is good. Cheers!
I faced the problem and after some rummage, finally found the solution. As Parse said in it's docs you should provide each Sender_ID that your app uses to push messages, if you use another push provider in addition to Parse. Take a look at below:
The Parse Android SDK chooses a reasonable default configuration so that you do not have to worry about GCM registration ids, sender ids, or API keys. In particular, the SDK will automatically register your app for push at startup time using Parse's sender ID (1076345567071) and will store the resulting registration ID in the deviceToken field of the app's current ParseInstallation.
However, as an advanced feature for developers that want to send pushes from multiple push providers, Parse allows you to optionally register your app for pushes with additional GCM sender IDs. To do this, specify the additional GCM sender ID with the following <meta-data> tag as a child of the <application> element in your app's AndroidManifest.xml:
<meta-data android:name="com.parse.push.gcm_sender_id"
android:value="id:YOUR_SENDER_ID" />;
In the sample snippet above, YOUR_SENDER_ID should be replaced by a numeric GCM sender ID. Note that the Parse SDK expects you to prefix your sender ID with an id: prefix, as shown in the sample snippet.
If you want to register your app with multiple additional sender IDs, then the android:value in the <meta-data> element above should hold a comma-delimited list of sender IDs, as in the following snippet:
<meta-data android:name="com.parse.push.gcm_sender_id"
android:value="id:YOUR_SENDER_ID_1,YOUR_SENDER_ID_2,YOUR_SENDER_ID_3" />;
I am developing an android app In that everything is working fine. I just want to ask that is there any way to unsubscribe from all the parse channel in one call instead of using :
ParsePush.unsubscribeInBackground("channel1");
checked on google and parse android api didn't find any way to do that.
Channels information are written in the Installation class. Simply clear the "channels" column.
ParseInstallation installation = ParseInstallation.getCurrentInstallation();
installation.remove("channels");
installation.saveEventually(new SaveCallback() { // or saveInBackground
#Override
public void done(ParseException e) {
// TODO: add code here
}
});
I have an Android app published in the Google Play Store and I want to add Push notification.
I tried Parse.com and I Implemented the code mentioned in this guide.
When I tried to send a test push, Parse.com says: `No clients have subscribed to the broadcast push channel. Please double check that you've setup the SDK correctly.
Also, when I go to the parse cloud
DASHBOARD --> Push Notifications --> Send A Push
Parse says:
Your application does not have any registered devices to which it can send notifications.
I'm sure that I've implemented ALL the code correctly!
I think that you forgot (as the error message say tell you) to sign up with the device!
ParseUser user = new ParseUser();
user.setUsername("jondoe");
user.setPassword("123456");
user.setEmail("jon#gmail.com");
user.signUpInBackground(new SignUpCallback() {
public void done(ParseException e) {
if (e == null) {
// Hooray! Let them use the app now.
} else {
// Sign up didn't succeed. Look at the ParseException
// to figure out what went wrong
}
}
#Override
public void done(com.parse.ParseException arg0) {
// TODO Auto-generated method stub
}
});
After you device is registred the system knows to whom send notification!
Also, in your Parse.com dashboard in the tab "Data browser" you must see a row if you click "installation" on the left.