Recyclerview notifyDataSetChanged twice stop pagination - android

i have a fragment that contains a recyclerview which needs pagination
so once it reach end of it it will call the API to retrieve a new data and append to the list.
in addition i had just added a swipeRefreshLayout to the view once the user swipe for a refresh it will clear the recyclerview and add the data again
Now the problem is that the adapter is unable to detect end of recyclerview once the swipe to refresh is called
Code Below:
newsSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
generateNews(1);
}
});
adapterNews.setLoadMore(new ToLoadMore() {
#Override
public void LoadMore() {
if (next_offset != 0) {
newsList.add(null);
adapterNews.notifyItemInserted(newsList.size() - 1);
newsApi.GetNews(Constant.ACCESSTOKEN, next_offset).enqueue(new Callback<NewsData>() {
#Override
public void onResponse(Call<NewsData> call, Response<NewsData> response) {
Log.d("PAGINATION", "PAGINATION");
newsList.remove(newsList.size() - 1);
adapterNews.notifyItemRemoved(newsList.size());
newsList.addAll(response.body().getNews());
next_offset = response.body().getNextOffset();
adapterNews.notifyDataSetChanged();
adapterNews.setLoaded();
}
#Override
public void onFailure(Call<NewsData> call, Throwable t) {
}
});
}
}
});
generateNews(1);
return v;
}
the function which generate news is the below
private void generateNews(final int offset) {
newsApi.GetNews(Constant.ACCESSTOKEN, offset).enqueue(new Callback<NewsData>() {
#Override
public void onResponse(Call<NewsData> call, Response<NewsData> response) {
newsList.clear();
newsList.addAll(response.body().getNews());
next_offset = response.body().getNextOffset();
if (newsSwipeRefreshLayout.isRefreshing()) {
newsSwipeRefreshLayout.setRefreshing(false);
}
news_progressbar.setVisibility(View.GONE);
news_progressText.setVisibility(View.GONE);
newsRecyclerView.setVisibility(View.VISIBLE);
adapterNews.notifyDataSetChanged();
}
#Override
public void onFailure(Call<NewsData> call, Throwable t) {
snackbar = Snackbar.make(getView(), "No Internet Connection", Snackbar.LENGTH_INDEFINITE).setAction("Close", new View.OnClickListener() {
#Override
public void onClick(View v) {
snackbar.dismiss();
generateNews(1);
}
});
snackbar.show();
if (newsSwipeRefreshLayout.isRefreshing()) {
newsSwipeRefreshLayout.setRefreshing(false);
}
}
});
}

Related

Andriod Retrofit 2: Refreshing Recycler View From from another Activity

