How do I auto increment a value stored in Firebase from an Android client?
Currently: I declare int id = 1. When I increment, I see the values 2, 3 etc. being stored. That's fine, but when I re-run the project, id is set equal to 1 again.
I want it to behave like a static variable, so I can create an id which will go from 1 to infinity without resetting.
UPDATED FAILED
I used the following to pass the Firebase reference and a string to the function incrementCounter.
if(language_chosen.equalsIgnoreCase("english"))
{
Firebase publRef = f.child("Language").child("English").child("Message");
Firebase newPublRef = publRef.push();
writeMsgActivity demo = new writeMsgActivity();
demo.incrementCounter(newPublRef,the_msg_enter);
}
Now I try to use the passed reference and string at public void incrementCounter(Firebase publref,String my_msg) in the oncomplete method but it gives me an error.
public void incrementCounter(Firebase publref,String my_msg) {
publref.runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(final MutableData currentData) {
if (currentData.getValue() == null) {
currentData.setValue(1);
} else {
currentData.setValue((Long) currentData.getValue() + 1);
}
return Transaction.success(currentData);
}
public void onComplete(FirebaseError firebaseError, boolean committed, DataSnapshot currentData) {
if (firebaseError != null) {
System.out.println("Firebase counter increment failed.");
} else {
System.out.println("Firebase counter increment succeeded.");
Map<String, Object> publ = new HashMap<String, Object>();
publ.put("pubMsg", my_msg);
publ.put("id",currentData);
publref.setValue(publ);
}
}
});
}
UPDATED SOLVED
final Firebase upvoteref = new Firebase("https://shareurday.firebaseio.com/Message/"+msg_id+"/upvotes");
upvoteref.runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(final MutableData currentData) {
if (currentData.getValue() == null) {
currentData.setValue(1);
} else {
currentData.setValue((Long) currentData.getValue() + 1);
}
return Transaction.success(currentData);
}
public void onComplete(FirebaseError firebaseError, boolean committed, DataSnapshot currentData) {
if (firebaseError != null) {
System.out.println("Firebase counter increment failed.");
} else {
System.out.println("Firebase counter increment succeeded.");
}
}
});
The variable msg_id is the random generated id from push.
Here's an example method that increments a single counter. The key idea is that you are either creating the entry (setting it equal to 1) or mutating the existing entry. Using a transaction here ensures that if multiple clients attempt to increment the counter at the same time, all requests will eventually succeed. From the Firebase documentation (emphasis mine):
Use our transactions feature when working with complex data that could be corrupted by concurrent updates
public void incrementCounter() {
firebase.runTransaction(new Transaction.Handler() {
#Override
public Transaction.Result doTransaction(final MutableData currentData) {
if (currentData.getValue() == null) {
currentData.setValue(1);
} else {
currentData.setValue((Long) currentData.getValue() + 1);
}
return Transaction.success(currentData);
}
#Override
public void onComplete(FirebaseError firebaseError, boolean committed, DataSnapshot currentData) {
if (firebaseError != null) {
Log.d("Firebase counter increment failed.");
} else {
Log.d("Firebase counter increment succeeded.");
}
}
});
}
A new feature is added to firestore to autoincrement values. As simple as
document("fitness_teams/Team_1").
updateData(["step_counter" : FieldValue.increment(500)])
refer link:
https://firebase.googleblog.com/2019/03/increment-server-side-cloud-firestore.html
Used below code in my project.
var firestore = Firestore.instance;
firestore.collection('student').document('attendance').updateData({'total_attendace': FieldValue.increment(1)});
Related
So I have quite a few model classes in my project and have a singleton class which makes Retrofit calls to convert between the model classes. The method getUsersFromChats in that class takes in an array of Chat model objects, converts each object to a User model object, and adds them to a public ArrayList of User objects.
Code:
//These are instantiated in another method and are not null.
public static ArrayList<User> users;
private static int index;
public void getUsersFromChats(Chat[] chats) {
if (index < chats.length) {
Call<UserResponse> getUser = AuthRetrofitClient
.getInstance()
.getAuthApi()
.getUserById(chats[index].getUserID());
getUser.enqueue(new Callback<UserResponse>() {
#Override
public void onResponse(Call<UserResponse> call, Response<UserResponse> response) {
if (response.isSuccessful()) {
UserResponse ur = response.body();
Log.i("User", ur.getUser().getName());
users.add(ur.getUser());
Log.i("List", users.toString());
index++;
getUsersFromChats(chats);
} else {
try {
String errorBody = response.errorBody().string();
int index = errorBody.indexOf("\"message\":");
if (index != -1) {
String errorSub = errorBody.substring(index + 10);
//errorBody = errorSub.substring(1, errorSub.length() - 2);
}
Toast.makeText(context, errorBody, Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Override
public void onFailure(Call<UserResponse> call, Throwable t) {
Log.i("Didn't work", t.toString());
}
});
}
}
I have an Activity which calls that method after getting an array of Chat objects from a Retrofit call and tries to save ArrayList from the singleton class to a local User objects ArrayList.
Call<ChatListResponse> getAllContacts = ChatRetrofitClient
.getInstance()
.getChatAPI()
.loadChats(SharedPrefManager.getInstance(ChatActivity.this).getUser().getToken());
getAllContacts.enqueue(new Callback<ChatListResponse>() {
#Override
public void onResponse(Call<ChatListResponse> call, Response<ChatListResponse> response) {
if(response.isSuccessful()){
ChatListResponse clr = response.body();
if(clr.getChats() == null || clr.getChats().length < 1)
createChat();
else{
for(Chat c: clr.getChats())
Log.i("Testing", c.getUserID());
ConvertFieldsToUserObjects.getInstance(ChatActivity.this).getUsersFromChats(clr.getChats());
ArrayList<User> users = ConvertFieldsToUserObjects.users;
Log.i("Testing", users.toString());
for(User u: users)
Log.i("Testing", u.getId());
Log.i("Testing", person.getId());
if(users.contains(person)){
int i = users.indexOf(person);
chat = clr.getChats()[i];
Toast.makeText(ChatActivity.this, chat.getName(), Toast.LENGTH_SHORT).show();
}
However, this code doesn't work and the local ArrayList is always empty even though the Chat objects list obtained from the Call contains elements. Does this have something to do with the Retrofit calls being asynchronous or is it something else? (The API's, RetrofitClients, and the Response objects work fine as far as I have tested.)
I implemented a paging library and it does not work quite as it should. I request data from the github and paginate the list of repositories. The code works well, but after several changes to the search query, it stops loading data. In the debug, the data always loads well. I guess the problem is asynchrony, but I can’t figure out where to look.
My code:
RepoDataSource
public class RepoDataSource extends PageKeyedDataSource<Integer, Repo> {
#Override
public void loadInitial(#NonNull LoadInitialParams<Integer> params, #NonNull LoadInitialCallback<Integer, Repo> callback) {
Timber.d("Initial RepoDataSource");
try {
Response<RepoSearchResponse> response = githubService.searchRepos(query, firstNumberPage).execute();
RepoSearchResponse repoSearchResponse = response.body();
if (repoSearchResponse != null) {
List<Repo> items = repoSearchResponse.getItems();
callback.onResult(items, 1, 2);
}
} catch (IOException e) {
Timber.i(e);
}
}
#Override
public void loadBefore(#NonNull LoadParams<Integer> params, #NonNull LoadCallback<Integer, Repo> callback) {
}
#Override
public void loadAfter(#NonNull LoadParams<Integer> params, #NonNull LoadCallback<Integer, Repo> callback) {
Timber.d("Fetching next page: %s", params.key);
try {
Response<RepoSearchResponse> response = githubService.searchRepos(query, params.key).execute();
if (response.isSuccessful()) {
RepoSearchResponse repoSearchResponse = response.body();
if (repoSearchResponse != null) {
List<Repo> items = repoSearchResponse.getItems();
callback.onResult(items, params.key + 1);
}
}
} catch (IOException e) {
Timber.i(e);
}
}
}
GithubApiCall
#GET("search/repositories")
Call<RepoSearchResponse> searchRepos(#Query("q") String query, #Query("page") Integer page);
RepoDataSourceFactory
public class RepoDataSourceFactory extends DataSource.Factory<Integer, Repo> {
private GithubService githubService;
private String query;
public RepoDataSourceFactory(GithubService githubService, String query) {
this.githubService = githubService;
this.query = query;
}
#NonNull
#Override
public DataSource<Integer, Repo> create() {
return new RepoDataSource(githubService, query);
}
}
Repository method
public class RepoRepository {
...
...
public RepoDataSourceFactory getRepoPagedFactory(String query) {
return new RepoDataSourceFactory(githubService, query);
}
}
ViewModel
public final class MyViewModel {
...
public MutableLiveData<String> searchQuery = new MutableLiveData<>();
...
public LiveData<PagedList<Repo>> getRepos() {
return Transformations.switchMap(searchQuery, query -> {
RepoDataSourceFactory factory = repository.getRepoPagedFactory(query);
return new LivePagedListBuilder<>(factory, pagedListConfig).build();
});
}
...
public SearchView.OnQueryTextListener listener = new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
if (query != null && !query.trim().equals("")) {
searchQuery.postValue(query);
}
return true;
}
#Override
public boolean onQueryTextChange(String newText) {
return true;
}
};
...
}
And in my activity
viewModel.getRepos().observe(this, adapter::submitList);
There is nothing wrong with your code. I was on a GitHub project, and was stuck in the same problem until I realize GitHub has a Rate Limit of 10 requests per minute for unauthenticated requests. But if it is an authenticated one, you can make up to 30 requests per minute.
I assume you also send request for every changes in the search query, just like I did, where typing/changing 5 characters equals 5 requests. So the real cause is the very limited request rate from GitHub, not your code.
Check this out: https://developer.github.com/v3/search/#rate-limit
Retrofit makes a call every X seconds and returns list of objects.
If any object of a list has what i need it should notify user.
The problem that i can not solve is retrofit makes a new call every X seconds and returns new List of objects. But objects or one of object may be same and if it is true no need to notify user again. How to do this?
#Override
public void onResponse(Call<LiveScore> call, Response<LiveScore> response) {
if (!response.isSuccessful()) {
Toast.makeText(MainService.this, response.code(), Toast.LENGTH_SHORT).show();
}
liveScore = response.body();
List<Result>results = response.body().getResult();
for (int i = 0; i < results.size(); i++) {
//here i check all object for a match
int homeScore = Integer.parseInt(results.get(i).getScores().getFirstQuarter().get(0).getScoreHome());
int awayScore = Integer.parseInt(results.get(i).getScores().getFirstQuarter().get(0).getScoreAway());
if (homeScore >= 5 && awayScore >= 5) {
//if true and
// if current position of object previously has
//not notification, notify
}
}
}
A quick and maybe naive approach would be something like this :
private final Set<Result> uniqueResults = new HashSet<>();
#Override
public void onResponse(Call<LiveScore> call, Response<LiveScore> response) {
if (!response.isSuccessful()) {
Toast.makeText(MainService.this, String.valueOf(response.code()), Toast.LENGTH_SHORT).show();
return;
}
liveScore = response.body();
for(Result r : response.body().getResult()) {
if(uniqueResults.add(r)) {
// notify user
}
}
});
You will need to override equals() and hashcode() in your Result class as well.
I use for loop to saving in Realm. Insertion order is very important. With main thread everything is ok but I want to save that data in background threads. So using multiple threads is losing insertion order.
for (int index = 1; index < upperNumber; index++) {
saveDB(index);
}
private void saveDB(final int index) {
try {
realm.executeTransactionAsync(new Realm.Transaction() {
#Override public void execute(Realm bgRealm) {
User user = bgRealm.createObject(User.class, index);
user.setName(String.valueOf(index) + " Name");
user.setAge(index);
}
}, new Realm.Transaction.OnSuccess() {
#Override public void onSuccess() {
Log.d(TAG, "Transaction Success: " + index);
}
});
} catch (Exception e) {
Log.e(TAG, "Error during Transaction: " + e.getMessage());
}
}
There is a Realm documentation about auto-increment id and insertion order but this is also not working with Async Transaction;
https://realm.io/docs/java/latest/#auto-incrementing-ids
How i guarantee insertion order with Async Transaction?
Thanks.
I have a quick question. I'm trying to enable automatic user creation in my app, so that when you open it for the first time, one and only one user is created. I read the User docs thoroughly, and found that Parse persists the user session on disk, and that once you save an object to that user, that user is saved on the cloud. However, I've noticed two problems with this:
1) When I save an object and create a pointer to the Owner User, the user object does not exist on the cloud.
2) If I check whether the current user is linked using ParseAnonUtils.isLinked(), this sometimes returns true, as in the current user is linked, or false, as in the current user is not linked, for the same exact ParseUser that was cached on session.
Here is the method I'm using to create and determine the user:
private void getUser() {
if (!ParseAnonymousUtils.isLinked(ParseUser.getCurrentUser())) {
Log.v("Miles", "new user");
ParseAnonymousUtils.logIn(new LogInCallback() {
#Override
public void done(final ParseUser user, ParseException e) {
if (e == null) {
user.setUsername(UUID.randomUUID().toString());
user.setPassword(UUID.randomUUID().toString());
user.signUpInBackground(new SignUpCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
user.saveInBackground(new SaveCallback() {
#Override
public void done(ParseException e) {
if (e == null) {
Log.v("Miles", "user saved");
} else {
Log.v("Miles", "user not saved " + String.valueOf(e.getCode()));
}
}
});
} else {
Log.v("Miles", "signup fail " + String.valueOf(e.getCode()));
}
}
});
} else {
Log.v("Miles", "anon login fail " + String.valueOf(e.getCode()));
}
}
});
}
}
Help would be greatly appreciated.