can not update Data in Kinvey Database - android

I am trying to update a record in kinvey collection but it keep saying insufficient credentials
i found a similar question on the forum that said i have to setGloballyWriteable to the acl of my model i have done that but still have the error
here is my code
public void loadMenuHelper(){
menu_list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
final Order order = my_orders[position];
order.getAcl().setGloballyWriteable(true);
order.getAcl().setGloballyReadable(true);
int requests = order.getRequests();
////////////have the handler
AlertDialog.Builder alert = new AlertDialog.Builder(
new ContextThemeWrapper(ImageTargets.this, R.style.AlertDialogCustom));
LinearLayout layout = new LinearLayout(ImageTargets.this);
layout.setOrientation(LinearLayout.VERTICAL);
alert.setTitle("There are " + requests + "Before you");
alert.setView(layout);
alert.setPositiveButton("Create", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
///////////////
order.getAcl().setGloballyWriteable(true);
order.getAcl().setGloballyReadable(true);
order.setRequests(order.getRequests() + 1);
AsyncAppData<Order> myevents = mKinveyClient.appData("Order", Order.class);
myevents.save(order, new KinveyClientCallback<Order>() {
#Override
public void onFailure(Throwable e) {
Log.i("TAG", "failed to save event data" + e.getMessage());
Log.i("TAG", sharedpreferences.getString("owner_name", ""));
}
#Override
public void onSuccess(Order r) {
Log.d("TAG", "saved data for entity " + r.getName());
Toast.makeText(getApplicationContext(), "Your Order was Created Sucessfully", Toast.LENGTH_SHORT).show();
}
});
}
////////////
});
alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// what ever you want to do with No option.
}
});
alert.show();
////////////////
}
});
///////////////
}

Considering that you have a user properly logged in, this seems to be related to collection permissions.
Your collection is in shared mode, that's why you are able to read the order object created by somebody else. But, because only creator can modify the object, you are getting "insufficient credentials" error.
You have two options here. The first option is to change collection permissions to public so that anybody can edit any order object. You can do that in Kinvey web console by going to settings tab in the data browser for the Order collection. After this, you won't need to use setGloballyWriteable/setGloballyReadable.
Other option (better suitable according to me) is to create another collection for requests for specific orders. In this new collection, you will be able to store a reference of the order object as well as store user information about the user who requested it.
You can find necessary documentation related to collection permissions here - http://devcenter.kinvey.com/android/guides/security

Related

How to stop Firebase database infinite looping