So I have a app where I am displaying the products list from the database using a Recycler View.
There is delete button beside each item which is working fine.
And there is a scan item button which opens a BARCODE SCANNER which on succesfull scanning adds the new product to the database and goes back to the Recycler View display is supposed to refresh the view and display the new Item.
but I am having problem with refreshing the recycler view on adding a new Product.
there is also a delete product function which works perfectly so I tried to do the add item method the same way, but the recycler view doesn't refresh.
UserPage activity
public class UserPage extends AppCompatActivity implements ProductAdaptar.clickedItem {
Toolbar toolbar;
RecyclerView recyclerView;
String rfidNo;
public static String barcode;
Button scanItem;
Button payBill;
TextView total;
ProductAdaptar productAdaptar;
Call<List<UserLoginResp>> productList;
List<UserLoginResp> productListsItems = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_user_page);
toolbar=findViewById(R.id.toolbar);
recyclerView=findViewById(R.id.recyclerview);
scanItem = findViewById(R.id.scanItem);
payBill = findViewById(R.id.payBill);
total = findViewById(R.id.total);
scanItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startActivity(new Intent(getApplicationContext(),ScannerView.class).putExtra("rfid",rfidNo));
}
});
payBill.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
checkout();
}
});
LinearLayoutManager manager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(manager);
recyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL));
productAdaptar = new ProductAdaptar(this::clickedItem, this);
Intent intent =getIntent();
if(intent.getExtras()!=null){
rfidNo= intent.getStringExtra("rfid");
}
getAllProducts(rfidNo);
}
public void getAllProducts(String rfidno){
LinearLayoutManager manager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(manager);
productAdaptar = new ProductAdaptar(this::clickedItem,this);
productList= ApiClient.getUserPageService().getCartItems(rfidno);
productList.enqueue(new Callback<List<UserLoginResp>>() {
#Override
public void onResponse(Call<List<UserLoginResp>> call, Response<List<UserLoginResp>> response) {
if (response.isSuccessful()) {
productListsItems = response.body();
productAdaptar.setData(productListsItems);
RecyclerView recyclerView = findViewById(R.id.recyclerview);
recyclerView.setAdapter(productAdaptar);
getTotal();
}
}
#Override
public void onFailure(Call<List<UserLoginResp>> call, Throwable t) {
Log.e("listfailed",t.getLocalizedMessage());
}
});
}
public void getTotal(){
Call<getBill> bill = ApiClient.getUserPageService().getBill(rfidNo);
bill.enqueue(new Callback<getBill>() {
#Override
public void onResponse(Call<getBill> call, Response<getBill> response) {
if(response.isSuccessful()){
getBill getBill = response.body();
String bill = String.valueOf(getBill.getBill());
total.setText(bill);
}
}
#Override
public void onFailure(Call<getBill> call, Throwable t) {
Log.e("bill error",""+t);
}
});
}
public void checkout(){
Call<String> payment= APIClientString.getUserPageService().checkout(rfidNo);
payment.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
if(response.isSuccessful()){
getAllProducts(rfidNo);
Toast.makeText(UserPage.this, "Payment Successful", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<String> call, Throwable t) {
Log.e("paymentfail",""+t);
}
});
}
#Override
public void clickedItem(UserLoginResp userLoginResp) {
Log.e("clicked prodcut", userLoginResp.toString());
}
}
ScannerView Class
enter #Override
public void handleResult(Result rawResult) {
barcode = rawResult.getText();
if(addItem(barcode,rfidNo)) {
userPage.getAllProducts(rfidNo);
}
onBackPressed();
}
public boolean addItem(String barcode,String rfidNo){
final boolean[] res = {false};
Call<String> resp = APIClientString.getUserPageService().addProduct(barcode,rfidNo);
resp.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
if(response.isSuccessful()){
Toast.makeText(ScannerView.this, response.body().toString(), Toast.LENGTH_SHORT).show();
res[0] =true;
}
}
This is the scanner class which is suppose to call the call the getAllproducts function from the UserPage Activity to refresh the view. It shows no error but the recycler view doesn't get updated.
This is the Adapter Class
public class ProductAdaptar extends RecyclerView.Adapter<ProductAdaptar.ProductAdaptarVH> {
private List<UserLoginResp> productListItems;
private UserPage context;
private clickedItem clickedItem;
public ProductAdaptar(clickedItem clickedItem, UserPage activity) {
this.clickedItem = clickedItem;
this.context= activity;
}
public void setData(List<UserLoginResp> productListItems) {
this.productListItems = productListItems;
notifyDataSetChanged();
}
#NonNull
#Override
public ProductAdaptarVH onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new ProductAdaptar.ProductAdaptarVH(LayoutInflater.
from(context).inflate(R.layout.row_products,parent,false));
}
#Override
public void onBindViewHolder(#NonNull ProductAdaptarVH holder, int position) {
UserLoginResp userLoginResp = productListItems.get(position);
String pName = userLoginResp.getProductName();
String pQuan = userLoginResp.getQuantity();
String pPrice = userLoginResp.getProductPrice();
String pBarcode = userLoginResp.getProductID();
String userID = userLoginResp.getUserID();
holder.pName.setText(pName);
holder.pQuan.setText(pQuan);
holder.pPrice.setText(pPrice);
holder.delProdcut.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
delProduct(userID,pBarcode);
}
});
holder.moreDetails.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
}
public interface clickedItem{
public void clickedItem(UserLoginResp userLoginResp);
}
public void delProduct(String userID, String pBarcode){
Call<String> res = APIClientString.getUserPageService().deleteProduct(pBarcode,userID);
res.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
if(response.isSuccessful()){
Toast.makeText(context, response.body().toString(), Toast.LENGTH_SHORT).show();
context.getAllProducts(userID);
}
}
#Override
public void onFailure(Call<String> call, Throwable t) {
Log.e("deletefailed",""+t);
}
});
}
#Override
public int getItemCount() {
return productListItems.size();
}
public static class ProductAdaptarVH extends RecyclerView.ViewHolder {
TextView pName;
TextView pQuan;
TextView pPrice;
Button delProdcut;
Button moreDetails;
public ProductAdaptarVH(#NonNull View itemView) {
super(itemView);
pName=itemView.findViewById(R.id.pName);
pQuan=itemView.findViewById(R.id.pQuantity);
pPrice=itemView.findViewById(R.id.pPrice);
delProdcut=itemView.findViewById(R.id.delProduct);
moreDetails=itemView.findViewById(R.id.moreDetails);
}
}
}
In this Product Apdapter there is a delete product item function
holder.delProdcut.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
delProduct(userID,pBarcode);
}
});
public void delProduct(String userID, String pBarcode){
Call<String> res = APIClientString.getUserPageService().deleteProduct(pBarcode,userID);
res.enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
if(response.isSuccessful()){
Toast.makeText(context, response.body().toString(), Toast.LENGTH_SHORT).show();
context.getAllProducts(userID);
}
}
#Override
public void onFailure(Call<String> call, Throwable t) {
Log.e("deletefailed",""+t);
}
});
}
Which also calls the getProduts function from UserPage activity and it works perfectly fine but the AddItem function doesn't refresh the view.
The Retrofit APIs are working completly fine too, the problem is only with refreshing the recycler view display on Item Add.
I am new to android coding so I can't seem to understand how to do it.
As you are refreshing your adapter using getAllProducts() method but it is being called in onCreate(). Now whenever you start a ScannerView activity, UserPage activity gets paused and then started (not created) when ScannerView activity finishes. So, you should call getAllProducts() in onStart() method like this:
#Override
protected void onStart() {
super.onStart();
Intent intent = getIntent();
if (intent.getExtras() != null) {
rfidNo = intent.getStringExtra("rfid");
}
getAllProducts(rfidNo);
}

