Room LiveData onChange called too fast. Many Activities stacked atop - java

#Dao
public interface LibraryCoverContentDao {
#Query("SELECT * FROM LibraryCoverContent where rush_id = :rush_id")
LiveData<List<LibraryCoverContent>> getContentsFromRushID(String rush_id);
#Query("DELETE FROM library_cover where rush_id = :rush_id")
void deleteContentsFromRushID(String rush_id);
#Insert(onConflict = REPLACE)
void insertCoverContents(LibraryCoverContent... contents);
}
I want to open another activity once a list LiveData> mLibraryCoverContents is not null.
I am inserting the items downloaded from a retrofit call one by one into the room database, so apparently, my startActivity() call for the next activity happens many a times and multiple-same activities are opened over this activity.
I want only a single activity on top by calling onChanged only after all items of the retrofit call are inserted into db.
Please see the following related code for reference:
public void openReadRushScreen(final int index) {
int count = mCoversList.size();
if(count > index){
mRushIDContent = mLibraryContentRepository.getContentsFromID(mCoversList.get(index).getRush_id());
mRushIDContent.observe(this, new Observer<List<LibraryCoverContent>>() {
#Override
public void onChanged(#Nullable List<LibraryCoverContent> libraryCoverContents) {
Toast.makeText(getActivity(), "ON CHANGED", Toast.LENGTH_SHORT).show();
if(libraryCoverContents!=null && libraryCoverContents.size()>0){
mRushIDContentsList = libraryCoverContents;
if(mRushIDContentsList.size()>0 && mRushIDContentsList.get(0).getRush_id().equals(mCoversList.get(index).getRush_id())){
mRushIDContentsList = new ArrayList<>();
startActivity(ReadRushActivity.getStartIntent(getActivity(), mCoversList.get(index).getRush_id(),
mCoversList.get(index).isRush_audio(),
mCoversList.get(index).getTitle()));
}
}
else {
if(mCoversList!=null && mCoversList.size()>index) getContent(mCoversList.get(index).getRush_id());
}
}
});
}
else Toast.makeText(getActivity(), "Empty Cover", Toast.LENGTH_SHORT).show();
}
public void getContent(String mRushId) {
mApiService = ApiClient.getClient().create(ApiInterface.class);
Call<List<Content>> call = mApiService.getRushContent(mRushId);
if(call!=null){
call.enqueue(new Callback<List<Content>>() {
#Override
public void onResponse(#NonNull Call<List<Content>> call, #NonNull Response<List<Content>> response) {
mContents = response.body();
if(mContents!=null && mContents.size()>0){
//noinspection ConstantConditions
List<LibraryCoverContent> coverContent = new ArrayList<>();
for(int i=0; i<mContents.size(); i++){
coverContent.add(new LibraryCoverContent
(mContents.get(i).getContent_id(), mContents.get(i).getRush_id(),
mContents.get(i).getContent(), mContents.get(i).getAttr(),
mContents.get(i).getDatetime(), mContents.get(i).getPage_no()));
}
mLibraryContentRepository.insertContentItems(coverContent);
}
}
#Override
public void onFailure(#NonNull Call<List<Content>> call, #NonNull Throwable t) {
// if(getActivity()!=null) Toast.makeText(getActivity(), "Network Error while downloading rush content", Toast.LENGTH_LONG).show();
}
});
}
}
#SuppressLint("StaticFieldLeak")
public void insertContentItems(final List<LibraryCoverContent> items) {
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... voids) {
for(int i=0; i<items.size(); i++){
mLibraryCoverContentDao.insertCoverContents(items.get(i));
}
return null;
}
}.execute();
}

Related

Firebase method don't allow a return