I am creating an app and part of it's features in user interaction. I want to store user comments on a post and I do not want to limit the amount of comments any one user can make. I do this by assigning a random number as the .setValue of the database entry.
With this implementation, whenever the comment is sent to the database it is stuck in an infinite loop where it will continually update with the same string entered in the text box but it will constantly generate new posts.
Full code;
sendComment.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
commitPost();
finish();
}
});
}
private void commitPost() {
commentProgress.setMessage("Posting");
commentProgress.show();
final String commentInput = commentText.getText().toString().trim();
if(TextUtils.isEmpty(commentInput)){
Snackbar.make(commentLayout,"You haven't finished your post yet",Snackbar.LENGTH_SHORT);
}else{
commentDB.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String uniqueId = UUID.randomUUID().toString();
commentDB.child(postID).child("Comments").child(uniqueId).setValue(commentInput);
commentProgress.dismiss();
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
}
The problem lies in the commentDB.addValueEventListener
The problem is that you set a listener for data changes and you also change data inside it (so it is called).
You don't need the listener, just add:
else {
String uniqueId = UUID.randomUUID().toString();
commentDB.child(postID)
.child("Comments")
.child(uniqueId)
.setValue(commentInput);
commentProgress.dismiss();
}

How deal with Firebase offline mode and data push?

When using Firebase, it is recommanded to use the push() command to manage data list. That's really fine, it provides a unique and ordered id for data pushed on the list.
However, when Firebase goes offline (goOffline or NetworkOffline), if the app try to push a data on a list, the completion listener is not triggered, until the app goes back online : so there is no unique id until the line is on again.
1/ Is that the expected/normal behavior ?
2/ I didn"t see in the document (as far I remember), that the push command is working differently (or only in onlinemode) in offline state. Did I miss a line somewhere ?
3/ My use case deal with data that own relationship. It means I want create an object (kind of master) in a list, and then reuse this master object id (provided by the completion listener) to build the relation between the master object and all other relevants objects. How may I deal with this offline state ?
Code Example :
findViewById(R.id.button3).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
new Firebase(getString(R.string.firebase_url)).child("stack").push().setValue(counter++, new Firebase.CompletionListener() {
#Override
public void onComplete(FirebaseError firebaseError, Firebase firebase) {
((TextView) findViewById(R.id.textView3)).setText(String.valueOf(counter) + " - " + firebase.toString());
}
});
}
});
Edit
Here is 4 ways to add data:
Push with Listener
findViewById(R.id.button3).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
new Firebase(getString(R.string.firebase_url)).child("stack").push().setValue(counter++, new Firebase.CompletionListener() {
#Override
public void onComplete(FirebaseError firebaseError, Firebase firebase) {
((TextView) findViewById(R.id.textView3)).setText(String.valueOf(counter) + " - " + firebase.toString());
}
});
}
});
SetValue with Listener
findViewById(R.id.button4).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
new Firebase(getString(R.string.firebase_url)).child("stackManual").child(UUID.randomUUID().toString()).setValue(counter++, new Firebase.CompletionListener() {
#Override
public void onComplete(FirebaseError firebaseError, Firebase firebase) {
((TextView) findViewById(R.id.textView4)).setText(String.valueOf(counter) + " - " + firebase.toString());
}
});
}
});
SetValue without Listener
findViewById(R.id.button5).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Firebase temp = new Firebase(getString(R.string.firebase_url)).child("stackManual").child(UUID.randomUUID().toString());
temp.setValue(counter++);
((TextView) findViewById(R.id.textView5)).setText(String.valueOf(counter) + " - " + temp.getKey().toString());
}
});
Push without Listener
findViewById(R.id.button6).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Firebase temp = new Firebase(getString(R.string.firebase_url)).child("stack").push();
temp.setValue(counter++);
((TextView) findViewById(R.id.textView6)).setText(String.valueOf(counter) + " - " + temp.getKey().toString());
}
});
IT APPEAR THAT THE LISTENERS ARE TRIGGERED ONLY WHEN THE DATA ARE IN THE DATABASE ON THE SERVER, AND NOT ON THE LOCAL DB !
- IS THAT CORRECT ?
- IS THAT DOCUMENTED ?
Because knowing that, a straight forward asynchronous application is more difficult to build now : encapsulation of asynchronous job may not be performed if offline, may it ?
so there is no unique id until the line is on again
This last statement is not true. Firebase push ids are generated client-side and are statistically guaranteed to be unique.
You can easily check this by splitting your operation:
Firebase ref = new Firebase(getString(R.string.firebase_url));
Firebase newStackRef = ref.child("stack").push();
System.out.println("New key/push id: "+newStackRef.key());
newStackRef.setValue(counter++, new Firebase.CompletionListener() {
The logging output will show you the new push id, even when you're not connected to the network.
IT APPEAR THAT THE LISTENERS ARE TRIGGERED ONLY WHEN THE DATA ARE IN THE DATABASE ON THE SERVER, AND NOT ON THE LOCAL DB! IS THAT CORRECT ?
The completion listeners will only trigger once the data has been committed on the server.
From the guide on saving data:
If you'd like to know when your data has been committed, you can add a completion listener. Both setValue() and updateChildren() take an optional completion listener that is called when the write has been committed to the database.
However, regular event listeners (like those created with addValueEventListener()) will be triggered both for local and remote edits. These are the listeners you should use to render your UI, etc.
Following what #Andrew-lee and #Frank-fan-puffelen (thank to both agree on that please), the solution appear to be :
Create a unique id with push() command
Add a listener (Single Value) to a firebase ref of the previous created node in (1.)
Schedule your job inside the listener (here update the UI, but could be another command)
findViewById(R.id.button7).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Firebase temp = new Firebase(getString(R.string.firebase_url)).child("stack").push();
temp.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
((TextView) findViewById(R.id.textView7)).setText(String.valueOf(counter) + " - " + dataSnapshot.getKey().toString());
}
#Override
public void onCancelled(FirebaseError firebaseError) {
}
});
temp.setValue(counter++);
}
});