Make n asynchronous calls from list and invoke method when all calls are completed

I make n asynchronous calls (n being the size of the arraylist, and indexes passed as integer parameters to the calls) and want to invoke a method when all of the calls are completed. I implemented the following code below. I used a counter to know that all of the calls are completed. It is working, however I know that it could be done in a more efficient and elegant way.
int n = mUserUrls.getM3u().size();
counter = n;
Observable.range(0, n)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(new Consumer<Integer>() {
#Override
public void accept(Integer integer) throws Exception {
final int index = integer;
Single<ResponseBody> singleGetChannels = aPI.getChannels(mUserUrls.getM3u().get(integer))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
Single<List<EPG>> singleGetEPGs = aPI.getEPGs(mUserUrls.getJson())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
Single.zip(singleGetChannels, singleGetEPGs, new BiFunction<ResponseBody, List<EPG>, ChannelsAndEPG>() {
#Override
public ChannelsAndEPG apply(ResponseBody responseBodyChannels, List<EPG> ePGs) {
ChannelsAndEPG channelsAndEPG = new ChannelsAndEPG(responseBodyChannels, ePGs);
return channelsAndEPG;
}
}).subscribe(new SingleObserver<ChannelsAndEPG>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onSuccess(ChannelsAndEPG channelsAndEPG) {
m3Us.put(index, M3UParser.parseList(channelsAndEPG.mResponseBodyChannels.byteStream()));
setEPGs(index, channelsAndEPG.mEPG);
setEPGsForNext24Hours();
counter--;
if (counter == 0) {
if (mCallback != null) {
isDataLoaded = true;
mCallback.onDataLoaded();
}
}
}
#Override
public void onError(Throwable e) {
}
});
}
})
.subscribe(new Observer<Integer>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Integer integer) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
Log.i(TAG, "onComplete called");
}
});
You can use flatMap to convert each integer to Single ( same way you're doing it now). And then call toList to get Single.
You could use this :
Observable.fromIterable(mUserUrls.getM3u())
.flatMap{ m3u ->
aPI.getChannels(m3u.getInteger)
.zipWith(aPI.getEPGs(mUserUrls.getJson()))
.subscribeOn(Schedulers.io())
}
.doOnNext{
m3Us.put(index, M3UParser.parseList(channelsAndEPG.mResponseBodyChannels.byteStream()));
setEPGs(index, channelsAndEPG.mEPG);
setEPGsForNext24Hours();
}
.subscribe(new Observer<Integer>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Integer integer) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
Log.i(TAG, "onComplete called");
}
})

how to check if Retrofit API call was successful in Android

