Following instructions from: https://developer.android.com/training/in-app-billing/purchase-iab-products.html under Query Purchased Items, I am checking for purchased items.
My code looks like this:
private void lookForPurchases() {
IabHelper.QueryInventoryFinishedListener lookForPurchasesListener = new IabHelper.QueryInventoryFinishedListener() {
public void onQueryInventoryFinished(IabResult result, Inventory inventory) {
if (result.isFailure()) {
Log.e(TAG, "Error checking for purchases:" + result.toString());
}
else {
Log.d(TAG, "Processing purchases." + inventory.toString());
if(inventory.hasPurchase(SKU_LEXCOINS_100)){
Purchase purchase = inventory.getPurchase(SKU_LEXCOINS_100);
consumeCoinPurchase(purchase);
}
if(inventory.hasPurchase(SKU_LEXCOINS_550)){
Purchase purchase = inventory.getPurchase(SKU_LEXCOINS_550);
consumeCoinPurchase(purchase);
}
if(inventory.hasPurchase(SKU_LEXCOINS_1200)){
Purchase purchase = inventory.getPurchase(SKU_LEXCOINS_1200);
consumeCoinPurchase(purchase);
}
}
}
};
Log.i(TAG, "Looking for purchases");
if(inAppBillingHelper == null) {
Log.e(TAG, "Null preventing query inventory");
} else {
try {
inAppBillingHelper.queryInventoryAsync(lookForPurchasesListener);
} catch (IabHelper.IabAsyncInProgressException ex) {
Log.e(TAG, "Error retrieving purchases.", ex);
}
}
}
In the log, though, the last thing I get is "Looking for purchases" with nothing following. It looks like I should get something on any branch, so, does anyone see what I'm doing wrong?
No errors or anything, it just never seems to come back.
The problem was on the line handler.post deep in IabHelper (Google Code) where it was executing my callback I was passing in. The handler.post was getting called, buy my callback never was. I'm not sure how that happened.
I replaced the handler.post with a AsyncTask and posted the call of my callback in the onPostExecute method to get it run on the UI thread. This appears to have solved my problem.
Related
I'm very new to Java, android and Rxjava. I recently noticed that in an existing project (not written by me) a chat notification that is supposed to be received isn't received. Thus I started to do some tracing. Below is part of the code.
Note: Notifications that do get received seems to always go to onSuccess in the file FCMServices
I've put breakpoints pretty much everywhere in the code below. What I noticed was for the notifications that I do not receive onSuccess and onError do not get called but onComplete does. However I find that strange as I thought either onSuccess or onError must be called before onComplete.
My understanding of those functions is based on http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/MaybeObserver.html
//FCMService.java
currentConversationRepo.getCurrentConversation()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new MaybeObserver<CurrentConversation>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
currentChatDisposable = d;
}
#Override
public void onSuccess(#NonNull CurrentConversation currentConversation) {
System.out.println("This is SUCCESS");
if (channelSid == null && author == null && usedAdId == null){
buildNotifyNotification(body, action, "", userId);
}
if (channelSid != null && author != null) {
if (!channelSid.equals(currentConversation.getChannelSid())) {
createChatNotification(author, channelSid, body);
}
}
currentChatDisposable.dispose();
}
#Override
public void onError(#NonNull Throwable e) {
System.out.println("Error getting current conversation: " + e.getMessage());
currentChatDisposable.dispose();
}
#Override
public void onComplete() {
System.out.println("This is onComplete");
currentChatDisposable.dispose();
}
});
I then started to do some tracing of where onComplete was called and appears that it was called by another onSuccess from the class TestObserver in reactivex.io
http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/observers/TestObserver.html
//TestObserver.java
#Override
public void onSuccess(T value) {
onNext(value);
onComplete();
}
Which was in turn called by the onSuccess in MaybeFlatMapBiSelector class. (Also a reactivex.io class I believe)
//MaybeFlatMapBiSSelector.java
#Override
public void onSuccess(U value) {
T t = this.value;
this.value = null;
R r;
try {
r = ObjectHelper.requireNonNull(resultSelector.apply(t, value), "The resultSelector returned a null value");
} catch (Throwable ex) {
Exceptions.throwIfFatal(ex);
actual.onError(ex);
return;
}
actual.onSuccess(r);
}
This turned out to be from the MaybeObserver interface
http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/MaybeObserver.html#onComplete--
My question is what exactly are the onSuccess of TestObserver and MaybeFlatMapBiSelector doing? And if it is even possible based on the information I have provided, why is it that some notifications goes to onComplete without going to onSuccess or onError in FCMServices.java
Have you tried to comment currentChatDisposable.dispose(); ? I've had the same issue not long ago where I was disposing of my disposable too early and no data where showing
Usually you call .dispose() when onPause() or onDestroy() of the lifecycle
PS: In case you didn't know Maybe in RxJava return either a single value, nothing at all or an exception.
I am new to Android’s background tasks. I am using Firestore to perform the following tasks:
Read a document.
https://firebase.google.com/docs/firestore/query-data/get-data
DBInstance.collection("restaurants")
.get()
.addOnCompleteListener(task -> {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : Objects.requireNonNull(task.getResult())) {
// some other code
}
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
}
});
Listen to realtime updates of another document. https://firebase.google.com/docs/firestore/query-data/listen
final DocumentReference docRef = DBInstance.collection("users").document(FirebaseAuth.getInstance().getCurrentUser().getUid());
docRef.addSnapshotListener(new EventListener<DocumentSnapshot>() {
#Override
public void onEvent(#Nullable DocumentSnapshot snapshot,
#Nullable FirebaseFirestoreException e) {
if (e != null) {
Log.w(TAG, "Listen failed.", e);
return;
}
String source = snapshot != null && snapshot.getMetadata().hasPendingWrites()
? "Local" : "Server";
if (snapshot != null && snapshot.exists()) {
Log.d(TAG, source + " data: " + snapshot.getData());
// some other code is run
} else {
Log.i(TAG,"no snapshot found");
}
}
});
Since these are asynchronous processes, they are performed at the same time (roughly).
I want to trigger an independent method when 1. is completed AND when 2. return a non-null snapshot. Therefore, when some other code comments above have been completed.
So, I essentially want some background process that sits idle/ listens for the above two conditions and perform a task/call a method that updates certain UI features.
I have briefly read about BroadcastReciever. Is this relevant? or maybe can I create a custom listener that runs in a background thread? Any suggestions would be helpful since I am not sure what to search for in order to find what I want.
solutions that seems to work (partly suggested by Nehal)
This is the same code as above with the blanks filled in
DBInstance.collection("restaurants")
.get()
.addOnCompleteListener(task -> {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : Objects.requireNonNull(task.getResult())) {
restaurantsLoaded = true;
updateUI();
}
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
restaurantsLoaded = false;
}
});
final DocumentReference docRef = DBInstance.collection("users").document(FirebaseAuth.getInstance().getCurrentUser().getUid());
docRef.addSnapshotListener(new EventListener<DocumentSnapshot>() {
#Override
public void onEvent(#Nullable DocumentSnapshot snapshot,
#Nullable FirebaseFirestoreException e) {
if (e != null) {
Log.w(TAG, "Listen failed.", e);
return;
}
String source = snapshot != null && snapshot.getMetadata().hasPendingWrites()
? "Local" : "Server";
if (snapshot != null && snapshot.exists()) {
Log.d(TAG, source + " data: " + snapshot.getData());
usersSnapshotTriggered = true;
udpateUI();
} else {
Log.i(TAG,"no snapshot found");
}
}
});
public void updateUI(){
if(usersSnapshotTriggered && restaurantsLoaded){
// perform the updates
}
}
You can try below solution:
Declare a global int variable, increment that variable in both firebase listener and call someMethod() from both listener.
private int count=0;
DBInstance.collection("restaurants")
.get()
.addOnCompleteListener(task -> {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : Objects.requireNonNull(task.getResult())) {
// some other code
}
count++;
someMethod();
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
}
});
final DocumentReference docRef = DBInstance.collection("users").document(FirebaseAuth.getInstance().getCurrentUser().getUid());
docRef.addSnapshotListener(new EventListener<DocumentSnapshot>() {
#Override
public void onEvent(#Nullable DocumentSnapshot snapshot,
#Nullable FirebaseFirestoreException e) {
if (e != null) {
Log.w(TAG, "Listen failed.", e);
return;
}
String source = snapshot != null && snapshot.getMetadata().hasPendingWrites()
? "Local" : "Server";
if (snapshot != null && snapshot.exists()) {
Log.d(TAG, source + " data: " + snapshot.getData());
// some other code is run
count++;
someMethod();
//Note : this method will call as many times as there is change in this data , so you have to handle according to your requirement
} else {
Log.i(TAG,"no snapshot found");
}
}
});
private void someMethod(){
if(count>=2){
//execute your code
}
}
Hope this will help!!
I want to trigger an independent method when both 1. and 2. have been completed
In your first example, by adding a complete listener, you'll always be able to know when the operation is complete. If the task.isSuccessful() returns true you know for sure that the operation is completed. Besides that, you can also call getResult() to get the elements that are apart of your restaurants collection. Furthermore, the following line of code:
DBInstance.collection("restaurants")
.get()
Returns a Task<QuerySnapshot> object. If you have had two different queries, you could pass both Task objects to Tasks's whenAllSuccess() method, as explained in my answer from the following post:
Firestore - Merging two queries locally
In this way, you'll be able to know when both operations are completed. However, when using the second solution, you cannot know when getting the data from the database is completed because Cloud Firestore is a real-time database and getting data might never complete. That's why is named a real-time database because at any moment the database can be changed, items can be added or deleted.
The only way to partially know if you have all the data in a particular collection is to perform a single value type query on it. Even then, the data may change after that listener is invoked, so all you really have is a snapshot at a particular moment in time.
As a conclusion, the only solution that you have is to use whenAllSuccess() and pass two or even more Task objects.
I have briefly read about BroadcastReciever. Is this relevant?
No, it's not. According to the docs, the BroadcastReceiver class is:
Base class for code that receives and handles broadcast intents sent by Context.sendBroadcast(Intent).
So, it's not the case.
or maybe can I create a custom listener that runs in a background thread?
The Cloud Firestore client already runs all network operations in a background thread. This means that all operations take place without blocking your main thread. Putting it in a background thread does not give any additional benefits.
swipeRefreshLayout.setOnRefreshListener(() -> {
swipeRefreshLayout.setRefreshing(true);
retrieveData(mCardAdapter, db);
});
For some reason the following method is blocking my main UI thread, but it should be running in the background. For example, the refresh indicator hangs when I run retrieveData(). If I initialize a progress dialog before running, it also hangs and I can't scroll through my RecyclerView. Am I fundamentally misunderstanding something here?
public void retrieveData(final CardAdapter mCardAdapter, SQLiteHelper db) {
CausticRetrofitService service = ServiceFactory.createRetrofitService(CausticRetrofitService.class, CausticRetrofitService.SERVICE_ENDPOINT);
service.getMedia()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber < MediaResponse > () {
#Override
public final void onCompleted() {
Log.e("CausticRetrofitService", "Caustic Request Completed!");
/* Cancel all progress indicators after data retrieval complete */
setRefreshingFalse();
// TODO: Add media to local data store and then display them one-by-one in real-time
mCardAdapter.addData(db.getAllMediaImages()); // Add all media images to card views
Log.d(getClass().toString(), "Added to local database: " + db.getAllMediaImages());
mCardAdapter.notifyDataSetChanged();
}
#Override
public final void onError(Throwable e) {
/* Cancel all progress indicators on data retrieval error */
setRefreshingFalse();
Toast.makeText(getApplicationContext(), "Cannot retrieve data. Please try again later.", Toast.LENGTH_SHORT).show();
Log.e("CausticRetrofitService", e.getMessage());
}
#Override
public final void onNext(MediaResponse mediaResponse) {
if (mediaResponse != null) {
Log.e("CausticRetrofitService", "Returned objects: " + mediaResponse.getResults());
for (String mediaId: mediaResponse.getResults()) {
Log.e("CausticRetrofitService", mediaId);
}
List < String > mediaIds = mediaResponse.getResults();
Log.d(getClass().toString(), "All Media IDs: " + mediaIds);
if (mediaIds.isEmpty()) {
Toast.makeText(getApplicationContext(), "Cannot retrieve data. Please try again later.", Toast.LENGTH_SHORT).show();
}
mCardAdapter.clear();
mCardAdapter.notifyDataSetChanged();
/* Store objects from remote web service to local database */
for (String mediaId: mediaIds) {
// TODO: Why are these null?
Log.d(getClass().toString(), "Media Id: " + mediaId);
MediaImage newMediaImage = new MediaImage();
newMediaImage.setTitle(mediaId);
db.addMediaImage(newMediaImage); // Add media image to local database
}
} else {
Log.e("CausticRetrofitService", "Object returned is null.");
}
}
});
}
I'm thinking that adding the remote data to the local data store in the onNext() method might be the thing that's blocking, although I'm not certain.
Your network call is done in a new thread as you specified, but the Subscriber methods onNext() and onComplete() runs on the observing Scheduler, which is the main thread.
You seem to be doing some database operations on those, try to offload the caching also to the background thread using a doOnNext() operator.
What doOnNext() will do, is that it is called for each emission in your stream.
It can go something like that
service.getMedia()
.doOnNext(data -> cacheData(data))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
Where cacheData() is a method that does all your DB calls. And the only things left in your onNext() and onComplete() would be updating the UI only.
I have a single incident where a complete duplicate of a entry was made into the database (the same user comment appeared twice). They had different object IDs but were otherwise the exact same. It was slower than usual to finish the posting and only happened once out of dozens of comments, so I want to say it was a Parse issue during the saveInBackground call. Even so, I expect a service like Parse to be a little more robust. As my first time working with Android though, I also can't be sure nothing is wrong on my end. Any help? Also just any criticisms? This is the method called when the user hits a comment submission button:
private void submitComment() {
String text = commentText.getText().toString().trim();
Intent intent = getIntent();
String ID = intent.getStringExtra("imageID");
String parentID = intent.getStringExtra("parent");
// Set up a progress dialog
final ProgressDialog loadingDialog = new ProgressDialog(CommentSubmitActivity.this);
loadingDialog.setMessage(getString(R.string.publishing_comment));
loadingDialog.show();
Comment comment = new Comment();
comment.setText(text);
comment.setUser((ParseUser.getCurrentUser()));
if (ID.equals("#child")) {
comment.setParent(parentID);
comment.setImage("#child");
ParseQuery<ParseObject> query = ParseQuery.getQuery("Comment");
query.getInBackground(parentID, new GetCallback<ParseObject>() {
public void done(ParseObject parentComment, ParseException e) {
if (e == null) {
int numChild = parentComment.getInt("numChild");
parentComment.put("numChild", ++numChild);
parentComment.saveInBackground();
} else {
Log.d("numChild: ", "error");
}
}
});
} else {
comment.setImage(ID);
comment.put("numChild", 0);
ParseQuery<ParseObject> query = ParseQuery.getQuery("ImageUpload");
query.getInBackground(ID, new GetCallback<ParseObject>() {
public void done(ParseObject image, ParseException e) {
if (e == null) {
int numComments = image.getInt("numComments");
image.put("numComments", ++numComments);
image.saveInBackground();
} else {
Log.d("numComments: ", "error");
}
}
});
}
comment.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
loadingDialog.dismiss();
finish();
}
}
});
}
I encountered similar problem like yours.
I created an app where user can create account and add photo to it and list of objects (friends in my case).
Once when I was testing it user was created twice.
I went through my code and my my suspicions are connected with async calls.
I see that you use asynchronous parse api in you application so no fragment of code is waiting for response and blocking the rest of operations.
You cannot control when parse server will response.
What I did I just put all synchronous requests in my custom async code (AsyncTask in Android).
Hope that my answer somehow meeets your expectations.
I have multiple methods to be called when activity is started. I have added those methods in the oncreate method. The problem is when the activity is started some methods are called some or not called. How do i call all the methods when the activity is started.
My code is
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
AsyncHttpClient client = new AsyncHttpClient();
RequestParams params = new RequestParams();
client.post("http://localhost/website/getdbrowcount.php",params ,new AsyncHttpResponseHandler()
{
public void onSuccess(String response)
{
try
{
Log.d("home", "success");
JSONObject obj = new JSONObject(response);
Log.d("home", obj.toString());
System.out.println(obj.get("count"));
syncDB();
sync();
subsync();
syncfeature();
syncelec();
syncconnector();
synccontrols();
synckeypad();
syncmech();
syncorder();
syncpower();
}
catch (JSONException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void onFailure(int statusCode, Throwable error,String content)
{
if(statusCode == 404)
{
update.setText("The update has been cancelled. Please update via Settings to work"
+ " with latest Sonetonix product data");
Toast.makeText(getApplicationContext(), "Requested resource not found", Toast.LENGTH_LONG).show();
btn1.setEnabled(true);
btn1.setTextColor(Color.parseColor("#FFFFFF"));
}
else if(statusCode == 500)
{
update.setText("The update has been cancelled. Please update via Settings to work"
+ " with latest Sonetonix product data");
Toast.makeText(getApplicationContext(), "Something went wrong at server end", Toast.LENGTH_LONG).show();
btn1.setEnabled(true);
btn1.setTextColor(Color.parseColor("#FFFFFF"));
}
else
{
update.setText("The update has been cancelled. Please update via Settings to work"
+ " with latest Sonetonix product data");
Toast.makeText(getApplicationContext(), "Unexpected Error occcured! [Most common Error: Device might not be connected to Internet]", Toast.LENGTH_LONG).show();
btn1.setEnabled(true);
btn1.setTextColor(Color.parseColor("#FFFFFF"));
}
Log.d("home", "failure");
}
});
}
In the code when OnSuccess the methods has to be called but only syncDB(),sync() is called and rest are not called . What change should i make in the code to resolve this issue.
Please help
It is because in the sync() method or possibly at the start of the subsync() method your program is throwing an error. Because of the try/catch block, you are allowing the program to continue.
Check the method for an error and fix that.