How to create an AlertDialog before an sms text is sent?

I would like to know if it is possible to create an AlertDialog before a text message is sent from the default messaging app. I have looked at other people's questions on intercepting outgoing sms messages, however, those only seem to show how to read the message and not actually prevent it from being sent. I also do not want to create my own messaging app from scratch, so please do not suggest that. Thank you.
First of all create a new AlertDialog object like this
AlertDialog.Builder builder = new AlertDialog.Builder(this);
Now lets say the SMS you want to send is triggered by the click of a button. Lets call it sendSMS. Inside the onClick method set the parameters of the AlertDialog object like this :
sendSMS.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
builder.setMessage("You are about to send an SMS! are you sure you want to send it?")
.setTitle("Warning!")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(#SuppressWarnings("unused") final DialogInterface dialog, #SuppressWarnings("unused") final int id) {
//*******************************
//PUT HERE THE SMS SENDING CODE!!!
//*******************************
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(final DialogInterface dialog, #SuppressWarnings("unused") final int id) {
//closes the dialog; nothing interesting happens here
dialog.cancel();
}
});
final AlertDialog alert = builder.create();
alert.show();
}
});
return true;
}
This displays an AlertDialog with the click of a button, warning the user that he/she is about to send an SMS. If he/she clicks yes then, the SMS code is triggered. If he/she clicks no nothing happens.

Making Retrofit request from DialogFragment

I have implemented a login DialogFragment in my app (API 19). I'm using Retrofit 1.7.1 for networking. The implementation is below:
public class SignInDialogFragment extends DialogFragment {
private EditText mUsernameEditText;
private EditText mPasswordEditText;
public static SignInDialogFragment newInstance() {
return new SignInDialogFragment();
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final Builder builder = new AlertDialog.Builder(getActivity())
.setPositiveButton(R.string.action_sign_in, new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
String username = mUsernameEditText.getText().toString();
APIFactory.getAPI().register(username, new Callback<RegistrationResponseModel>() {
#Override
public void success(RegistrationResponseModel registrationResponse, Response response) {
Toast.makeText(getActivity(), "Totally worked!", Toast.LENGTH_SHORT).show();
}
#Override
public void failure(RetrofitError error) {
Toast.makeText(getActivity(), "Something failed!", Toast.LENGTH_SHORT).show();
}
});
}
})
.setNegativeButton(R.string.action_cancel, null);
View view = getActivity().getLayoutInflater().inflate(R.layout.fragment_sign_in, null);
builder.setView(view);
mUsernameEditText = (EditText)view.findViewById(R.id.usernameEditText);
return builder.create();
}
}
As you might predict, this crashes upon success or failure when getActivity() hits a null reference. What is the best way to handle an asynchronous request such as this, including getting callbacks after the dialog has been dismissed?
I thought of a few options; are any of them viable?
I could simply hold the dialog open if there is a request in progress. I haven't implemented this because I'm not sure of the cleanest way to keep the dialog around (handling the Back button, etc).
I would really like to just cancel the Request if the dialog is closed, but I can't, because there is not currently a way to cancel Retrofit requests.
If there is an option that I'm not aware of, I'd like to hear about it.
You should move your action out of Dialog. Dialog is not Activity - once user did a choice, dialog should be dismissed and if there's any action to trigger it should be done as response to user action with dialog, but it should not be part of dialog. Therefore you should just react to user choice in your parent Fragment or Activity, not in Dialog.

Android Licensing Server on a device