I have this method looseMoney(...) with a firebase database. Unfortunately the methods onSuccess() and onFailure()
don't allow it to return any value. I want to check if the transaction is succesfull or not? But how could I do that? You can see my code below. What am I missing? I am grateful for every answer. Thank you!
private int looseMoney(String pUserID, final int pAmount) {
final FirebaseFirestore db = FirebaseFirestore.getInstance();
final DocumentReference sfDocRefAbs = db.collection("users").document(pUserID);
db.runTransaction(new Transaction.Function<Void>() {
#Override
public Void apply(Transaction transaction) throws FirebaseFirestoreException {
DocumentSnapshot snapshotAbs = transaction.get(sfDocRefAbs);
int neuerKontostandAbs = 0;
if(pAmount <= (snapshotAbs.getDouble("kontostand"))) {
neuerKontostandAbs = (int) (snapshotAbs.getDouble("kontostand") - pAmount);
transaction.update(sfDocRefAbs, "kontostand", neuerKontostandAbs);
}
else {
//return 1;
}
return null;
}
}).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Toast.makeText(getApplicationContext(), "Kontostand erfolgreich angepasst", Toast.LENGTH_SHORT).show();
//return 2;
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(getApplicationContext(), "Transaktion fehlgeschlagen: " + e.toString(), Toast.LENGTH_SHORT).show();
//return 3;
}
});
}
We have faced this case and we figured out a simple solution.
First of all you should know that firebase retrieval functions are asynchronous functions. i.e. you will need a call back to be triggered when firebase does it's job.
We have created a simple interface called RetrievalEventListener which provide functions you can call inside the onSuccess event for example.
public interface class RetrievalEventListener<T> {
public abstract void OnDataRetrieved(T t);
}
This interface can be passed as a parameter and you call the onDataRetrieved Function when you want to retrieve the value.
private void looseMoney(String pUserID, final int pAmount, RetrievalEventListener<int> retrievalEventListener) {
final FirebaseFirestore db = FirebaseFirestore.getInstance();
final DocumentReference sfDocRefAbs = db.collection("users").document(pUserID);
db.runTransaction(new Transaction.Function<Void>() {
#Override
public Void apply(Transaction transaction) throws FirebaseFirestoreException {
DocumentSnapshot snapshotAbs = transaction.get(sfDocRefAbs);
int neuerKontostandAbs = 0;
if(pAmount <= (snapshotAbs.getDouble("kontostand"))) {
neuerKontostandAbs = (int) (snapshotAbs.getDouble("kontostand") - pAmount);
transaction.update(sfDocRefAbs, "kontostand", neuerKontostandAbs);
}
else {
//return 1;
retrievalEventListener.onDataRetrieved(1);
}
return null;
}
}).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Toast.makeText(getApplicationContext(), "Kontostand erfolgreich angepasst", Toast.LENGTH_SHORT).show();
retrievalEventListener.onDataRetrieved(2);
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(getApplicationContext(), "Transaktion fehlgeschlagen: " + e.toString(), Toast.LENGTH_SHORT).show();
//return 3;
retrievalEventListener.onDataRetrieved(3);
}
});
}
How should you call the function?
It should like this:
String pUserID = "someId";
int pAmount = 10;
looseMoney(pUserID, pAmount, new RetrievalEventListener<int>() {
#Override
public void OnDataRetrieved(int number) {
// Now you have the required number do what do you need with it
}
});
If you want more clarifications let me know :)

SwipeRefresh layout progress bar is not hiding even after completion

