I'm currently trying to learn how to use MediatorLiveData as described here:
https://developer.android.com/reference/androidx/lifecycle/MediatorLiveData
What I want to do is wait for two livedata object to get an update then do some logic on both.
So in my activity i currently got this. While this works I'm currently only getting Orders while i would like to wait for Orders AND Orderrows to finish and then make some change.
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private Button myButt;
private MainViewModel mvw;
private TextView myView;
private MediatorLiveData data;
#Override
protected void onCreate(Bundle savedInstanceState) {
mvw = new ViewModelProvider(this).get(MainViewModel.class);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myButt = findViewById(R.id.button);
myView = findViewById(R.id.textview);
myButt.setOnClickListener(this);
data = new MediatorLiveData<>();
data.addSource(mvw.getAllOrders(), new Observer<Orders>() {
#Override
public void onChanged(Orders orders) {
data.setValue(orders);
}
});
data.addSource(mvw.getAllOrderRows(), new Observer<OrderRows>() {
#Override
public void onChanged(OrderRows orderRows) {
data.setValue(orderRows);
}
});
data.observe(this, new Observer<Orders>() { //this observers Orders but how do i get orders AND orderrows?
#Override
public void onChanged(Orders order) {
myView.setText(mvw.extractDate(order));
//Here i want to manipulate order and orderrows
Log.i("livedata" , order.getOrders().toString());
}
});
}
#Override
public void onClick(View view) {
switch(view.getId()){
case R.id.button:
Log.i("button","clicked");
mvw.updateOrderData(); // calls for new values to be fetched
mvw.updateOrderRowData();
}
}
}
in my viewmodel:
public class MainViewModel extends ViewModel {
private GetOrder getOrderRepo;
private MutableLiveData<Orders> allOrders;
private MutableLiveData<OrderRows> allOrderRows;
public MainViewModel(){
getOrderRepo = new GetOrder();
}
public MutableLiveData<Orders> getAllOrders() {
if(allOrders == null){
allOrders = new MutableLiveData<>();
allOrders = getOrderRepo.getAllOrders();
}
return allOrders;
}
public MutableLiveData<OrderRows> getAllOrderRows() {
if(allOrderRows == null){
allOrderRows = new MutableLiveData<>();
allOrderRows = getOrderRepo.getAllOrderRows();
}
return allOrderRows;
}
public void updateOrderData(){
Log.i("updating","updating data");
Orders orders = getOrderRepo.getAllOrders().getValue();
allOrders.setValue(orders);
}
public void updateOrderRowData(){
Log.i("updating","updating data");
OrderRows orderRows = getOrderRepo.getAllOrderRows().getValue();
allOrderRows.setValue(orderRows);
}
public String extractDate(Orders orders){
ArrayList<Order> listOfOrders = orders.getOrders();
Log.i("extractDate", ""+(listOfOrders.size()-1));
String date = listOfOrders.get(listOfOrders.size()-1).getOrderTime();
return date;
}
}
In the repostiory.
public class GetOrder {
private ApiService mAPIService;
MutableLiveData<Orders> allOrders;
MutableLiveData<OrderRows> allOrderRows;
public GetOrder(){
mAPIService = ApiUtils.getAPIService();
allOrders = new MutableLiveData<Orders>();
allOrderRows = new MutableLiveData<OrderRows>();
}
public MutableLiveData<Orders> getAllOrders(){
Log.i("func","starting func");
mAPIService.getOrders().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Orders>() {
#Override
public void onCompleted() {
Log.i("func","onComplete");
}
#Override
public void onError(Throwable e) {
Log.i("onError",e.toString());
}
#Override
public void onNext(Orders orders) {
Log.i("Repo",orders.toString());
allOrders.setValue(orders);
}
});
return allOrders;
}
public MutableLiveData<OrderRows> getAllOrderRows(){
Log.i("func","starting func");
mAPIService.getOrderRows().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<OrderRows>() {
#Override
public void onCompleted() {
Log.i("func","onComplete");
}
#Override
public void onError(Throwable e) {
Log.i("onError",e.toString());
}
#Override
public void onNext(OrderRows orderRows) {
Log.i("Repo",orderRows.toString());
allOrderRows.setValue(orderRows);
}
});
return allOrderRows;
}
}
Related
how are you, I hope everyone is fine
I have created an application to generate a sales invoice.
In SalesInvoiceActivity, when the user clicks on the FloatingActionButton, it goes to AddSaleInvoiceActivity.
addSaleInvoiceFab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
startActivity(new Intent(SalesInvoiceActivity.this, AddSaleInvoiceActivity.class));
}
});
In AddSaleInvoiceActivity there is an EditText and Date to store the name of the customer and invoice date in RecyclerView list in SalesInvoiceActivity.
customerName = addCustomerNameEt.getText().toString();
date = new Date();
When the user presses the Save Invoice button, the invoice number is saved with the ID, the invoice date, and the customer's name.
final SaleInvoiceEntry saleInvoiceEntry = new SaleInvoiceEntry(customerName, date);
appDatabase.salesInvoiceDao().insertSaleInvoice(saleInvoiceEntry);
#Entity(tableName = "saleInvoice")
public class SaleInvoiceEntry {
#PrimaryKey(autoGenerate = true)
private int id;
#ColumnInfo(name = "customerName")
private String customerName;
#ColumnInfo(name = "invoiceDate")
private Date invoiceDate;
#Ignore
public SaleInvoiceEntry(String customerName, Date invoiceDate) {
this.customerName = customerName;
this.invoiceDate = invoiceDate;
}
public SaleInvoiceEntry(int id) {
this.id = id;
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setCustomerName(String customerName) {
this.customerName = customerName;
}
public String getCustomerName() {
return customerName;
}
public void setInvoiceDate(Date invoiceDate) {
this.invoiceDate = invoiceDate;
}
public Date getInvoiceDate() {
return invoiceDate;
}
}
#Dao
public interface SalesInvoiceDao {
#Query("SELECT * FROM saleInvoice ORDER BY id")
LiveData<List<SaleInvoiceEntry>> loadAllSalesInvoice();
#Query("SELECT * FROM saleInvoice WHERE id = :saleInvoiceId")
LiveData<SaleInvoiceEntry> loadSalesInvoiceById(int saleInvoiceId);
#Insert
void insertSaleInvoice(SaleInvoiceEntry saleInvoiceEntry);
#Update(onConflict = OnConflictStrategy.REPLACE)
void updateSaleInvoice(SaleInvoiceEntry saleInvoiceEntry);
#Delete
void deleteSaleInvoice(SaleInvoiceEntry saleInvoiceEntry);
}
public class SalesInvoiceAdapter extends RecyclerView.Adapter<SalesInvoiceAdapter.SalesInvoiceViewHolder>
implements AddProductToSaleInvoiceAdapter.ItemClickListener {
private static final String DATE_FORMAT = "dd/MM/yyy";
private final SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT, Locale.getDefault());
private final ItemClickListener itemClickListener;
private List<SaleInvoiceEntry> saleInvoiceEntries;
private final Context context;
private AppDatabase appDatabase;
private String saleInvoiceNumber, customerName, saleInvoiceDate;
public SalesInvoiceAdapter(Context context, ItemClickListener listener, AppDatabase appDatabase) {
this.context = context;
itemClickListener = listener;
this.appDatabase = appDatabase;
}
#NonNull
#Override
public SalesInvoiceViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.sales_list, parent, false);
return new SalesInvoiceViewHolder(view);
}
#SuppressLint({"SetTextI18n", "NotifyDataSetChanged"})
#Override
public void onBindViewHolder(SalesInvoiceViewHolder salesInvoiceViewHolder, int saleInvoicePosition) {
SaleInvoiceEntry saleInvoiceEntry = saleInvoiceEntries.get(saleInvoicePosition);
saleInvoiceNumber = String.valueOf(saleInvoiceEntry.getId());
salesInvoiceViewHolder.saleInvoiceNumberView.setText(context.getString(R.string.sale_invoice_number) + " : " + saleInvoiceNumber);
customerName = saleInvoiceEntry.getCustomerName();
salesInvoiceViewHolder.customerNameView.setText(context.getString(R.string.customer_name) + " : " + customerName);
saleInvoiceDate = dateFormat.format(saleInvoiceEntry.getInvoiceDate());
salesInvoiceViewHolder.saleInvoiceDateView.setText(context.getString(R.string.invoice_date) + " : " + saleInvoiceDate);
appDatabase = AppDatabase.getInstance(context);
salesInvoiceViewHolder.deleteSaleInvoice.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(context, R.style.CutShapeTheme);
builder.setTitle(context.getString(R.string.delete_invoice))
.setMessage(context.getString(R.string.delete_invoice_message))
.setPositiveButton(context.getString(R.string.yes), (dialogInterface, i) -> {
try {
AppExecutors.getInstance().diskIO().execute(new Runnable() {
#Override
public void run() {
int saleInvoicePosition = salesInvoiceViewHolder.getAdapterPosition();
appDatabase.salesInvoiceDao().deleteSaleInvoice(saleInvoiceEntries.get(saleInvoicePosition));
}
});
Toast.makeText(context, context.getString(R.string.invoice_deleted), Toast.LENGTH_SHORT).show();
} catch (Exception e) {
e.printStackTrace();
}
})
.setCancelable(false)
.setNegativeButton(context.getString(R.string.no), (dialog, which) -> {
})
.create().show();
}
});
salesInvoiceViewHolder.editSaleInvoice.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent saleInvoiceIntent = new Intent(context, AddSaleInvoiceActivity.class);
int saleInvoiceId = saleInvoiceEntries.get(salesInvoiceViewHolder.getAdapterPosition()).getId();
saleInvoiceIntent.putExtra(EXTRA_SALE_INVOICE_ID, saleInvoiceId);
context.startActivity(saleInvoiceIntent);
}
});
}
#Override
public int getItemCount() {
if (saleInvoiceEntries == null) {
return 0;
}
return saleInvoiceEntries.size();
}
public List<SaleInvoiceEntry> getSaleInvoiceEntries() {
return saleInvoiceEntries;
}
#SuppressLint("NotifyDataSetChanged")
public void setSaleInvoiceEntries(List<SaleInvoiceEntry> saleInvoiceEntries) {
this.saleInvoiceEntries = saleInvoiceEntries;
notifyDataSetChanged();
}
#Override
public void onItemClickListener(int productId, String productName) {
}
public interface ItemClickListener {
void onItemClickListener(int saleInvoiceId, String customerName);
}
class SalesInvoiceViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private final TextView saleInvoiceNumberView, customerNameView, saleInvoiceDateView;
private final ImageView deleteSaleInvoice, editSaleInvoice;
public SalesInvoiceViewHolder(View itemView) {
super(itemView);
saleInvoiceNumberView = itemView.findViewById(R.id.listInvoiceNumber);
customerNameView = itemView.findViewById(R.id.listCustomerName);
saleInvoiceDateView = itemView.findViewById(R.id.listInvoiceDate);
deleteSaleInvoice = itemView.findViewById(R.id.listDeleteSaleInvoice);
editSaleInvoice = itemView.findViewById(R.id.listEditSaleInvoice);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
int saleInvoiceId = saleInvoiceEntries.get(getAdapterPosition()).getId();
String customerName = saleInvoiceEntries.get(getAdapterPosition()).getCustomerName();
itemClickListener.onItemClickListener(saleInvoiceId, customerName);
}
}
}
public class SalesInvoiceActivity extends AppCompatActivity implements SalesInvoiceAdapter.ItemClickListener {
private FloatingActionButton addSaleInvoiceFab;
private RecyclerView salesInvoiceRv;
private SalesInvoiceAdapter salesInvoiceAdapter;
private AppDatabase appDatabase;
private View salesInvoiceEmptyView;
private SearchView salesInvoiceSv;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sales_invoice);
initSalesInvoiceView();
setupSalesInvoiceViewModel();
setupSalesInvoiceRecycleView();
addSaleInvoiceFab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
startActivity(new Intent(SalesInvoiceActivity.this, AddSaleInvoiceActivity.class));
}
});
salesInvoiceSv.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
#Override
public boolean onQueryTextSubmit(String query) {
return false;
}
#Override
public boolean onQueryTextChange(String newText) {
setupSalesInvoiceViewModel();
salesInvoiceFilter(newText);
return true;
}
});
}
private void salesInvoiceFilter(String text) {
ArrayList<SaleInvoiceEntry> salesInvoiceList = new ArrayList<>();
for (SaleInvoiceEntry saleInvoice : salesInvoiceAdapter.getSaleInvoiceEntries()) {
if (saleInvoice.getCustomerName().toLowerCase().contains(text.toLowerCase())) {
salesInvoiceList.add(saleInvoice);
}
}
if (salesInvoiceList.isEmpty()) {
Toast.makeText(this, getString(R.string.no_invoice), Toast.LENGTH_SHORT).show();
} else {
salesInvoiceAdapter.setSaleInvoiceEntries(salesInvoiceList);
}
}
private void initSalesInvoiceView() {
salesInvoiceEmptyView = findViewById(R.id.salesInvoiceEmptyView);
addSaleInvoiceFab = findViewById(R.id.addSaleInvoiceFab);
salesInvoiceRv = findViewById(R.id.salesInvoiceRv);
salesInvoiceSv = findViewById(R.id.saleSearchView);
}
private void setupSalesInvoiceRecycleView() {
appDatabase = AppDatabase.getInstance(getApplicationContext());
salesInvoiceAdapter = new SalesInvoiceAdapter(this, this, appDatabase);
salesInvoiceRv.setHasFixedSize(true);
salesInvoiceRv.setLayoutManager(new LinearLayoutManager(this));
salesInvoiceRv.setAdapter(salesInvoiceAdapter);
}
private void setupSalesInvoiceViewModel() {
MainViewModel mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
mainViewModel.getListSalesInvoiceLiveData().observe(this, new Observer<List<SaleInvoiceEntry>>() {
#SuppressLint("NotifyDataSetChanged")
#Override
public void onChanged(#Nullable List<SaleInvoiceEntry> saleInvoiceEntries) {
salesInvoiceAdapter.setSaleInvoiceEntries(saleInvoiceEntries);
salesInvoiceAdapter.notifyDataSetChanged();
if (salesInvoiceAdapter.getItemCount() == 0) {
salesInvoiceRv.setVisibility(View.GONE);
salesInvoiceEmptyView.setVisibility(View.VISIBLE);
salesInvoiceSv.setVisibility(View.GONE);
} else {
salesInvoiceRv.setVisibility(View.VISIBLE);
salesInvoiceEmptyView.setVisibility(View.GONE);
salesInvoiceSv.setVisibility(View.VISIBLE);
}
}
});
}
#Override
public void onItemClickListener(int saleInvoiceId, String customerName) {
Intent intent = new Intent(SalesInvoiceActivity.this, SaleDetailsActivity.class);
intent.putExtra("saleInvoiceId", saleInvoiceId);
intent.putExtra("productName", customerName);
startActivity(intent);
}
}
In AddSaleInvoiceActivity, I created a button that when the user clicks on it, it will be directed to the product store to capture the name of the product, selling price and store it in the RecyclerView list inside AddSaleInvoiceActivity
private Button addProductToSaleInvoiceBtn;
private void initAddProductToSaleInvoiceViews() {
addProductToSaleInvoiceBtn = findViewById(R.id.addProductToSaleInvoiceBtn);
addProductToSaleInvoiceBtn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent productToSaleInvoiceIntent = new Intent(AddSaleInvoiceActivity.this, ProductStoreActivity.class);
productToSaleInvoiceIntent.putExtra(EXTRA_PRODUCT_TO_SALE_INVOICE_ID, 1);
startActivity(productToSaleInvoiceIntent);
}
});
productToSaleInvoiceRv = findViewById(R.id.productToSaleInvoiceRv);
addProductToSaleInvoiceAdapter = new AddProductToSaleInvoiceAdapter(this, this, appDatabase);
productToSaleInvoiceRv.setHasFixedSize(true);
productToSaleInvoiceRv.setLayoutManager(new LinearLayoutManager(this));
productToSaleInvoiceRv.setAdapter(addProductToSaleInvoiceAdapter);
MainViewModel mainViewModel = ViewModelProviders.of(this).get(MainViewModel.class);
mainViewModel.getListToSalesInvoiceLiveData().observe(this, new Observer<List<AddProductToSalesInvoiceEntry>>() {
#SuppressLint("NotifyDataSetChanged")
#Override
public void onChanged(#Nullable List<AddProductToSalesInvoiceEntry> addProductToSalesInvoiceEntries) {
addProductToSaleInvoiceAdapter.setProductToSalesInvoiceEntries(addProductToSalesInvoiceEntries);
addProductToSaleInvoiceAdapter.notifyDataSetChanged();
}
});
}
#Entity(tableName = "addProductToSalesInvoice")
public class AddProductToSalesInvoiceEntry {
#PrimaryKey(autoGenerate = true)
private int addProductToSalesInvoiceId;
private final String toSalesInvoiceProductName;
private final int toSalesInvoiceProductQuantity;
private final float toSalesInvoiceProductSellingPrice;
#Ignore
public AddProductToSalesInvoiceEntry(String toSalesInvoiceProductName, int toSalesInvoiceProductQuantity
, float toSalesInvoiceProductSellingPrice) {
this.toSalesInvoiceProductName = toSalesInvoiceProductName;
this.toSalesInvoiceProductQuantity = toSalesInvoiceProductQuantity;
this.toSalesInvoiceProductSellingPrice = toSalesInvoiceProductSellingPrice;
}
public AddProductToSalesInvoiceEntry(int addProductToSalesInvoiceId, String toSalesInvoiceProductName, int toSalesInvoiceProductQuantity
, float toSalesInvoiceProductSellingPrice) {
this.addProductToSalesInvoiceId = addProductToSalesInvoiceId;
this.toSalesInvoiceProductName = toSalesInvoiceProductName;
this.toSalesInvoiceProductQuantity = toSalesInvoiceProductQuantity;
this.toSalesInvoiceProductSellingPrice = toSalesInvoiceProductSellingPrice;
}
public int getAddProductToSalesInvoiceId() {
return addProductToSalesInvoiceId;
}
public void setAddProductToSalesInvoiceId(int addProductToSalesInvoiceId) {
this.addProductToSalesInvoiceId = addProductToSalesInvoiceId;
}
public String getToSalesInvoiceProductName() {
return toSalesInvoiceProductName;
}
public int getToSalesInvoiceProductQuantity() {
return toSalesInvoiceProductQuantity;
}
public float getToSalesInvoiceProductSellingPrice() {
return toSalesInvoiceProductSellingPrice;
}
}
#Dao
public interface AddProductToSalesInvoiceDao {
#Query("SELECT * FROM addProductToSalesInvoice ORDER BY addProductToSalesInvoiceId")
LiveData<List<AddProductToSalesInvoiceEntry>> addProductToSalesInvoiceLoadAllProducts();
#Query("SELECT * FROM addProductToSalesInvoice WHERE addProductToSalesInvoiceId = :addProductToSalesInvoiceProductId")
LiveData<AddProductToSalesInvoiceEntry> addProductToSalesInvoiceLoadProductsById(int addProductToSalesInvoiceProductId);
#Insert
void addProductToSalesInvoiceInsertProduct(AddProductToSalesInvoiceEntry addProductToSalesInvoiceEntry);
#Update(onConflict = OnConflictStrategy.REPLACE)
void addProductToSalesInvoiceUpdateProduct(AddProductToSalesInvoiceEntry addProductToSalesInvoiceEntry);
#Delete
void addProductToSalesInvoiceDeleteProduct(AddProductToSalesInvoiceEntry addProductToSalesInvoiceEntry);
#Delete
void addProductToSalesInvoiceDeleteAllProduct(List<AddProductToSalesInvoiceEntry> toSalesInvoiceEntries);
}
public class AddProductToSaleInvoiceAdapter extends RecyclerView
.Adapter<AddProductToSaleInvoiceAdapter.AddProductToSalesInvoiceViewHolder> {
private final ItemClickListener addProductToSalesInvoiceItemClickListener;
private List<AddProductToSalesInvoiceEntry> addProductToSalesInvoiceEntries;
private final Context context;
private AppDatabase appDatabase;
private String addProductToSalesInvoiceProductName;
private int addProductToSalesInvoiceProductQuantity;
private float addProductToSalesInvoiceProductSellingPrice;
private List<AddProductToSalesInvoiceEntry> addProductToSalesInvoiceEntryList;
public AddProductToSaleInvoiceAdapter(Context context, ItemClickListener listener, AppDatabase appDatabase) {
this.context = context;
addProductToSalesInvoiceItemClickListener = listener;
this.appDatabase = appDatabase;
}
#NonNull
#Override
public AddProductToSalesInvoiceViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.add_products_to_sale_invoice_list, parent, false);
return new AddProductToSalesInvoiceViewHolder(view);
}
#SuppressLint("SetTextI18n")
#Override
public void onBindViewHolder(AddProductToSalesInvoiceViewHolder addProductToSalesInvoiceViewHolder
, int productToSalesInvoicePosition) {
AddProductToSalesInvoiceEntry addProductToSalesInvoiceEntry
= addProductToSalesInvoiceEntries.get(productToSalesInvoicePosition);
addProductToSalesInvoiceProductName = addProductToSalesInvoiceEntry.getToSalesInvoiceProductName();
addProductToSalesInvoiceViewHolder.listAddProductToSalesInvoiceProductNameView.setText(addProductToSalesInvoiceProductName);
addProductToSalesInvoiceProductQuantity = addProductToSalesInvoiceEntry.getToSalesInvoiceProductQuantity();
addProductToSalesInvoiceViewHolder.listAddProductToSalesInvoiceProductQuantityView.setText("" + addProductToSalesInvoiceProductQuantity);
addProductToSalesInvoiceProductSellingPrice = addProductToSalesInvoiceEntry.getToSalesInvoiceProductSellingPrice();
addProductToSalesInvoiceViewHolder.listAddProductToSalesInvoiceProductSellingPriceView.setText("" + addProductToSalesInvoiceProductSellingPrice);
addProductToSalesInvoiceViewHolder.productSellingPriceView.setText(context.getString(R.string.product_selling_price) + " : " + addProductToSalesInvoiceProductSellingPrice);
#Override
public int getItemCount() {
if (addProductToSalesInvoiceEntries == null) {
return 0;
}
return addProductToSalesInvoiceEntries.size();
}
public List<AddProductToSalesInvoiceEntry> getAddProductToSalesInvoiceEntries
(ArrayList<AddProductToSalesInvoiceEntry> uProductToSalesInvoiceList) {
return addProductToSalesInvoiceEntries;
}
#SuppressLint("NotifyDataSetChanged")
public void setProductToSalesInvoiceEntries(List<AddProductToSalesInvoiceEntry> addProductToSalesInvoiceEntries) {
this.addProductToSalesInvoiceEntries = addProductToSalesInvoiceEntries;
notifyDataSetChanged();
}
public interface ItemClickListener {
void onItemClickListener(int productId, String productName);
}
class AddProductToSalesInvoiceViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private final TextView listAddProductToSalesInvoiceProductNameView, listAddProductToSalesInvoiceProductQuantityView
, listAddProductToSalesInvoiceProductSellingPriceView;
public AddProductToSalesInvoiceViewHolder(View itemView) {
super(itemView);
listAddProductToSalesInvoiceProductNameView = itemView
.findViewById(R.id.listAddProductToSalesInvoiceProductNameView);
listAddProductToSalesInvoiceProductQuantityView = itemView
.findViewById(R.id.listAddProductToSalesInvoiceProductQuantityView);
listAddProductToSalesInvoiceProductSellingPriceView = itemView
.findViewById(R.id.listAddProductToSalesInvoiceProductSellingPriceView);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
int productId = addProductToSalesInvoiceEntries.get(getAdapterPosition()).getAddProductToSalesInvoiceId();
String productName = addProductToSalesInvoiceEntries.get(getAdapterPosition()).getToSalesInvoiceProductName();
addProductToSalesInvoiceItemClickListener.onItemClickListener(productId, productName);
}
}
}
Now how do I store the AddProductToSalesInvoiceEntry list inside the SaleInvoiceEntry list when the user clicks the save invoice button ?
I am trying to update the UI depending on whether the data is being loaded or has loaded but it is not working properly. I am using enum class for different states.
Initially the error was
Attempt to invoke virtual method 'void androidx.lifecycle.LiveData.observe(androidx.lifecycle.LifecycleOwner, androidx.lifecycle.Observer)' on a null object reference
Then I passed an empty new MutableLiveData()<>. Now, it doesn't crashes the application, however, the getDataStatus() observer isn't working correctly. Kindly look at my implementations and see if they are right.
DataSource
public class ArticlesDataSource extends PageKeyedDataSource<Integer, NewsItem> {
private static final int FIRST_PAGE = 1;
private static final String TAG = "ArticlesDataSource";
public static final String SORT_ORDER = "publishedAt";
public static final String LANGUAGE = "en";
public static final String API_KEY = Utils.API_KEY;
public static final int PAGE_SIZE = 10;
private String mKeyword;
private MutableLiveData<DataStatus> dataStatusMutableLiveData = new MutableLiveData<>();
public ArticlesDataSource(String keyword) {
mKeyword = keyword;
dataStatusMutableLiveData = new MutableLiveData<>();
}
public MutableLiveData<DataStatus> getDataStatusMutableLiveData() {
return dataStatusMutableLiveData;
}
#Override
public void loadInitial(#NonNull LoadInitialParams<Integer> params, #NonNull LoadInitialCallback<Integer, NewsItem> callback) {
dataStatusMutableLiveData.postValue(DataStatus.LOADING);
NewsAPI newsAPI = ServiceGenerator.createService(NewsAPI.class);
Call<RootJsonData> call = newsAPI.searchArticlesByKeyWord(mKeyword, SORT_ORDER, LANGUAGE, API_KEY, FIRST_PAGE, PAGE_SIZE);
call.enqueue(new Callback<RootJsonData>() {
#Override
public void onResponse(Call<RootJsonData> call, Response<RootJsonData> response) {
if (response.body() != null) {
callback.onResult(response.body().getNewsItems(), null, FIRST_PAGE + 1);
dataStatusMutableLiveData.postValue(DataStatus.LOADED);
}
}
#Override
public void onFailure(Call<RootJsonData> call, Throwable t) {
Log.d(TAG, "onFailure: " + t.getMessage());
dataStatusMutableLiveData.postValue(DataStatus.ERROR);
}
});
}
#Override
public void loadBefore(#NonNull LoadParams<Integer> params, #NonNull LoadCallback<Integer, NewsItem> callback) {
NewsAPI newsAPI = ServiceGenerator.createService(NewsAPI.class);
Call<RootJsonData> call = newsAPI.searchArticlesByKeyWord(mKeyword, SORT_ORDER, LANGUAGE, API_KEY, FIRST_PAGE, PAGE_SIZE);
call.enqueue(new Callback<RootJsonData>() {
#Override
public void onResponse(Call<RootJsonData> call, Response<RootJsonData> response) {
// if the current page is greater than one
// we are decrementing the page number
// else there is no previous page
Integer adjacentKey = (params.key > 1) ? params.key - 1 : null;
if (response.body() != null) {
// passing the loaded data
// and the previous page key
callback.onResult(response.body().getNewsItems(), adjacentKey);
}
}
#Override
public void onFailure(Call<RootJsonData> call, Throwable t) {
Log.d(TAG, "onFailure: " + t.getMessage());
}
});
}
#Override
public void loadAfter(#NonNull LoadParams<Integer> params, #NonNull LoadCallback<Integer, NewsItem> callback) {
NewsAPI newsAPI = ServiceGenerator.createService(NewsAPI.class);
Call<RootJsonData> call = newsAPI.searchArticlesByKeyWord(mKeyword, SORT_ORDER, LANGUAGE, API_KEY, params.key, PAGE_SIZE);
call.enqueue(new Callback<RootJsonData>() {
#Override
public void onResponse(Call<RootJsonData> call, Response<RootJsonData> response) {
dataStatusMutableLiveData.postValue(DataStatus.LOADED);
if (response.code() == 429) {
// no more results
List<NewsItem> emptyList = new ArrayList<>();
callback.onResult(emptyList, null);
}
if (response.body() != null) {
// if the response has next page
// incrementing the next page number
Integer key = params.key + 1;
// passing the loaded data and next page value
if (!response.body().getNewsItems().isEmpty()) {
callback.onResult(response.body().getNewsItems(), key);
}
}
}
#Override
public void onFailure(Call<RootJsonData> call, Throwable t) {
Log.d(TAG, "onFailure: " + t.getMessage());
dataStatusMutableLiveData.postValue(DataStatus.ERROR);
}
});
}
}
DataSourceFactory
public class ArticlesDataSourceFactory extends DataSource.Factory {
private final MutableLiveData<ArticlesDataSource> itemLiveDataSource;
private String mQuery;
private final LiveData<DataStatus> dataStatusLiveData = Transformations.switchMap(itemLiveDataSource, (itemDataSource) -> {
return itemDataSource.getDataStatusMutableLiveData();
});
public ArticlesDataSourceFactory() {
mQuery = "news";
itemLiveDataSource = new MutableLiveData<>();
}
#Override
public DataSource<Integer, NewsItem> create() {
ArticlesDataSource itemDataSource = new ArticlesDataSource(mQuery);
itemLiveDataSource.postValue(itemDataSource);
// dataStatusMutableLiveData = itemDataSource.getDataStatusMutableLiveData();
return itemDataSource;
}
public MutableLiveData<ArticlesDataSource> getArticlesLiveDataSource() {
return itemLiveDataSource;
}
public void setQuery(String query) {
mQuery = query;
}
public MutableLiveData<DataStatus> getDataStatusMutableLiveData() {
return dataStatusMutableLiveData;
}
public void setDataStatusMutableLiveData(DataStatus dataStatus){
dataStatusMutableLiveData.postValue(dataStatus);
}
public LiveData<DataStatus> getDataStatusLiveData() {
return dataStatusLiveData;
}
}
ViewModel
public class ArticlesViewModel extends ViewModel {
public LiveData<PagedList<NewsItem>> itemPagedList;
private MutableLiveData<ArticlesDataSource> liveDataSource;
private ArticlesDataSourceFactory articlesDataSourceFactory;
private LiveData dataStatus = new MutableLiveData<>();
public ArticlesViewModel() {
articlesDataSourceFactory = new ArticlesDataSourceFactory();
liveDataSource = articlesDataSourceFactory.getArticlesLiveDataSource();
dataStatus = articlesDataSourceFactory.getDataStatusMutableLiveData();
PagedList.Config pagedListConfig =
(new PagedList.Config.Builder())
.setEnablePlaceholders(false)
.setPageSize(10).build();
itemPagedList = (new LivePagedListBuilder(articlesDataSourceFactory, pagedListConfig)).build();
}
public void setKeyword(String query) {
if (query.equals("") || query.length() == 0)
articlesDataSourceFactory.setDataStatusMutableLiveData(DataStatus.EMPTY);
else {
articlesDataSourceFactory.setQuery(query);
refreshData();
}
}
void refreshData() {
if (itemPagedList.getValue() != null) {
itemPagedList.getValue().getDataSource().invalidate();
}
}
public LiveData<DataStatus> getDataStatus() {
return dataStatus;
}
}
Fragment
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View rootView = inflater.inflate(R.layout.fragment_articles, container, false);
mContext = getActivity();
progressBar = rootView.findViewById(R.id.progress_circular);
emptyStateTextView = rootView.findViewById(R.id.empty_view);
swipeRefreshLayout = rootView.findViewById(R.id.swipe_refresh);
textViewTitle = rootView.findViewById(R.id.text_view_top_headlines);
recyclerView = rootView.findViewById(R.id.recycler_view);
if (savedInstanceState != null) {
keyword = savedInstanceState.getString("keyword");
}
initEmptyRecyclerView();
articlesViewModel = ViewModelProviders.of(this).get(ArticlesViewModel.class);
articlesViewModel.itemPagedList.observe(getViewLifecycleOwner(), new Observer<PagedList<NewsItem>>() {
#Override
public void onChanged(PagedList<NewsItem> newsItems) {
adapter.submitList(newsItems);
// TODO: Handle UI changes
// handleUIChanges(newsItems);
}
});
articlesViewModel.getDataStatus().observe(getViewLifecycleOwner(), new Observer<DataStatus>() {
#Override
public void onChanged(DataStatus dataStatus) {
switch (dataStatus) {
case LOADED:
progressBar.setVisibility(View.GONE);
emptyStateTextView.setVisibility(View.INVISIBLE);
swipeRefreshLayout.setRefreshing(false);
textViewTitle.setVisibility(View.VISIBLE);
break;
case LOADING:
progressBar.setVisibility(View.VISIBLE);
swipeRefreshLayout.setRefreshing(true);
textViewTitle.setVisibility(View.INVISIBLE);
emptyStateTextView.setVisibility(View.INVISIBLE);
break;
case EMPTY:
progressBar.setVisibility(View.GONE);
swipeRefreshLayout.setRefreshing(false);
textViewTitle.setVisibility(View.INVISIBLE);
emptyStateTextView.setVisibility(View.VISIBLE);
emptyStateTextView.setText(R.string.no_news_found);
break;
case ERROR:
progressBar.setVisibility(View.GONE);
swipeRefreshLayout.setRefreshing(false);
textViewTitle.setVisibility(View.INVISIBLE);
emptyStateTextView.setVisibility(View.VISIBLE);
emptyStateTextView.setText(R.string.no_internet_connection);
break;
}
}
});
swipeRefreshLayout.setOnRefreshListener(() -> {
articlesViewModel.setKeyword(keyword);
});
setHasOptionsMenu(true);
return rootView;
}
DataStatus
public enum DataStatus {
ERROR,
LOADING,
LOADED,
EMPTY
}
When you call invalidate(), a new datasource will be created by the factory. However, you are directly exposing the data status liveData of the "current" created datasource, without taking into consideration that more will be created in the future.
The solution is to store the current data source in the factory in a MutableLiveData, and expose the "most recent current data status" using switchMap.
public class ArticlesDataSourceFactory extends DataSource.Factory {
private final MutableLiveData<ArticlesDataSource> itemLiveDataSource = new MutableLiveData<>();
private String mQuery = "news";
private final LiveData<DataStatus> dataStatusLiveData = Transformations.switchMap(itemLiveDataSource, (itemDataSource) -> {
return itemDataSource.getDataStatusMutableLiveData();
});
public ArticlesDataSourceFactory() {
}
#Override
public DataSource<Integer, NewsItem> create() {
ArticlesDataSource itemDataSource = new ArticlesDataSource(mQuery);
itemLiveDataSource.postValue(itemDataSource);
...
public LiveData<DataStatus> getDataStatusLiveData() {
return dataStatusLiveData;
}
I am using Sync Adapter along with Dagger 2 for dependency injection. I am stuck since I cannot seem to figure out where should I use XYZ.inject since SyncAdapter class does not provide OnCreate or an Activity to stick to. Can someone suggest how to deal with Dependency injection in case of Sync Adapter alike classes which do not belong to activity/fragment?
PS: I have looked at several similar questions but failed to find a solution to my problem.
SyncAdapter.java
public class SyncAdapter extends AbstractThreadedSyncAdapter {
ContentResolver mContentResolver;
//Injects here
#Inject
SyncCenterPresenter mSyncCenterPresenter;
private final AccountManager mAccountManager;
Context context;
public SyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
mContentResolver = context.getContentResolver();
mAccountManager = AccountManager.get(context);
this.context=context;
}
Account mainAccount;
public static final int SYNC_INTERVAL = 60 * 1;
public static final int SYNC_FLEXTIME = SYNC_INTERVAL/3;
#Override
public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
Log.v("Sync class me","sync adapter on perform sync");
if (mSyncCenterPresenter == null){
Log.v("messsage","null");
} else {
Log.v("messsage","not null");
mSyncCenterPresenter.loadDatabaseCenterPayload();
mSyncCenterPresenter.syncPayload();
}
}
/**
* Helper method to schedule the sync adapter periodic execution
*/
public static void configurePeriodicSync(Context context, int syncInterval, int flexTime) {
Account account = myAccount;
String authority = "com.mifos.provider";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
// we can enable inexact timers in our periodic sync
SyncRequest request = new SyncRequest.Builder().
syncPeriodic(syncInterval, flexTime).
setSyncAdapter(account, authority).
setExtras(new Bundle()).build();
ContentResolver.requestSync(request);
} else {
ContentResolver.addPeriodicSync(account,
authority, new Bundle(), syncInterval);
}
}
static Account myAccount;
public static void onAccountCreated(Account newAccount, Context context) {
/*
* Since we've created an account
*/
myAccount = newAccount;
SyncAdapter.configurePeriodicSync(context, SYNC_INTERVAL, SYNC_FLEXTIME);
/*
* Without calling setSyncAutomatically, our periodic sync will not be enabled.
*/
ContentResolver.setSyncAutomatically(newAccount, "com.mifos.provider", true);
/*
* Finally, let's do a sync to get things started
*/
syncImmediately(context);
}
public static Account getSyncAccount(Context context) {
// Create the account type and default account
Account newAccount = new Account(
context.getString(R.string.app_name), "com.mifos");
return newAccount;
}
/**
* Helper method to have the sync adapter sync immediately
* #param context The context used to access the account service
*/
public static void syncImmediately(Context context) {
Bundle bundle = new Bundle();
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
ContentResolver.requestSync(getSyncAccount(context),
"com.mifos.provider", bundle);
}
}
SyncCenterPresenter.java
public class SyncCenterPresenter {
private final DataManagerCenter mDataManagerCenter;
private CompositeSubscription mSubscriptions;
List<CenterPayload> centerPayloads;
int mCenterSyncIndex = 0;
#Inject
public SyncCenterPresenter(DataManagerCenter dataManagerCenter) {
Log.v("messsage","const me");
mDataManagerCenter = dataManagerCenter;
mSubscriptions = new CompositeSubscription();
centerPayloads = new ArrayList<>();
}
public void loadDatabaseCenterPayload() {
Log.v("messsage","load me");
mSubscriptions.add(mDataManagerCenter.getAllDatabaseCenterPayload()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<List<CenterPayload>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<CenterPayload> centerPayloads) {
showCenters(centerPayloads);
}
}));
}
public void syncCenterPayload(CenterPayload centerPayload) {
mSubscriptions.add(mDataManagerCenter.createCenter(centerPayload)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Observer<SaveResponse>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(SaveResponse center) {
showCenterSyncResponse();
}
}));
}
public void deleteAndUpdateCenterPayload(int id) {
mSubscriptions.add(mDataManagerCenter.deleteAndUpdateCenterPayloads(id)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Observer<List<CenterPayload>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<CenterPayload> centerPayloads) {
showPayloadDeletedAndUpdatePayloads(centerPayloads);
}
}));
}
public void showCenters(List<CenterPayload> centerPayload) {
centerPayloads = centerPayload;
}
public void showCenterSyncResponse() {
deleteAndUpdateCenterPayload(centerPayloads
.get(mCenterSyncIndex).getId());
}
public void showPayloadDeletedAndUpdatePayloads(List<CenterPayload> centers) {
mCenterSyncIndex = 0;
this.centerPayloads = centers;
}
public void syncPayload() {
for (int i = 0; i < centerPayloads.size(); ++i) {
if (centerPayloads.get(i).getErrorMessage() == null) {
syncCenterPayload(centerPayloads.get(i));
mCenterSyncIndex = i;
break;
} else {
Log.v("messsage","else block");
}
}
}
}
ActivityComponent
#PerActivity
#Component(dependencies = ApplicationComponent.class, modules =
ActivityModule.class)
public interface ActivityComponent {
void inject(LoginActivity loginActivity);
void inject(PassCodeActivity passCodeActivity);
//other methods
void inject(SyncAdapter syncAdapter);
}
ActivityModule
#Module
public class ActivityModule {
private Activity mActivity;
public ActivityModule(Activity activity) {
mActivity = activity;
}
#Provides
Activity provideActivity() {
return mActivity;
}
#Provides
#ActivityContext
Context providesContext() {
return mActivity;
}
}
EDIT
SyncService.java
public class SyncService extends Service {
private static final Object sSyncAdapterLock = new Object();
private static SyncAdapter sSyncAdapter = null;
#Override
public void onCreate() {
synchronized (sSyncAdapterLock) {
if (sSyncAdapter == null) {
sSyncAdapter = new SyncAdapter(getApplicationContext(), true);
}
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return sSyncAdapter.getSyncAdapterBinder();
}
}
Can someone please help me to get this working? since I can't figure out where to use the inject and how to do it without an Activity Component? A different and a better approach would be appreciated as well.
Thanks
You can implement the constructor, so please use constructor injection to initialize your adapter.
public class SyncAdapter extends AbstractThreadedSyncAdapter {
// ...
#Inject
public SyncAdapter(Context context, boolean autoInitialize) { /*...*/ }
}
Then you simply inject the service that returns the SyncAdapter like you would anything else...
public class SyncService extends Service {
#Inject SyncAdapter syncAdapter;
#Override
public void onCreate() {
AndroidInjection.inject(this);
// or
DaggerSyncServiceComponent.create().inject(this);
}
#Override
public IBinder onBind(Intent intent) {
return syncAdapter;
}
}
And that's it.
Trying to convert JSON array to list of objects using Gson library.
Code:
TypeToken<List<Comment>> token = new TypeToken<List<Comment>>() {}; //this line throws the following exception
public class Comment implements Serializable{
#SerializedName("id")
private int mID;
#SerializedName("content")
private String mContent;
#SerializedName("author")
private String mAuthor;
#SerializedName("author_id")
private int mAuthorID;
#SerializedName("author_email")
private String mAuthorEmail;
#SerializedName("date")
private String mDate;
#SerializedName("name")
private String mName;
#SerializedName("image")
private String mImage;
public int getmID() {
return mID;
}
public void setmID(int mID) {
this.mID = mID;
}
public String getmContent() {
return mContent;
}
public void setmContent(String mContent) {
this.mContent = mContent;
}
public String getmAuthor() {
return mAuthor;
}
public void setmAuthor(String mAuthor) {
this.mAuthor = mAuthor;
}
public int getmAuthorID() {
return mAuthorID;
}
public void setmAuthorID(int mAuthorID) {
this.mAuthorID = mAuthorID;
}
public String getmAuthorEmail() {
return mAuthorEmail;
}
public void setmAuthorEmail(String mAuthorEmail) {
this.mAuthorEmail = mAuthorEmail;
}
public String getmDate() {
return mDate;
}
public void setmDate(String mDate) {
this.mDate = mDate;
}
public String getmName() {
return mName;
}
public void setmName(String mName) {
this.mName = mName;
}
public String getmImage() {
return mImage;
}
public void setmImage(String mImage) {
this.mImage = mImage;
}
}
public class CommentListingActivity extends BaseActivity implements View.OnClickListener {
private static final String TAG = "CommentListingActivity";
private ListView mListViewComments;
private CommentAdapter mCommentAdapter;
private ArrayList<Comment> mCommentList = new ArrayList<Comment>();
private ProgressBar mProgressBar;
private TextView mTextViewErrorMessage;
private Button mButtonRefresh;
private LinearLayout mLinearLayoutError;
private int mPostID;
private boolean isCommentListLoading = true;
private EditText mEditTextComment;
private ImageView mImageViewSend;
private InputMethodManager mInputMethodManager;
private SwipeRefreshLayout mSwipeRefreshLayout;
private boolean mIsRefreshing = false;
private int mOffset = 0;
private View mProgressBarView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_comment_listing);
mPostID = getIntent().getIntExtra("postID",0);
setToolbar();
setToolBarTitle(getString(R.string.commentsLabel));
setToolbarHomeAsUpEnabled(true);
mSwipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
#Override
public void onRefresh() {
mSwipeRefreshLayout.setRefreshing(false);
mIsRefreshing = true;
mOffset = 0;
fetchComments();
}
});
mListViewComments = (ListView) findViewById(R.id.listViewComments);
mCommentAdapter = new CommentAdapter(this, mCommentList,mImageLoader);
mListViewComments.setAdapter(mCommentAdapter);
mProgressBarView = getLayoutInflater().inflate(R.layout.recyclerview_loading_item,null);
mListViewComments.addFooterView(mProgressBarView);
// set the custom dialog components - text, image and button
mEditTextComment = (EditText) findViewById(R.id.editTextComment);
mImageViewSend = (ImageView) findViewById(R.id.imageViewSend);
mImageViewSend.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
postComment();
}
});
mEditTextComment.requestFocus();
mInputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
mInputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
initProgressBar();
initErrorView();
mListViewComments.setOnScrollListener(new EndlessScrollListener() {
#Override
public boolean onLoadMore(int page, int totalItemsCount) {
mOffset = mOffset + LIST_LIMIT;
mListViewComments.addFooterView(mProgressBarView);
fetchComments();
return true;
}
});
fetchComments();
}
private void fetchComments(){
if (!mIsRefreshing && mCommentAdapter.getCount()==0)
showProgressBar();
HashMap<String,String> parameters = new HashMap<String, String>();
parameters.put("post",String.valueOf(mPostID));
parameters.put("offset",String.valueOf(mOffset));
NetworkUtility.getJSONRquest(this, APIURLs.LIST_COMMENTS, parameters, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
//Remove loading item
if(mCommentList.size()>0) {
mListViewComments.removeFooterView(mProgressBarView);
}
try {
if(response.getString(API_KEY_STATUS).equalsIgnoreCase(API_RESPONSE_SUCCESS)){
JSONArray jsonArrayComments = response.getJSONArray(API_KEY_DATA);
if(jsonArrayComments.length()>0) {
if (mIsRefreshing) {
mCommentList.clear();
mCommentAdapter.notifyDataSetChanged();
}
TypeToken<List<Comment>> token = new TypeToken<List<Comment>>() {};
mCommentList.addAll((Collection<? extends Comment>) GsonUtility.convertJSONStringToObject(jsonArrayComments.toString(), token));
mCommentAdapter.notifyDataSetChanged();
}
mIsRefreshing = false;
if(mCommentAdapter.getCount()>0) {
showContent();
} else {
if (mOffset == 0)
showError("No comments found.");
}
} else {
mProgressBarView.setVisibility(View.GONE);
if(mCommentAdapter.getCount()>0){
AlertDialogUtility.showErrorMessage(CommentListingActivity.this,getString(R.string.errorLabel),response.getString(API_KEY_MESSAGE),getString(R.string.okLabel),null,null,null);
} else
showError(response.getString(API_KEY_MESSAGE));
}
} catch (JSONException e) {
e.printStackTrace();
mProgressBarView.setVisibility(View.GONE);
if(mCommentAdapter.getCount()>0){
AlertDialogUtility.showErrorMessage(CommentListingActivity.this,getString(R.string.errorLabel),getString(R.string.volleyErrorMessage),getString(R.string.okLabel),null,null,null);
} else {
showError(getString(R.string.volleyErrorMessage));
}
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
mProgressBarView.setVisibility(View.GONE);
if(mCommentAdapter.getCount()>0){
AlertDialogUtility.showErrorMessage(CommentListingActivity.this,getString(R.string.errorLabel),getString(R.string.volleyErrorMessage),getString(R.string.okLabel),null,null,null);
} else {
showError(error.getCause().getMessage());
}
error.printStackTrace();
}
},null,TAG);
}
private void postComment() {
final AppProgressDialog appProgressDialog = new AppProgressDialog(this);
appProgressDialog.setProgressDialogTitle("Posting Comment");
appProgressDialog.setProgressDialogMessage("Please Wait...");
appProgressDialog.showProgressDialog();
try {
final String comment = mEditTextComment.getText().toString();
if (!ValidatorUtility.isBlank(comment) && mPostID!=0) {
JSONObject jsonData = new JSONObject();
jsonData.put("content",comment);
jsonData.put("post",mPostID);
NetworkUtility.postRquestWithBasicAuth(this, APIURLs.POST_COMMENT, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
try {
if(response.has("id") && response.getInt("id")>0){
AppToast.toastLong(mContext,"Comment Added");
Comment objComment = new Comment();
if (response.has("author_name"))
objComment.setmAuthor(response.getString("author_name"));
if (response.has("author_email"))
objComment.setmAuthorEmail(response.getString("author_email"));
if (response.has("author"))
objComment.setmAuthorID(response.getInt("author"));
objComment.setmContent(comment);
if (response.has("date"))
objComment.setmDate(response.getString("date"));
if (response.has("id"))
objComment.setmID(response.getInt("id"));
objComment.setmImage(mUser.getImage());
objComment.setmName(mUser.getName());
mCommentList.add(0,objComment);
mCommentAdapter.notifyDataSetChanged();
mEditTextComment.setText(null);
mEditTextComment.clearFocus();
mInputMethodManager.hideSoftInputFromWindow(mEditTextComment.getWindowToken(),0);
showContent();
} else {
AlertDialogUtility.showErrorMessage(CommentListingActivity.this,getString(R.string.errorLabel),"Failed to add comment. Please try again later.",getString(R.string.okLabel),null,null,null);
}
} catch (JSONException e) {
e.printStackTrace();
} finally {
appProgressDialog.dismissProgressDialog();
}
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
appProgressDialog.dismissProgressDialog();
AlertDialogUtility.showErrorMessage(CommentListingActivity.this,getString(R.string.errorLabel),error.getCause().getMessage(),getString(R.string.okLabel),null,null,null);
}
}, jsonData, TAG);
} else {
appProgressDialog.dismissProgressDialog();
}
} catch (Exception e){
e.printStackTrace();
appProgressDialog.dismissProgressDialog();
}
}
private void initProgressBar(){
mProgressBar = (ProgressBar) findViewById(R.id.progressBar);
}
private void initErrorView(){
mLinearLayoutError = (LinearLayout) findViewById(R.id.linearLayoutError);
mTextViewErrorMessage = (TextView) findViewById(R.id.textViewErrorMessage);
mButtonRefresh = (Button) findViewById(R.id.buttonRefresh);
mButtonRefresh.setOnClickListener(this);
}
private void showProgressBar(){
mProgressBar.setVisibility(View.VISIBLE);
mLinearLayoutError.setVisibility(View.GONE);
mSwipeRefreshLayout.setVisibility(View.GONE);
}
private void showContent(){
mProgressBar.setVisibility(View.GONE);
mLinearLayoutError.setVisibility(View.GONE);
mSwipeRefreshLayout.setVisibility(View.VISIBLE);
}
private void showError(String message){
mProgressBar.setVisibility(View.GONE);
mLinearLayoutError.setVisibility(View.VISIBLE);
mSwipeRefreshLayout.setVisibility(View.GONE);
mTextViewErrorMessage.setText(message);
}
#Override
public void onClick(View view) {
if(view == mButtonRefresh){
fetchComments();
}
}
#Override
protected void onDestroy() {
super.onDestroy();
KeyboardUtility.closeKeyboard(this,mEditTextComment);
}
}
Exception:
Method threw 'java.lang.NullPointerException' exception. Cannot evaluate app.govindaconnect.mangaltaraproductions.com.views.CommentListingActivity$4$1.toString()
Only 1 JSON objects get converted and added to the list.
What might be causing the exception? Because this is not happening with other classes.
You can try this
replace this line
TypeToken<List<Comment>> token = new TypeToken<List<Comment>>() {};
with this and try
Type token = new TypeToken<List<Comment>>() {}.getType();
List<Comment> commentsList = gson.fromJson(String.valueOf(resultArray), type);
I took JSON array in a string variable and then it started working.
why don't you try Jackson parser for conversion, it's much simpler and easier
example for you :
List<Comment> list=null;
String json="your json array";
ObjectMapper mapper = new ObjectMapper();
try {
list= mapper.readValue(json, new TypeReference<List<Comment>>(){});
} catch (IOException e) {
throw new Exception(e);
}
I'm working on an Android application that features a shopping cart. The Cart object extends java.lang.Observable so if there are any changes, they are saved to the disk immediately and also so that the badge icon can be updated.
public class Cart extends Observable{
private Set<Product> products;
public Cart(){
products = new HashSet<>();
}
public Cart(Collection<Product> products){
this.products = new HashSet<>(products);
}
public int getTotalItems() {
return products.size();
}
public void clear(){
products.clear();
setChanged();
notifyObservers();
}
public void addProduct(Product product){
products.add(product);
setChanged();
notifyObservers();
}
public void removeProduct(Product product){
products.remove(product);
setChanged();
notifyObservers();
}
public void updateProduct(Product product){
products.remove(product);
products.add(product);
setChanged();
notifyObservers();
}
public List<Product> getProducts() {
return new ArrayList<>(products);
}
}
Example usage
public class MainActivity extends BaseActivity implements Observer {
Cart mCart;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
getApp().getCart().addObserver(this);
setCartItemsCount(getApp().getCart().getTotalItems());
//...
}
#Override
protected void onDestroy() {
super.onDestroy();
if (getApp().getCart() != null) {
getApp().getCart().deleteObserver(this);
}
}
#Override
public void update(Observable observable, Object data) {
if (observable instanceof Cart) {
setCartItemsCount(((Cart) observable).getTotalItems());
}
}
}
I'd like to migrate this code to RxJava but i don't have a clear idea on how to go about it. From what I read, I'm supposed to use BehavioralSubject but I don't know how to adapt the examples I've read to my scenario.
I would appreciate if some guidance or an example.
I made some small example that can help you catch the idea.
I have used this library https://github.com/trello/RxLifecycle, I also recommend you to use this http://google.github.io/dagger/
public class MainActivity extends RxActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final CartPresenter cartPresenter = new CartPresenter();
final TextView counterTextView = (TextView) findViewById(R.id.counter);
final Button button = (Button) findViewById(R.id.button2);
RxView.clicks(button)
.subscribe(new Action1<Void>() {
#Override
public void call(Void aVoid) {
cartPresenter.getCardModel().addProduct(new Random().nextInt(100));
}
});
cartPresenter.getCardObservable()
.compose(this.<CartPresenter.CardModel>bindToLifecycle())
.subscribe(new Action1<CartPresenter.CardModel>() {
#Override
public void call(CartPresenter.CardModel cardModel) {
counterTextView.setText(String.valueOf(cardModel.getProducts().size()));
}
});
}
}
One solution simpler to understand for a beginner
public class CartPresenter {
private final PublishSubject<CardModel> dataChangedSubject = PublishSubject.create();
private final Observable<CardModel> cardObservable;
private final CardModel cardModel;
public CartPresenter() {
cardModel = new CardModel();
this.cardObservable = dataChangedSubject.startWith(cardModel)
.flatMap(new Func1<CardModel, Observable<CardModel>>() {
#Override
public Observable<CardModel> call(CardModel cardModel) {
return Observable.just(cardModel);
}
});
}
public CardModel getCardModel() {
return cardModel;
}
#NonNull
public Observable<CardModel> getCardObservable() {
return cardObservable;
}
class CardModel {
final private Set<Integer> products;
public CardModel() {
this.products = new HashSet<>();
}
public void addProduct(Integer integer) {
products.add(integer);
dataChangedSubject.onNext(this);
}
public Set<Integer> getProducts() {
return products;
}
}
}
And for some more advanced users
public class CartPresenter {
private final PublishSubject<CardModel> dataChangedSubject = PublishSubject.create();
private final Observable<CardModel> cardObservable;
private final CardModel cardModel;
public CartPresenter() {
cardModel = new CardModel();
this.cardObservable = Observable.just(cardModel)
.compose(new Observable.Transformer<CardModel, CardModel>() {
#Override
public Observable<CardModel> call(final Observable<CardModel> cardModelObservable) {
return dataChangedSubject.switchMap(new Func1<Object, Observable<? extends CardModel>>() {
#Override
public Observable<? extends CardModel> call(Object o) {
return cardModelObservable;
}
});
}
});
}
public CardModel getCardModel() {
return cardModel;
}
#NonNull
public Observable<CardModel> getCardObservable() {
return cardObservable;
}
class CardModel {
final private Set<Integer> products;
public CardModel() {
this.products = new HashSet<>();
}
public void addProduct(Integer integer) {
products.add(integer);
dataChangedSubject.onNext(null);
}
public Set<Integer> getProducts() {
return products;
}
}
}