I'm trying to figure out the whole Android licensing thing, and getting frustrated.
In the emulator, I run the app with no account, or one that isn't in the testing environment, and it seems to work correctly, returning the not licensed response and pops up the buy the app now message.
When I try to run it on an actual Android device, it returns licensed every time, even though the device account isn't one that is in the testing environment.
Also, even though it returns licensed, the "checking license" box never goes away, unless you click cancel. Then it just lets you use the app as if it was licensed.
It's mostly C&P from the example, with a few changes. I removed the check license button and the status text box.
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler();
// Try to use more data here. ANDROID_ID is a single point of attack.
String deviceId = Secure.getString(getContentResolver(), Secure.ANDROID_ID);
// Library calls this when it's done.
mLicenseCheckerCallback = new MyLicenseCheckerCallback();
// Construct the LicenseChecker with a policy.
mChecker = new LicenseChecker(
this, new ServerManagedPolicy(this,
new AESObfuscator(SALT, getPackageName(), deviceId)),
BASE64_PUBLIC_KEY);
doCheck();
ArrayAdapter<String> booksAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, mBooks);
this.setListAdapter(booksAdapter);
}
protected Dialog onCreateDialog(int id) {
// We have only one dialog.
return new AlertDialog.Builder(this)
.setTitle(R.string.unlicensed_dialog_title)
.setMessage(R.string.unlicensed_dialog_body)
.setPositiveButton(R.string.buy_button, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Intent marketIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(
"http://market.android.com/details?id=" + getPackageName()));
startActivity(marketIntent);
finish();
}
})
.setNegativeButton(R.string.quit_button, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
finish();
}
})
.create();
}
private void doCheck() {
setProgressBarIndeterminateVisibility(true);
alertbox("status", getString(R.string.checking_license));
mChecker.checkAccess(mLicenseCheckerCallback);
}
protected void alertbox(String title, String mymessage)
{
new AlertDialog.Builder(this)
.setMessage(mymessage)
.setTitle(title)
.setCancelable(true)
.setNeutralButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton){}
})
.show();
}
private void displayResult(final String result) {
mHandler.post(new Runnable() {
public void run() {
alertbox("status", result);
setProgressBarIndeterminateVisibility(false);
}
});
}
private class MyLicenseCheckerCallback implements LicenseCheckerCallback {
public void allow() {
if (isFinishing()) {
// Don't update UI if Activity is finishing.
return;
}
// Should allow user access.
//displayResult(getString(R.string.allow));
}
public void dontAllow() {
if (isFinishing()) {
// Don't update UI if Activity is finishing.
return;
}
//displayResult(getString(R.string.dont_allow));
// Should not allow access. In most cases, the app should assume
// the user has access unless it encounters this. If it does,
// the app should inform the user of their unlicensed ways
// and then either shut down the app or limit the user to a
// restricted set of features.
// In this example, we show a dialog that takes the user to Market.
showDialog(0);
}
public void applicationError(ApplicationErrorCode errorCode) {
if (isFinishing()) {
// Don't update UI if Activity is finishing.
return;
}
// This is a polite way of saying the developer made a mistake
// while setting up or calling the license checker library.
// Please examine the error code and fix the error.
String result = String.format(getString(R.string.application_error), errorCode);
displayResult(result);
}
}
#Override
protected void onDestroy() {
super.onDestroy();
mChecker.onDestroy();
}
I just don't know what I need to change to make it work... or if the license is somehow cached (even though this is the first time I've run it on this device) and if I can uncache it without wiping the device, as that'll be nice for when I do testing on other apps.
Also, how do I remove the "checking license" message without having to click the cancel button... should I just make it so that it doesn't show up?
I'm just getting into licensing myself so don't take this as gospel but a few things stick out:
or if the license is somehow cached (even though this is the first time I've run it on this device) and if I can uncache it without wiping the device, as that'll be nice for when I do testing on other apps.
You are using the ServerManagedPolicy so the approval will be cached and obfuscated. This is the recommended way to do it. ( I assume to provide a better user experience and better response time ) In order to debug your approval you need to log into your market profile and change the "Test response" option. You need to use a device that has the same account as your publisher profile for the test response to work for a app that isn't released to the market yet.
You also have no code in your allow() method for your MyLicenseCheckerCallback class which should probably be where you clear the dialog (outside the isFinishing conditional).
if I can uncache it without wiping the device, as that'll be nice for when I do testing on other apps
Based on LicenseValidator.java It looks like the approval is stored in a prefs file at com.android.vending.licensing.ServerManagedPolicy in private mode. You can use the sharedpreferences editor to clear it from another place in the app.
Again I'm not a pro on this yet so I could be wrong but I think you might be able to troubleshoot your bug if you get it configured right.

Categories

Resources