0

I am new to Android and need some help. I'm making an App which manages expenses. I have room database table 'expenses':

CostEntry.java

@Entity(tableName = "expenses")
public class CostEntry {

    @PrimaryKey(autoGenerate = true)
    private int id;
    private String category;
    private String name;
    private int cost;
    private String date;
    private String currency;

//....
}

My activity shows all the expenses on a certain date. Now, I want to make viewpager2 whose page will show all categories with theirs sums - in particular currency (number of pages equals number of different currencies).

So, I made a simple pojo:

TotalCostPojo.java

public class TotalCostPojo {

    public TotalCostPojo() {
    }

    private String currency;
    private List<String> category;
    private List<Integer> categoryCosts;


    public String getCurrency() {
        return currency;
    }

    public void setCurrency(String currency) {
        this.currency = currency;
    }

    public List<String> getCategory() {
        return category;
    }

    public void setCategory(List<String> category) {
        this.category = category;
    }

    public List<Integer> getCategoryCosts() {
        return categoryCosts;
    }

    public void setCategoryCosts(List<Integer> categoryCosts) {
        this.categoryCosts = categoryCosts;
    }
}

But, I got an error:

error: Cannot figure out how to read this field from a cursor. private List categoryCosts; private List category;

This is my Dao:

@Query("SELECT DISTINCT currency, category, SUM (cost) AS categoryCosts FROM expenses WHERE date = :date group by category")
    LiveData<List<TotalCostPojo>> loadTotalCategoryCosts(String date);

I had similar problem before, but then I resolved it with @Relation annotations. I don't know how to do it here because there isn't more than one entity.

Thanks in advance.

castaway
  • 5
  • 4

2 Answers2

0

I think in Room there is no built-in way for what you want (as you've written it's not a case for @Relation annotation, since there is no two real tables to join). I can only suggest workaround you can try (may be it's a obvious boilerplate, and you've expected for more elegance, but still is a way):

  1. Add class CostPojo with fields: currency(String), category(String), categoryCosts(Integer).
  2. Change the type of returning value in your loadTotalCategoryCosts to List<CostPojo>:
LiveData<List<CostPojo>> loadTotalCategoryCosts(String date);
  1. To get the needed data structure for your ViewPager you have to make your own transformation function from List<CostPojo> to List<TotalCostPojo>. For this you can play with LiveData Transformations or for example to add static auxilary method convertToTotalCost to your CostPojo class (input - List<CostPojo>, output - List<TotalCostPojo>).

UPDATE For those who interested - final solution based on this answer made by author of the question is inside separate answer

sergiy tikhonov
  • 4,961
  • 1
  • 10
  • 27
  • Thank you for your directions. This is some new stuff for me so I'll need a day or two. I'll get back to you ;) – castaway Jun 03 '20 at 16:53
  • I'm affraid I'll need aditional help. This static auxilary method convertToTotalCost - how convert a list of objects to another list with different size? I can't convert CostPojo to TotalCostPojo no matter what I do. Any hint would be more than welcome. – castaway Jun 06 '20 at 17:02
  • Then my hint would be next one - you have List, you can create empty List, then use loop for List and processing it - to do some stuff to fill List with whatever logic you want :) If you RxJava fan you can try to make it with its operators, otherwise - just simple loop's processing – sergiy tikhonov Jun 06 '20 at 17:25
  • @castaway, can you post your final solution with `convertToTotalCost` as a separate answer? Since it's a result of your brains' work. It's a usual method on Stackoverflow. Who knows, maybe somebody would upvote your answer or would want to leave some comment on it, so it wouldn't be efficient to merge it with my answer. – sergiy tikhonov Jun 09 '20 at 19:28
  • 1
    sure thing. I thought this is the right way, after all, my answer is based on yours. Thanks again for help ;) – castaway Jun 09 '20 at 21:15
0

Above answer is definitively the correct one, I just want to add some code to clarify transformation function and converting List<CostPojo> to List<TotalCostPojo>. Maybe someone will find that usefull, I knew nothing about that before. Also, if I made some mistakes or if I could made this nicer, please point that out.

TotalCostPojo.java

public class TotalCostPojo {

    public TotalCostPojo() {
    }

    private String currency;
    private Map<String, Integer> categoryCosts;

All the other stuff was in ViewModel (getting LiveData from db and converting to List<TotalCostPojo:

public class DailyExpensesViewModel extends ViewModel {

    private LiveData<List<CostPojo>> categoryCosts;

    public DailyExpensesViewModel(CostDatabase database, String date) {
        categoryCosts = database.costDao().loadCategoryCosts(date);
    }

    public LiveData<List<TotalCostPojo>> getTotalCategoryCosts() {
       return Transformations.map(categoryCosts, this::convertToTotalCost);
    }

    private List<TotalCostPojo> convertToTotalCost(List<CostPojo> costPojo) {

        List<TotalCostPojo> totalCostPojos = new ArrayList<>();
        List<String> helpPair = new ArrayList<>();
        List<String> helpCurrency = new ArrayList<>();

        for (CostPojo costPojo1 : costPojo) {
            //new TotalCostPojo, new currency
            if (!helpCurrency.contains(costPojo1.getCurrency())) {
                TotalCostPojo obj = new TotalCostPojo();
                obj.setCurrency(costPojo1.getCurrency());
                Map<String, Integer> catCost = new TreeMap<>();
                catCost.put(costPojo1.getCategory(), costPojo1.getCategoryCosts());
                obj.setCategoryCosts(catCost);
                totalCostPojos.add(obj);
                //fill helper lists:
                helpCurrency.add(costPojo1.getCurrency());
                helpPair.add(costPojo1.getCurrency() + costPojo1.getCategory());
                //---------------------------------------
                // same currency, new category
            } else if (!helpPair.contains(costPojo1.getCurrency() + costPojo1.getCategory())) {
                for (TotalCostPojo tcp : totalCostPojos) {
                    if (tcp.getCurrency().equals(costPojo1.getCurrency())) {
                        Map<String, Integer> catCost = tcp.getCategoryCosts();
                        catCost.put(costPojo1.getCategory(), costPojo1.getCategoryCosts());
                        tcp.setCategoryCosts(catCost);
                        //fill helper lists:
                        helpPair.add(costPojo1.getCurrency() + costPojo1.getCategory());
                        //-------------------
                    }
                }
                //same currency, same category, updating costValue
            } else if (helpPair.contains(costPojo1.getCurrency() + costPojo1.getCategory())) {
                for (TotalCostPojo tcp : totalCostPojos) {
                    if (tcp.getCurrency().equals(costPojo1.getCurrency())) {
                        Map<String, Integer> catCost = tcp.getCategoryCosts();
                       Integer oldValue = catCost.put(costPojo1.getCategory(), costPojo1.getCategoryCosts());
                       if (oldValue != null) {
                           catCost.put(costPojo1.getCategory(), costPojo1.getCategoryCosts() + oldValue);
                           tcp.setCategoryCosts(catCost);
                       }
                    }
                }
            }
        }
        helpPair.clear();
        helpCurrency.clear();
        return totalCostPojos;
    }
}
castaway
  • 5
  • 4