here is my code:
by this method, I send some data to my API server
if data received successfully success variable will be true inside onResponse method
private boolean success=false;
public boolean commit(){
requestHandler.insertFields(tableName,values).enqueue(new Callback<DatabaseModel>() {
#Override
public void onResponse(Call<DatabaseModel> call,
Response<DatabaseModel> response) {
if (response.isSuccessful())
Log.i("debug8","result is:"+response.body().toString());
else
Log.e("debug8","error in server response:"+response.toString());
success=true;
}
#Override
public void onFailure(Call<DatabaseModel> call, Throwable t) {
Log.e("tempo",t.getMessage());
}
});
return success;//returns false before finishing the request
}
and in my activity i want to check the result like this:
Database db=new Database("cars");
db.put("field1","val1")
.put("field2","val2");
if(db.commit())
Toast.makeText(this, "data inserted successfully", Toast.LENGTH_SHORT).show();
else
Toast.makeText(this, "failed to insert new record", Toast.LENGTH_SHORT).show();
the problem is that always the commit method result is false because code returns false before retrofit enqueue method finishes its job
is it a good idea to make a custom listener or something like that to do this?
thanks in advance
Yes,you need to create listener for this because these calls happen asynchronously that's why it's returning always false.
You can create a interface like below.
public interface RetrofitResponseListener {
void onSuccess();
void onFailure();
}
And pass it as a argument in your method.
public void commit(final RetrofitResponseListener retrofitResponseListener) {
requestHandler.insertFields(tableName, values).enqueue(new Callback<DatabaseModel>() {
#Override
public void onResponse(Call<DatabaseModel> call,
Response<DatabaseModel> response) {
if (response.isSuccessful())
retrofitResponseListener.onSuccess();
else
retrofitResponseListener.onFailure();
}
#Override
public void onFailure(Call<DatabaseModel> call, Throwable t) {
retrofitResponseListener.onFailure();
}
});
}
And finally use this where you like:
private void setListener() {
commit(new RetrofitResponseListener() {
#Override
public void onSuccess() {
}
#Override
public void onFailure() {
}
});
}
Now do anything you want onSuccess or onFailure.
what happens there is the next: the request method is an async task, when you call it, it´ll start working on a second plane and your commit function continues with its sync flow which is return (the false value in this case).
you can try 2 things:
something like this,
public XX(whatever correspond) commit(){
return requestHandler.insertFields(tableName,values).enqueue(new Callback<DatabaseModel>() {
#Override
public void onResponse(Call<DatabaseModel> call,
Response<DatabaseModel> response) {
if (response.isSuccessful())
Log.i("debug8","result is:"+response.body().toString());
else
Log.e("debug8","error in server response:"+response.toString());
return true;
}
#Override
public void onFailure(Call<DatabaseModel> call, Throwable t) {
Log.e("tempo",t.getMessage()); return false;
}
});
}
and fix the if at the other side
or this way
public void commit(){
requestHandler.insertFields(tableName,values).enqueue(new Callback<DatabaseModel>() {
#Override
public void onResponse(Call<DatabaseModel> call,
Response<DatabaseModel> response) {
if (response.isSuccessful())
Log.i("debug8","result is:"+response.body().toString());
else
Log.e("debug8","error in server response:"+response.toString());
Toast.makeText(this, "data inserted successfully", Toast.LENGTH_SHORT).show();
}
#Override
public void onFailure(Call<DatabaseModel> call, Throwable t) {
Toast.makeText(this, "failed to insert new record", Toast.LENGTH_SHORT).show();
}
});}

ProgressDIalog freezing during load data from Retrofit + RxJava

I am using a ProgressDialog to be presented before making a Rest request using Retrofit + RxJava, the response of the request is large and this is freezing the animation of ProgressDialog. How can I fix this? I only found examples saying to use runOnUiThread or the doInBackground with AsyncTask but, I'm using RxJava. I tried the runOnUiThread but it did not work.
//My request
public void getMyData(final MyListener listener) {
AppApi AppApi = getInstanceMyApi();
AppApi.getMyData()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<ResponseData>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
//error
}
#Override
public void onNext(ResponseData response) {
//success, send data to presenter to update view
}
});
//Presenter call ws
public void attemptGetDataFromWS() {
showProgress();
getMyData(this);
}
#Override
public void onGetMyDataSuccess(ResponseData response) {
hideProgress();
}
#Override
public void onGetMyDataError(String error) {
hideProgress();
}
public void getMyData(final MyListener listener) {
AppApi AppApi = getInstanceMyApi();
AppApi.getMyData()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new DisposableObserver<ResponseData>() {
#Override
public void onCompleted() {
dispose();
}
#Override
public void onError(Throwable e) {
listener.onGetMyDataError(e.getMessage());
}
#Override
public void onNext(ResponseData response) {
listener.onGetMyDataSuccess(response);
}
});
//Presenter call ws
public void attemptGetDataFromWS() {
showProgress();
getMyData(this);
}
#Override
public void onGetMyDataSuccess(ResponseData response) {
hideProgress();
}
#Override
public void onGetMyDataError(String error) {
hideProgress();
}
Follow this method .this is calling example with REST API + Retrofit
private void callRestAPI() {
Retrofit retrofit = new Retrofit.Builder().baseUrl(BASEURL)
.addConverterFactory(GsonConverterFactory.create())
.build();
newsAPI = retrofit.create(NewsAPI.class);
Call<JSONResponse> call = newsAPI.topNews("soure", "api-key");
// Set up progress before call
final ProgressDialog progressDoalog;
progressDoalog = new ProgressDialog(MainActivity.this);
progressDoalog.setMax(100);
progressDoalog.setMessage("Its loading....");
progressDoalog.setTitle("ProgressDialog bar example");
progressDoalog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
// show it
progressDoalog.show();
call.enqueue(new Callback<JSONResponse>() {
#Override
public void onResponse(Call<JSONResponse> call, Response<JSONResponse> response) {
// close it after response
progressDoalog.dismiss();
}
}
#Override public void onFailure (Call < JSONResponse > call, Throwable t){
// close it after response
progressDoalog.dismiss();
}
});
}