I am using MVVM pattern in which I am using SwipeRefresh layout to refresh recycler view in my layout.When I am pulling it then it continue to refresh even after method completed successfully.
Below is my code:
MainActivity.java
refresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
userRepository.getUserList();
}
});
UserRepository.java
public void getUserList(){
Retrofit retrofit = RetrofitClient.getInstance();
ApiService apiService = retrofit.create(ApiService.class);
Call<List<User>> userList = apiService.getUser();
userList.enqueue(new Callback<List<User>>() {
#Override
public void onResponse(Call<List<User>> call, final Response<List<User>> response) {
Completable.fromAction(new Action() {
#Override
public void run() throws Exception {
if(response.body() != null) {
List<User> list = response.body();
for (int i = 0; i < list.size(); i++) {
String id = list.get(i).get_id();
String names = list.get(i).getName();
String age = list.get(i).getAge();
User user = new User(id,names,age);
userDb.userDao().Insert(user);
}
}
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CompletableObserver() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onComplete() {
Toast.makeText(context,"Data inserted",Toast.LENGTH_SHORT).show();
}
#Override
public void onError(Throwable e) {
Toast.makeText(context,e.getMessage(),Toast.LENGTH_LONG).show();
}
});
}
#Override
public void onFailure(Call<List<User>> call, Throwable t) {
Toast.makeText(context,t.getMessage(),Toast.LENGTH_LONG).show();
}
});
}
Method fetching list is in another class UserRepository and I am calling method in another activity MainActivity.I am not getting any way how can I stop refreshing process.Someone please let me know a way to stop refreshing process.
Any help would be appreciated.
THANKS
To disable the progress dialog add this,
swipeLayout.setRefreshing(false);

Boolean Return from AsyncTask in Android

I want to return a Boolean after a AsyncTask.
This is the AsyncTask (not the whole code because isn't important and sstackoverflow give me error):
public class CallSoap extends AsyncTask<CallSoapParams, Void, Void> {
public interface AsyncResponse {
void processFinish(String output);
}
private Context activityContext;
public AsyncResponse delegate = null;//Call back interface
public CallSoap(Context context, AsyncResponse asyncResponse) {
activityContext = context;
delegate = asyncResponse;
}
#Override
protected Void doInBackground(CallSoapParams... params) {
request = new SoapObject(params[0].NAMESPACE, params[0].METHOD_NAME);
// no important things
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
//dismiss ProgressDialog
delegate.processFinish(response.toString());
}
#Override
protected void onPreExecute() {
super.onPreExecute();
//create and show ProgressDialog
}
}
And this is the implementation on Activity (not the whole code because isn't important and sstackoverflow give me error):
private boolean checkDataRegistrationByServer() {
if (NickNameExist()) {
// DO STUFF
}
return true;
}
Boolean r;
private boolean NickNameExist() {
CallSoapParams callParams = new CallSoapParams(NICKNAME_EXIST);
CallSoap NickNameExistCall = new CallSoap(RegistrationActivity.this, new CallSoap.AsyncResponse() {
#Override
public void processFinish(String output) {
Log.d("Response From AsyTask:", output);
if (output.equals(FALSE_RESPONSE)) {
r = false;
Toast.makeText(getApplicationContext(), output + " - NickNameExistCall - Nick don't exist", Toast.LENGTH_SHORT).show();
} else {
r = true;
}
}
});
NickNameExistCall.execute(callParams);
return r;
}
I tried to create a global Boolean but the App crash. Someone can help me?
1) You don't have a response variable anywhere, and doInBackground has returned null instead of any response, so not clear how you got that value.
delegate.processFinish(response.toString());
2) You can't return from that function. And your app crashes probably because Boolean's can be null. boolean's cannot. However, you should not attempt to make a global variable here because that's not how asynchronous code should run.
What you need is to pass the callback through the function
private void checkDataRegistrationByServer(String data, CallSoap.AsyncResponse callback) {
CallSoap nickNameExistCall = new CallSoap(RegistrationActivity.this, callback);
CallSoapParams callParams = new CallSoapParams(data);
nickNameExistCall.execute(callParams);
}
Elsewhere...
final String nick = NICKNAME_EXIST;
checkDataRegistrationByServer(nick, new CallSoap.AsyncResponse() {
#Override
public void processFinish(String response) {
Log.d("Response From AsyncTask:", output);
boolean exists = !response.equals(FALSE_RESPONSE);
if (!exists) {
Toast.makeText(getApplicationContext(), output + " - NickNameExistCall - Nick " + nick + " doesn't exist", Toast.LENGTH_SHORT).show();
}
}
});
Note: If you make your AsyncTask just return a Boolean in the AsyncResponse you can shorten this code some.

How do I return a value from onResponse

Basically this it the code structure, I would like to know how i can modify my codes so that I can get the value inside onResponse and returning it. As of now, my mainReply variable return "(blank)" but im expecting it to pass the data in the arraylist called details inside my onResponse segment. Rest assure, there are values returned as I have checked, but i just cant get the value to be passed out of the onResponse segment.
I have checked for alternatives and they mentioned to use interface. However, I do not know how to modify my codes to use the solution that mentioned interface and use of callBacks.
public class MainActivity extends AppCompatActivity {
EditText et_message;
FloatingActionButton fab_send;
API api;
ListView list_view_conversation;
List<ChatModel> list_chat = new ArrayList<>();
RevealDetailsCallbacks callback;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_message = (EditText) findViewById(R.id.et_message);
fab_send = (FloatingActionButton) findViewById(R.id.fab_send);
list_view_conversation = (ListView) findViewById(R.id.list_view_conversation);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
api = retrofit.create(API.class);
fab_send.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//this method ultimately is to get response and send back to user
String s = et_message.getText().toString();
ChatModel model = new ChatModel(s, true);
list_chat.add(model);
new retrieveDetails().execute(list_chat);
et_message.setText("'");
}
});
}
public class retrieveDetails extends AsyncTask<List<ChatModel>, Void, String> {
String text = et_message.getText().toString();
String mainReply = "";
List<ChatModel> models;
List<String> details = new ArrayList<String>();
#Override
public String doInBackground(List<ChatModel>[] lists) {
Call<List<Patient>> call = api.getPatients();
models = lists[0];
call.enqueue(new Callback<List<Patient>>() {
public String reply;
#Override
public void onResponse(Call<List<Patient>> call, Response<List<Patient>> response) {
List<Patient> patients = response.body();
for (int i = 0; i < patients.size(); i++) {
if (patients.get(i).getNric().equals(text)) {
details.add("Name: " + patients.get(i).getName() + "\nNRIC: " + patients.get(i).getNric()
+ "\nDOB: " + patients.get(i).getDob() + "\nContact No: " + patients.get(i).getContactno());
}
}
this.mainReply = details.get(0);
Log.i("Here Log i", reply);
}
#Override
public void onFailure(Call<List<Patient>> call, Throwable t) {
Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
return mainReply;//I want to reply with the data added into the details arraylist in the onResponse segment
}
#Override
public void onPostExecute(String s) {
ChatModel chatModel = new ChatModel(s, false);
models.add(chatModel);
CustomAdapter adapter = new CustomAdapter(models, getApplicationContext());
list_view_conversation.setAdapter(adapter);
}
}
}
If you wanted to modify your existing code, you would add an interface like the one I added up top (RevealDetailsCallbacks), pass it into the asynctask constructor, and run it. The code would look like this:
public class MainActivity extends AppCompatActivity {
//Interface callback here
interface RevealDetailsCallbacks {
public void getDataFromResult(List<String> details);
}
EditText et_message;
FloatingActionButton fab_send;
API api;
ListView list_view_conversation;
List<ChatModel> list_chat = new ArrayList<>();
RevealDetailsCallbacks callback;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_message = (EditText) findViewById(R.id.et_message);
fab_send = (FloatingActionButton) findViewById(R.id.fab_send);
list_view_conversation = (ListView) findViewById(R.id.list_view_conversation);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
this.callback = new RevealDetailsCallbacks() {
#Override
public void getDataFromResult(List<String> details) {
//Do stuff here with the returned list of Strings
}
};
api = retrofit.create(API.class);
fab_send.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//this method ultimately is to get response and send back to user
String s = et_message.getText().toString();
ChatModel model = new ChatModel(s, true);
list_chat.add(model);
new retrieveDetails(callback).execute(list_chat);
et_message.setText("'");
}
});
}
public class retrieveDetails extends AsyncTask<List<ChatModel>, Void, String> {
String text = et_message.getText().toString();
String mainReply = "";
List<ChatModel> models;
List<String> details = new ArrayList<String>();
private RevealDetailsCallbacks listener;
retrieveDetails(RevealDetailsCallbacks listener){
this.listener = listener;
}
#Override
public String doInBackground(final List<ChatModel>[] lists) {
Call<List<Patient>> call = api.getPatients();
models = lists[0];
call.enqueue(new Callback<List<Patient>>() {
public String reply;
#Override
public void onResponse(Call<List<Patient>> call, Response<List<Patient>> response) {
List<Patient> patients = response.body();
for (int i = 0; i < patients.size(); i++) {
if (patients.get(i).getNric().equals(text)) {
details.add("Name: " + patients.get(i).getName() + "\nNRIC: " + patients.get(i).getNric()
+ "\nDOB: " + patients.get(i).getDob() + "\nContact No: " + patients.get(i).getContactno());
}
}
this.mainReply = details.get(0);
Log.i("Here Log i", reply);
if(listener != null) {
listener.getDataFromResult(details);
}
}
#Override
public void onFailure(Call<List<Patient>> call, Throwable t) {
//Don't make a toast here, it will throw an exception due to it being in doInBackground
//Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
return mainReply;//I want to reply with the data added into the details arraylist in the onResponse segment
}
#Override
public void onPostExecute(String s) {
ChatModel chatModel = new ChatModel(s, false);
models.add(chatModel);
CustomAdapter adapter = new CustomAdapter(models, getApplicationContext());
list_view_conversation.setAdapter(adapter);
}
}
}
However, there is no need for asynctask here since you are running Retrofit and calling .enqueue, which runs on a background thread. A simpler version would look like this:
public class MainActivity extends AppCompatActivity {
//Interface callback here
interface RevealDetailsCallbacks {
public void getDataFromResult(List<String> details);
}
//Keep your same variables here
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Same setup here
this.callback = new RevealDetailsCallbacks() {
#Override
public void getDataFromResult(List<String> details) {
//Do stuff here with the returned list of Strings
}
};
fab_send.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//Same setup here, then call the method
makeWebCalls();
}
});
}
private void makeWebCalls(){
Call<List<Patient>> call = api.getPatients();
models = lists[0];
call.enqueue(new Callback<List<Patient>>() {
#Override
public void onResponse(Call<List<Patient>> call, Response<List<Patient>> response) {
//Run your response code here. When done, pass to the callback
}
#Override
public void onFailure(Call<List<Patient>> call, Throwable t) {
Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
}
You can just enqueue the Retrofit call immediately in the OnClick and handle its response there
fab_send.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
final String text = et_message.getText().toString();
// if you're trying to filter data, add a parameter to getPatients()
api.getPatients().enqueue(new Callback<List<Patient>>() {
#Override
public void onResponse(Call<List<Patient>> call, Response<List<Patient>> response) {
// Here you have a full list of patients
final List<Patient> patients = response.body();
// adapter = new PatientAdapter(MainActivity.this, patients);
// mListView.setAdapter(adapter);
}

Retrieve the parseUser through a pointer with Java

I am trying to display an user who posted something to the app, I have been able to retrieve the Content of the post but not the user. However the User is being saved because in my IOS version I can Clearly see the Posts I have made to test the App out. I need to display the user however the user Pointer<_user>does not save as a string.
How would I get the username behind the pointer?
IOS Version:
var findUser:PFQuery = PFUser.query()
findUser.whereKey("objectId", equalTo: posts.objectForKey("user").objectId)
Posts Class:
#ParseClassName("Posts")
public class Posts extends ParseObject {
public ParseUser getUser() {
return getParseUser("user");
}
public void setUser(ParseUser value) {
put("user", value);
}
public String getContent(){
return getString("content");
}
public void setContent(String content){
put("content", content);
}
#Override
public String toString(){
return getString("user") + "\n" + getString("content");
}
}
List Activity:
List<Posts> posts = new ArrayList<Posts>();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.parse_list);
ParseQuery<Posts> query = new ParseQuery<Posts>("Posts");
query.orderByDescending("createdAt");
query.findInBackground(new FindCallback<Posts>() {
#Override
public void done(List<Posts> list, ParseException e) {
if (e != null) {
Toast.makeText(ParseListActivity.this, "Error " + e, Toast.LENGTH_SHORT).show();
}
for (Posts post : list) {
Posts newPost = new Posts();
newPost.setUser(post.getUser());
newPost.setContent(post.getContent());
posts.add(newPost);
}
ArrayAdapter<Posts> adapter = new ArrayAdapter<Posts>(ParseListActivity.this, android.R.layout.simple_list_item_1, posts);
setListAdapter(adapter);
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.my, menu);
return true;
}
/*
* Creating posts and refreshing the list will be controlled from the Action
* Bar.
*/
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_refresh: {
updatePostList();
break;
}
case R.id.action_new: {
newPost();
break;
}
}
return super.onOptionsItemSelected(item);
}
private void newPost() {
Intent i = new Intent(this, newPost.class);
startActivityForResult(i, 0);
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
// If a new post has been added, update
// the list of posts
updatePostList();
}
}
private void updatePostList() {
ParseQuery<Posts> query = new ParseQuery<Posts>("Posts");
query.orderByDescending("createdAt");
query.findInBackground(new FindCallback<Posts>() {
#Override
public void done(List<Posts> list, ParseException e) {
if (e != null) {
Toast.makeText(ParseListActivity.this, "Error " + e, Toast.LENGTH_SHORT).show();
}
for (Posts post : list) {
Posts newPost = new Posts();
newPost.setContent(post.getContent());
posts.add(newPost);
}
ArrayAdapter<Posts> adapter = new ArrayAdapter<Posts>(ParseListActivity.this, android.R.layout.simple_list_item_1, posts);
setListAdapter(adapter);
}
});
}
}

Categories