How should the model feedback to the view in android MVP?

I am using MVP to decouple my view and model in my android application. I need to know how the model should feedback the result of the business logic to the view.
If for example a button is pressed to login, the activity would be like this, using butterknife #OnClick annotation:
#OnClick(R.id.login_button)
public void login() {
String email = mEmailEditText.getText().toString();
String password = mPasswordEditText.getText().toString();
LoginCredentials loginCredentials = new LoginCredentials(email, password);
mPresenter.loginWithEmail(loginCredentials);
}
The presenter would then validate and make a request to the repository:
public void loginWithEmail(LoginCredentials loginCredentials) {
boolean isEmailValid = AuthValidator.validateEmail(loginCredentials.getUsername());
boolean isPasswordValid = AuthValidator.validatePassword(loginCredentials.getPassword());
if(isEmailValid && isPasswordValid) repository.loginEmailUser(loginCredentials);
if (!isEmailValid) view.handleInvalidEmail();
if (!isPasswordValid) view.handleInvalidPassword();
}
The repository would then execute the business logic:
#Override
public void loginEmailUser(LoginCredentials loginCredentials) {
Call<Token> call = userServiceApi.loginInToken(loginCredentials);
call.enqueue(new Callback<Token>() {
#Override
public void onResponse(#NonNull Call<Token> call, #NonNull Response<Token> response) {
if (response.isSuccessful()) {
// handle successful login
} else {
// Handle unsuccessful login
}
}
#Override
public void onFailure(#NonNull Call<Token> call, #NonNull Throwable t) {
// Handle failed request
}
});
Where the comments say // handle unsuccessful something, how does the model feedback to the view the outcomes of the business logic so that the view can handle these outcomes?
Thank you.
You can use a interface as callback, for example :
public interface RepositoryCallback {
void onLoginEmailUserSuccess(/*paramaters if you need*/);
void onLoginEmailUserError(/*paramaters if you need*/);
void onRequestFailed(/*paramaters if you need*/)
}
In the repository defined the listener
public class MyRepository {
private RepositoryCallback mListener;
#Override
public void loginEmailUser(LoginCredentials loginCredentials) {
Call<Token> call = userServiceApi.loginInToken(loginCredentials);
call.enqueue(new Callback<Token>() {
#Override
public void onResponse(#NonNull Call<Token> call, #NonNull Response<Token> response) {
if (response.isSuccessful()) {
// handle successful login
if (mListener != null) {
mListener.onLoginEmailUserSuccess()
}
} else {
// Handle unsuccessful login
if (mListener != null) {
mListener.onLoginEmailUserError()
}
}
}
#Override
public void onFailure(#NonNull Call<Token> call, #NonNull Throwable t) {
// Handle failed request
if (mListener != null) {
mListener.onRequestFailed()
}
}
});
public void setRepositoryCallback(RepositoryCallback listener) {
mListener = listener;
}
}
Then set the presenter as listener :
public class MyPresenter implements RepositoryCallback {
public void loginWithEmail(LoginCredentials loginCredentials) {
repository.setRepositoryCallback(this) // here or in the presenter constructor
boolean isEmailValid = AuthValidator.validateEmail(loginCredentials.getUsername());
boolean isPasswordValid = AuthValidator.validatePassword(loginCredentials.getPassword());
if(isEmailValid && isPasswordValid) repository.loginEmailUser(loginCredentials);
if (!isEmailValid) view.handleInvalidEmail();
if (!isPasswordValid) view.handleInvalidPassword();
}
#Override
public void onLoginEmailUserSuccess(//paramaters if you need){
// your code
}
#Override
public void onLoginEmailUserError(//paramaters if you need){
// your code
}
#Override
public void onRequestFailed(//paramaters if you need){
// your code
}
}
Hope this helps.
Sorry for my english.

Categories

Resources