diff --git a/app/build.gradle b/app/build.gradle index 0bdbd53..55eb919 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -54,4 +54,6 @@ dependencies { compile 'com.google.dagger:dagger:2.0.1' apt 'com.google.dagger:dagger-compiler:2.0.1' provided 'org.glassfish:javax.annotation:10.0-b28' + + testCompile 'junit:junit:4.12' } \ No newline at end of file diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/AddRecordActivity.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/AddRecordActivity.java index ad4f6e9..1fe3bb3 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/AddRecordActivity.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/AddRecordActivity.java @@ -76,7 +76,7 @@ protected boolean initData() { recordController = new RecordController(new RecordRepo(dbHelper), new CategoryController(new CategoryRepo(dbHelper)), new AccountController(accountRepo)); - record = (Record) getIntent().getSerializableExtra(KEY_RECORD); + record = getIntent().getParcelableExtra(KEY_RECORD); mode = (Mode) getIntent().getSerializableExtra(KEY_MODE); type = getIntent().getIntExtra(KEY_TYPE, -1); accountList = accountController.readAll(); diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/ReportActivity.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/ReportActivity.java index 0d5b040..d032b3a 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/ReportActivity.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/ReportActivity.java @@ -1,36 +1,25 @@ package com.blogspot.e_kanivets.moneytracker.activity; -import android.util.Pair; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewTreeObserver; +import android.content.DialogInterface; +import android.support.v7.app.AlertDialog; import android.widget.ExpandableListView; -import android.widget.ListView; import com.blogspot.e_kanivets.moneytracker.R; import com.blogspot.e_kanivets.moneytracker.activity.base.BaseActivity; import com.blogspot.e_kanivets.moneytracker.adapter.ExpandableListReportAdapter; -import com.blogspot.e_kanivets.moneytracker.adapter.ReportItemAdapter; import com.blogspot.e_kanivets.moneytracker.controller.AccountController; -import com.blogspot.e_kanivets.moneytracker.controller.CategoryController; import com.blogspot.e_kanivets.moneytracker.controller.ExchangeRateController; -import com.blogspot.e_kanivets.moneytracker.controller.RecordController; import com.blogspot.e_kanivets.moneytracker.DbHelper; -import com.blogspot.e_kanivets.moneytracker.model.Category; import com.blogspot.e_kanivets.moneytracker.model.Period; import com.blogspot.e_kanivets.moneytracker.model.Record; -import com.blogspot.e_kanivets.moneytracker.model.Report; import com.blogspot.e_kanivets.moneytracker.repo.AccountRepo; -import com.blogspot.e_kanivets.moneytracker.repo.CategoryRepo; import com.blogspot.e_kanivets.moneytracker.repo.ExchangeRateRepo; -import com.blogspot.e_kanivets.moneytracker.repo.base.IRepo; -import com.blogspot.e_kanivets.moneytracker.repo.RecordRepo; -import com.blogspot.e_kanivets.moneytracker.util.Constants; +import com.blogspot.e_kanivets.moneytracker.report.ReportConverter; +import com.blogspot.e_kanivets.moneytracker.report.ReportMaker; +import com.blogspot.e_kanivets.moneytracker.report.base.IReport; +import com.blogspot.e_kanivets.moneytracker.ui.TotalReportViewCreator; -import java.util.ArrayList; -import java.util.HashMap; import java.util.List; -import java.util.Map; import butterknife.Bind; @@ -39,17 +28,11 @@ public class ReportActivity extends BaseActivity { private static final String TAG = "ReportActivity"; public static final String KEY_PERIOD = "key_period"; + public static final String KEY_RECORD_LIST = "key_record_list"; - private Report report; - private String currency; - - @Bind(R.id.list_view) - ListView listView; @Bind(R.id.exp_list_view) ExpandableListView expandableListView; - - private RecordController recordController; - private Period period; + private IReport report; @Override protected int getContentViewId() { @@ -60,129 +43,62 @@ protected int getContentViewId() { protected boolean initData() { super.initData(); + List recordList = getIntent().getParcelableArrayListExtra(KEY_RECORD_LIST); + if (recordList == null) return false; + + Period period = getIntent().getParcelableExtra(KEY_PERIOD); + if (period == null) return false; + DbHelper dbHelper = new DbHelper(ReportActivity.this); - IRepo categoryRepo = new CategoryRepo(dbHelper); - CategoryController categoryController = new CategoryController(categoryRepo); AccountController accountController = new AccountController(new AccountRepo(dbHelper)); ExchangeRateController rateController = new ExchangeRateController(new ExchangeRateRepo(dbHelper)); - recordController = new RecordController(new RecordRepo(dbHelper), categoryController, - accountController); - - currency = DbHelper.DEFAULT_ACCOUNT_CURRENCY; + String currency = DbHelper.DEFAULT_ACCOUNT_CURRENCY; if (accountController.readAll().size() > 0) currency = accountController.readAll().get(0).getCurrency(); - period = getIntent().getParcelableExtra(KEY_PERIOD); - report = new Report(recordController.getRecordsForPeriod(period), currency, rateController); + ReportMaker reportMaker = new ReportMaker(rateController); + report = reportMaker.getReport(currency, period, recordList); + + if (report == null) { + List ratesNeeded = reportMaker.currencyNeeded(currency, recordList); + showExchangeRatesNeededDialog(currency, ratesNeeded); + } - return period != null; + return true; } @Override protected void initViews() { super.initViews(); - listView.setAdapter(new ReportItemAdapter(ReportActivity.this, report.getReportList())); + if (report == null) return; + ReportConverter reportConverter = new ReportConverter(report); - /* Scroll list to bottom only once at start */ - listView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { - private boolean isFirst = true; - - @Override - public void onGlobalLayout() { - if (isFirst) { - isFirst = false; - listView.setSelection(listView.getCount() - 1); - } - } - }); - - initExpandableListView(); + expandableListView.addFooterView(new TotalReportViewCreator(ReportActivity.this, report).create()); + expandableListView.setAdapter(new ExpandableListReportAdapter(ReportActivity.this, reportConverter)); } - private void initExpandableListView() { - /* List for groups */ - List> groupData; - - /* List for child items */ - List> childDataItem; + private void showExchangeRatesNeededDialog(String currency, List ratesNeeded) { + AlertDialog.Builder builder = new AlertDialog.Builder(ReportActivity.this); + builder.setTitle(getString(R.string.cant_make_report)); - /* List for childDataItems */ - List>> childData; - - /* Buffer map for names of values */ - Map m; - - /* Fill the group list */ - groupData = new ArrayList<>(); - for (Pair item : report.getReportList()) { - /* Fill up attribute names for each group */ - m = new HashMap<>(); - m.put(Constants.TITLE_PARAM_NAME, item.first); - m.put(Constants.PRICE_PARAM_NAME, Integer.toString(item.second)); - - groupData.add(m); + StringBuilder sb = new StringBuilder(getString(R.string.rates_needed)); + for (String str : ratesNeeded) { + sb.append("\n").append(str).append(getString(R.string.arrow)).append(currency); } - /* List of attributes of groups for reading */ - String groupFrom[] = new String[]{Constants.TITLE_PARAM_NAME, Constants.PRICE_PARAM_NAME}; - /* List of view IDs for information insertion */ - int groupTo[] = new int[]{R.id.tv_category, R.id.tv_total}; - - /* Create list for childDataItems */ - childData = new ArrayList<>(); - - for (Map group : groupData) { - childDataItem = new ArrayList<>(); - /* Fill up attribute names for each child item */ - for (Record record : report.getSummaryRecordList()) { - if (record.getCategory().equals(group.get(Constants.TITLE_PARAM_NAME))) { - int price = record.getPrice(); - if (!record.isIncome()) { - price *= -1; - } - - m = new HashMap<>(); - m.put(Constants.TITLE_PARAM_NAME, record.getTitle()); - m.put(Constants.PRICE_PARAM_NAME, Integer.toString(price)); - - childDataItem.add(m); - } - } + builder.setMessage(sb.toString()); - /* Add childDataItem to common childData */ - childData.add(childDataItem); - } + builder.setPositiveButton(android.R.string.ok, null); - /* List of attributes of childItems for reading */ - String childFrom[] = new String[]{Constants.TITLE_PARAM_NAME, Constants.PRICE_PARAM_NAME}; - /* List of view IDs for information insertion */ - int childTo[] = new int[]{R.id.tv_category, R.id.tv_total}; - - expandableListView.addFooterView(getSummaryReportView(report.getSummaryReportList())); - expandableListView.setAdapter(new ExpandableListReportAdapter( - ReportActivity.this, - groupData, - R.layout.view_report_item_exp, - groupFrom, - groupTo, - childData, - R.layout.view_report_item, - childFrom, - childTo) { + builder.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialog) { + finish(); + } }); - } - - private View getSummaryReportView(List> summaryReportList) { - ViewGroup viewGroup = (ViewGroup) getLayoutInflater().inflate(R.layout.view_summary_report, null); - - ReportItemAdapter adapter = new ReportItemAdapter(ReportActivity.this, summaryReportList); - - for (int i = 0; i < adapter.getCount(); i++) { - viewGroup.addView(adapter.getView(i, null, null)); - } - return viewGroup; + builder.show(); } } \ No newline at end of file diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/ExpandableListReportAdapter.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/ExpandableListReportAdapter.java index 5f74037..455c391 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/ExpandableListReportAdapter.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/ExpandableListReportAdapter.java @@ -8,9 +8,11 @@ import android.widget.TextView; import com.blogspot.e_kanivets.moneytracker.R; +import com.blogspot.e_kanivets.moneytracker.report.ReportConverter; import com.blogspot.e_kanivets.moneytracker.util.Constants; import java.util.List; +import java.util.Locale; import java.util.Map; import butterknife.Bind; @@ -31,6 +33,12 @@ public class ExpandableListReportAdapter extends SimpleExpandableListAdapter { private int red; private int green; + public ExpandableListReportAdapter(Context context, ReportConverter converter) { + this(context, converter.getGroupData(), converter.getGroupLayout(), + converter.getGroupFrom(), converter.getGroupTo(), converter.getChildData(), + converter.getChildLayout(), converter.getChildFrom(), converter.getChildTo()); + } + @SuppressWarnings("deprecation") public ExpandableListReportAdapter(Context context, List> groupData, int groupLayout, String[] groupFrom, int[] groupTo, @@ -68,7 +76,7 @@ private void customizeView(View view, Map values, boolean groupV if (viewHolder == null) viewHolder = new ViewHolder(view); /* Customize view to fit to model and UI */ - Integer price = Integer.parseInt(values.get(Constants.PRICE_PARAM_NAME)); + Double price = Double.parseDouble(values.get(Constants.PRICE_PARAM_NAME)); if (groupView) view.setBackgroundColor(price < 0 ? whiteRed : whiteGreen); else view.setBackgroundColor(white); @@ -77,7 +85,11 @@ private void customizeView(View view, Map values, boolean groupV viewHolder.tvTotal.setTextColor(price >= 0 ? green : red); viewHolder.tvCategory.setText(values.get(Constants.TITLE_PARAM_NAME)); - viewHolder.tvTotal.setText((price >= 0 ? "+ " : "- ") + Math.abs(price)); + viewHolder.tvTotal.setText(format(price)); + } + + private String format(double amount) { + return (amount >= 0 ? "+ " : "- ") + String.format(Locale.getDefault(), "%.0f", Math.abs(amount)); } public static class ViewHolder { diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/ReportItemAdapter.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/ReportItemAdapter.java deleted file mode 100644 index b42a609..0000000 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/ReportItemAdapter.java +++ /dev/null @@ -1,101 +0,0 @@ -package com.blogspot.e_kanivets.moneytracker.adapter; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.util.Pair; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.TextView; - -import com.blogspot.e_kanivets.moneytracker.R; -import com.blogspot.e_kanivets.moneytracker.model.Record; - -import java.util.List; - -import butterknife.Bind; -import butterknife.ButterKnife; - -/** - * Custom adapter class for {@link Record} entity. - * Created on 11/09/14. - * - * @author Evgenii Kanivets - */ -public class ReportItemAdapter extends BaseAdapter { - private Context context; - private List> records; - - private int whiteRed; - private int whiteGreen; - private int red; - private int green; - - @SuppressWarnings("deprecation") - public ReportItemAdapter(Context context, List> records) { - this.context = context; - this.records = records; - - whiteRed = context.getResources().getColor(R.color.white_red); - whiteGreen = context.getResources().getColor(R.color.white_green); - red = context.getResources().getColor(R.color.red); - green = context.getResources().getColor(R.color.green); - } - - @Override - public int getCount() { - return records.size(); - } - - @Override - public Object getItem(int i) { - return records.get(i); - } - - @Override - public long getItemId(int i) { - return i; - } - - @SuppressLint("SetTextI18n") - @Override - public View getView(int i, View convertView, ViewGroup parent) { - ViewHolder viewHolder; - - if (convertView == null) { - LayoutInflater layoutInflater = LayoutInflater.from(context); - - convertView = layoutInflater.inflate(R.layout.view_summary_report_item, parent, false); - viewHolder = new ViewHolder(convertView); - - convertView.setTag(viewHolder); - } else viewHolder = (ViewHolder) convertView.getTag(); - - if (i == getCount() - 1) viewHolder.line.setVisibility(View.VISIBLE); - else if (i == getCount() - 3) viewHolder.line.setVisibility(View.VISIBLE); - - convertView.setBackgroundColor(records.get(i).second < 0 ? whiteRed : whiteGreen); - - viewHolder.tvTotal.setTextColor(records.get(i).second >= 0 ? green : red); - - viewHolder.tvCategory.setText(records.get(i).first); - viewHolder.tvTotal.setText((records.get(i).second >= 0 ? "+ " : "- ") - + Math.abs(records.get(i).second)); - - return convertView; - } - - public static class ViewHolder { - @Bind(R.id.line) - View line; - @Bind(R.id.tv_category) - TextView tvCategory; - @Bind(R.id.tv_total) - TextView tvTotal; - - public ViewHolder(View view) { - ButterKnife.bind(this, view); - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/fragment/RecordsFragment.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/fragment/RecordsFragment.java index 953f38b..8409c35 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/fragment/RecordsFragment.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/fragment/RecordsFragment.java @@ -42,6 +42,7 @@ import com.blogspot.e_kanivets.moneytracker.ui.ChangeDateDialog; import com.blogspot.e_kanivets.moneytracker.util.PrefUtils; +import java.util.ArrayList; import java.util.Calendar; import java.util.Date; import java.util.List; @@ -156,6 +157,7 @@ public void addIncome() { public void showReport() { Intent intent = new Intent(getActivity(), ReportActivity.class); intent.putExtra(ReportActivity.KEY_PERIOD, periodController.getPeriod()); + intent.putExtra(ReportActivity.KEY_RECORD_LIST, (ArrayList) recordList); startActivity(intent); } diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/model/ExchangeRate.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/model/ExchangeRate.java index f1665f7..0935c53 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/model/ExchangeRate.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/model/ExchangeRate.java @@ -50,6 +50,18 @@ public double getAmount() { return amount; } + @Override + public boolean equals(Object o) { + if (o instanceof ExchangeRate) { + ExchangeRate rate = (ExchangeRate) o; + return this.id == rate.getId() + && this.createdAt == rate.getCreatedAt() + && this.fromCurrency.equals(rate.getFromCurrency()) + && this.toCurrency.equals(rate.getToCurrency()) + && this.amount == rate.getAmount(); + } else return false; + } + @SuppressWarnings("StringBufferReplaceableByString") @Override public String toString() { diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/model/Period.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/model/Period.java index ef4aa4e..9d5a19e 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/model/Period.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/model/Period.java @@ -176,4 +176,13 @@ public static Period yearPeriod() { return new Period(first, last); } + + @Override + public boolean equals(Object o) { + if (o instanceof Period) { + Period period = (Period) o; + return this.first.equals(period.getFirst()) + && this.last.equals(period.getLast()); + } else return false; + } } \ No newline at end of file diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/model/Record.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/model/Record.java index 088c14e..5b7e945 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/model/Record.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/model/Record.java @@ -1,5 +1,9 @@ package com.blogspot.e_kanivets.moneytracker.model; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.NonNull; + import com.blogspot.e_kanivets.moneytracker.DbHelper; import com.blogspot.e_kanivets.moneytracker.MtApp; import com.blogspot.e_kanivets.moneytracker.controller.CategoryController; @@ -11,7 +15,7 @@ * Entity class. * Created by eugene on 01/09/14. */ -public class Record implements IEntity, Serializable { +public class Record implements IEntity, Parcelable { public static final int TYPE_INCOME = 0; public static final int TYPE_EXPENSE = 1; @@ -54,6 +58,42 @@ public Record(long time, int type, String title, String category, int price, lon this.currency = currency; } + public Record(@NonNull Record record) { + this.id = record.getId(); + this.time = record.getTime(); + this.type = record.getType(); + this.title = record.getTitle(); + this.categoryId = record.getCategoryId(); + this.category = record.getCategory(); + this.price = record.getPrice(); + this.accountId = record.getAccountId(); + this.currency = record.getCurrency(); + } + + protected Record(Parcel in) { + id = in.readLong(); + time = in.readLong(); + type = in.readInt(); + title = in.readString(); + categoryId = in.readLong(); + category = in.readString(); + price = in.readInt(); + accountId = in.readLong(); + currency = in.readString(); + } + + public static final Creator CREATOR = new Creator() { + @Override + public Record createFromParcel(Parcel in) { + return new Record(in); + } + + @Override + public Record[] newArray(int size) { + return new Record[size]; + } + }; + public int getType() { return type; } @@ -153,4 +193,38 @@ public String toString() { return sb.toString(); } + + @Override + public boolean equals(Object o) { + if (o instanceof Record) { + Record record = (Record) o; + return this.id == record.getId() + && this.time == record.getTime() + && this.type == record.getType() + && this.title.equals(record.getTitle()) + && this.categoryId == record.getCategoryId() + && this.category.equals(record.getCategory()) + && this.price == record.getPrice() + && this.accountId == record.getAccountId() + && this.currency.equals(record.getCurrency()); + } else return false; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(id); + dest.writeLong(time); + dest.writeInt(type); + dest.writeString(title); + dest.writeLong(categoryId); + dest.writeString(category); + dest.writeInt(price); + dest.writeLong(accountId); + dest.writeString(currency); + } } \ No newline at end of file diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/model/Report.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/model/Report.java deleted file mode 100644 index dfd93d8..0000000 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/model/Report.java +++ /dev/null @@ -1,196 +0,0 @@ -package com.blogspot.e_kanivets.moneytracker.model; - -import android.util.Pair; - -import com.blogspot.e_kanivets.moneytracker.R; -import com.blogspot.e_kanivets.moneytracker.MtApp; -import com.blogspot.e_kanivets.moneytracker.controller.ExchangeRateController; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -/** - * Created on 11/09/14. - * - * @author Evgenii Kanivets - */ -public class Report { - private List records; - private String currency; - private ExchangeRateController exchangeRateController; - private List summaryRecordList; - private List> reportList; - private List> summaryReportList; - private Map rateMap; - - public Report(List records, String currency, ExchangeRateController exchangeRateController) { - this.records = records; - this.currency = currency; - this.exchangeRateController = exchangeRateController; - - rateMap = initRateMap(); - makeReport(); - } - - public List> getReportList() { - return reportList; - } - - public List> getSummaryReportList() { - return summaryReportList; - } - - public List getSummaryRecordList() { - return summaryRecordList; - } - - private void makeReport() { - HashMap map = new HashMap<>(); - HashMap recordMap = new HashMap<>(); - - // Calculate total of all records - int totalIncome = 0, totalExpense = 0; - - for (Record record : records) { - int sum = 0; - //If category has already exist add to it - if (map.containsKey(record.getCategory())) { - sum = map.get(record.getCategory()); - } - - int convertedPrice = record.getPrice(); - if (!currency.equals(record.getCurrency())) { - ExchangeRate rate = rateMap.get(record.getCurrency()); - if (rate != null) convertedPrice *= rate.getAmount(); - } - - //Add or subtract price - if (record.isIncome()) { - totalIncome += convertedPrice; - map.put(record.getCategory(), sum + convertedPrice); - } else { - totalExpense -= convertedPrice; - map.put(record.getCategory(), sum - convertedPrice); - } - - String key = record.getCategory() + "/" + record.getTitle(); - Record summaryRecord = recordMap.get(key); - int price = convertedPrice; - - if (record.isIncome()) { - price *= -1; - } - - if (summaryRecord == null) { - summaryRecord = new Record(-1, -1, -1, record.getTitle(), record.getCategoryId(), - price, record.getAccountId(), currency); - } else { - summaryRecord.setPrice(summaryRecord.getPrice() + price); - } - - recordMap.put(key, summaryRecord); - } - - fillReportList(map); - fillSummaryReportList(totalIncome, totalExpense); - fillRecordList(recordMap); - } - - private void fillReportList(HashMap map) { - //Sort reportList - List> reportIncomes = new ArrayList<>(); - List> reportExpenses = new ArrayList<>(); - - for (String name : map.keySet()) { - if (map.get(name) > 0) reportIncomes.add(new Pair<>(name, map.get(name))); - else reportExpenses.add(new Pair<>(name, map.get(name))); - } - - sortList(reportIncomes); - sortList(reportExpenses); - - //Added incomes and expenses to ArrayList - reportList = new ArrayList<>(); - reportList.addAll(reportIncomes); - reportList.addAll(reportExpenses); - } - - private void fillSummaryReportList(int totalIncome, int totalExpense) { - //Add summary row to list - summaryReportList = new ArrayList<>(); - summaryReportList.add(new Pair<>( - MtApp.get().getResources().getString(R.string.total_incomes) + " :", totalIncome)); - summaryReportList.add(new Pair<>( - MtApp.get().getResources().getString(R.string.total_expenses) + " :", totalExpense)); - summaryReportList.add(new Pair<>( - MtApp.get().getResources().getString(R.string.total) + " :", totalExpense + totalIncome)); - } - - private void fillRecordList(HashMap recordMap) { - //Sort reportList - List recordIncomes = new ArrayList<>(); - List recordExpenses = new ArrayList<>(); - - for (String name : recordMap.keySet()) { - if (recordMap.get(name).getPrice() > 0) { - recordIncomes.add(recordMap.get(name)); - } else { - recordExpenses.add(recordMap.get(name)); - } - } - - sortRecordList(recordIncomes); - sortRecordList(recordExpenses); - - //Added incomes and expenses to ArrayList - summaryRecordList = new ArrayList<>(); - summaryRecordList.addAll(recordIncomes); - summaryRecordList.addAll(recordExpenses); - - sortRecordList(summaryRecordList); - } - - private void sortList(List> list) { - int n = list.size(); - - for (int i = 0; i < n - 1; i++) { - for (int j = 0; j < n - i - 1; j++) { - if (Math.abs(list.get(j).second) < Math.abs(list.get(j + 1).second)) { - Pair tmp = list.get(j); - list.set(j, list.get(j + 1)); - list.set(j + 1, tmp); - } - } - } - } - - private void sortRecordList(List list) { - int n = list.size(); - - for (int i = 0; i < n - 1; i++) { - for (int j = 0; j < n - i - 1; j++) { - if (Math.abs(list.get(j).getPrice()) < Math.abs(list.get(j + 1).getPrice())) { - Record tmp = list.get(j); - list.set(j, list.get(j + 1)); - list.set(j + 1, tmp); - } - } - } - } - - private Map initRateMap() { - Map rateMap = new TreeMap<>(); - - for (ExchangeRate rate : exchangeRateController.readAll()) { - if (!currency.equals(rate.getToCurrency())) continue; - if (rateMap.containsKey(rate.getFromCurrency())) continue; - - rateMap.put(rate.getFromCurrency(), rate); - } - - return rateMap; - } -} \ No newline at end of file diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/ExchangeRateProvider.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/ExchangeRateProvider.java new file mode 100644 index 0000000..f053e67 --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/ExchangeRateProvider.java @@ -0,0 +1,62 @@ +package com.blogspot.e_kanivets.moneytracker.report; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.blogspot.e_kanivets.moneytracker.controller.ExchangeRateController; +import com.blogspot.e_kanivets.moneytracker.model.ExchangeRate; +import com.blogspot.e_kanivets.moneytracker.model.Record; +import com.blogspot.e_kanivets.moneytracker.report.base.IExchangeRateProvider; + +import java.util.Map; +import java.util.TreeMap; + +/** + * First {@link IExchangeRateProvider} implementation. + * Created on 2/25/16. + * + * @author Evgenii Kanivets + */ +public class ExchangeRateProvider implements IExchangeRateProvider { + @SuppressWarnings("unused") + private static final String TAG = "ExchangeRateProvider"; + + private String toCurrency; + private final ExchangeRateController controller; + private final Map rateMap; + + /** + * @param toCurrency code of toCurrency to convert to + * @param controller dependency to get needed rates + */ + public ExchangeRateProvider(String toCurrency, ExchangeRateController controller) { + this.toCurrency = toCurrency; + this.controller = controller; + + rateMap = getRateMap(); + } + + @Nullable + @Override + public ExchangeRate getRate(@Nullable Record record) { + if (record == null) return null; + + String fromCurrency = record.getCurrency(); + + return rateMap.get(fromCurrency); + } + + @NonNull + private Map getRateMap() { + Map rateMap = new TreeMap<>(); + + for (ExchangeRate rate : controller.readAll()) { + if (!toCurrency.equals(rate.getToCurrency())) continue; + if (rateMap.containsKey(rate.getFromCurrency())) continue; + + rateMap.put(rate.getFromCurrency(), rate); + } + + return rateMap; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/Report.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/Report.java new file mode 100644 index 0000000..25046fb --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/Report.java @@ -0,0 +1,222 @@ +package com.blogspot.e_kanivets.moneytracker.report; + +import android.support.annotation.NonNull; + +import com.blogspot.e_kanivets.moneytracker.model.ExchangeRate; +import com.blogspot.e_kanivets.moneytracker.model.Period; +import com.blogspot.e_kanivets.moneytracker.model.Record; +import com.blogspot.e_kanivets.moneytracker.report.base.IExchangeRateProvider; +import com.blogspot.e_kanivets.moneytracker.report.base.IReport; +import com.blogspot.e_kanivets.moneytracker.report.model.CategoryRecord; +import com.blogspot.e_kanivets.moneytracker.report.model.SummaryRecord; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * First {@link IReport} implementation. + * Created on 2/25/16. + * + * @author Evgenii Kanivets + */ +public class Report implements IReport { + @SuppressWarnings("unused") + private static final String TAG = "Report"; + + private String currency; + private Period period; + private IExchangeRateProvider rateProvider; + + private double totalIncome; + private double totalExpense; + private List categoryRecordList; + + public Report(String currency, Period period, List recordList, IExchangeRateProvider rateProvider) { + if (currency == null || period == null || recordList == null || rateProvider == null) + throw new NullPointerException("Params can't be null"); + + this.currency = currency; + this.period = period; + this.rateProvider = rateProvider; + + categoryRecordList = new ArrayList<>(); + + makeReport(recordList); + } + + @NonNull + @Override + public String getCurrency() { + return currency; + } + + @NonNull + @Override + public Period getPeriod() { + return period; + } + + @Override + public double getTotal() { + return getTotalExpense() + getTotalIncome(); + } + + @Override + public double getTotalIncome() { + return totalIncome; + } + + @Override + public double getTotalExpense() { + return totalExpense; + } + + @NonNull + @Override + public List getSummary() { + return categoryRecordList; + } + + private void makeReport(List recordList) { + totalIncome = 0; + totalExpense = 0; + categoryRecordList.clear(); + + List convertedRecordList = convertRecordList(recordList); + + Map> categorySortedMap = new TreeMap<>(); + + for (Record record : convertedRecordList) { + switch (record.getType()) { + case Record.TYPE_INCOME: + totalIncome += record.getPrice(); + break; + + case Record.TYPE_EXPENSE: + totalExpense -= record.getPrice(); + break; + + default: + break; + } + + String category = record.getCategory(); + + if (!categorySortedMap.containsKey(category)) + categorySortedMap.put(category, new ArrayList()); + categorySortedMap.get(category).add(record); + } + + for (String category : categorySortedMap.keySet()) { + categoryRecordList.add(createCategoryRecord(category, categorySortedMap.get(category))); + } + + Collections.sort(categoryRecordList, new Comparator() { + @Override + public int compare(CategoryRecord lhs, CategoryRecord rhs) { + return compareDoubles(lhs.getAmount(), rhs.getAmount()); + } + }); + } + + @NonNull + private List convertRecordList(List recordList) { + List convertedRecordList = new ArrayList<>(); + + for (Record record : recordList) { + int convertedPrice = record.getPrice(); + + if (!currency.equals(record.getCurrency())) { + ExchangeRate exchangeRate = rateProvider.getRate(record); + if (exchangeRate == null) throw new NullPointerException("No exchange rate found"); + convertedPrice *= exchangeRate.getAmount(); + } + + Record convertedRecord = new Record(record); + convertedRecord.setPrice(convertedPrice); + + convertedRecordList.add(convertedRecord); + } + + return convertedRecordList; + } + + @NonNull + private CategoryRecord createCategoryRecord(String category, List recordList) { + Map> titleSortedMap = new TreeMap<>(); + + double amount = 0; + + for (Record record : recordList) { + amount += getAmount(record); + + String title = record.getTitle(); + + if (!titleSortedMap.containsKey(title)) + titleSortedMap.put(title, new ArrayList()); + titleSortedMap.get(title).add(record); + } + + CategoryRecord categoryRecord = new CategoryRecord(category, currency, amount); + + for (String title : titleSortedMap.keySet()) { + categoryRecord.add(createSummaryRecord(title, titleSortedMap.get(title))); + } + + Collections.sort(categoryRecord.getSummaryRecordList(), new Comparator() { + @Override + public int compare(SummaryRecord lhs, SummaryRecord rhs) { + return compareDoubles(lhs.getAmount(), rhs.getAmount()); + } + }); + + return categoryRecord; + } + + @NonNull + private SummaryRecord createSummaryRecord(String title, List recordList) { + double amount = 0; + + for (Record record : recordList) { + amount += getAmount(record); + } + + SummaryRecord summaryRecord = new SummaryRecord(title, currency, amount); + + for (Record record : recordList) { + summaryRecord.add(record); + } + + Collections.sort(summaryRecord.getRecordList(), new Comparator() { + @Override + public int compare(Record lhs, Record rhs) { + return compareDoubles(getAmount(lhs), getAmount(rhs)); + } + }); + + return summaryRecord; + } + + private double getAmount(Record record) { + switch (record.getType()) { + case Record.TYPE_INCOME: + return record.getPrice(); + + case Record.TYPE_EXPENSE: + return -record.getPrice(); + + default: + return 0; + } + } + + private int compareDoubles(double lhs, double rhs) { + if (lhs > 0 && rhs < 0) return -1; + else if (lhs < 0 && rhs > 0) return 1; + else return -1 * Double.compare(Math.abs(lhs), Math.abs(rhs)); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/ReportConverter.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/ReportConverter.java new file mode 100644 index 0000000..7aad6e4 --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/ReportConverter.java @@ -0,0 +1,93 @@ +package com.blogspot.e_kanivets.moneytracker.report; + +import android.support.annotation.LayoutRes; +import android.support.annotation.NonNull; + +import com.blogspot.e_kanivets.moneytracker.R; +import com.blogspot.e_kanivets.moneytracker.report.base.IReport; +import com.blogspot.e_kanivets.moneytracker.report.model.CategoryRecord; +import com.blogspot.e_kanivets.moneytracker.report.model.SummaryRecord; +import com.blogspot.e_kanivets.moneytracker.util.Constants; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Util class to convert {@link Report} to {@link android.widget.ExpandableListView} input data. + * Created on 2/26/16. + * + * @author Evgenii Kanivets + */ +public class ReportConverter { + private final IReport report; + + public ReportConverter(@NonNull IReport report) { + this.report = report; + } + + public List> getGroupData() { + List> groupData = new ArrayList<>(); + + for (CategoryRecord categoryRecord : report.getSummary()) { + Map m = new HashMap<>(); + m.put(Constants.TITLE_PARAM_NAME, categoryRecord.getTitle()); + m.put(Constants.PRICE_PARAM_NAME, Double.toString(categoryRecord.getAmount())); + + groupData.add(m); + } + + return groupData; + } + + @LayoutRes + public int getGroupLayout() { + return R.layout.view_report_item_exp; + } + + @NonNull + public String[] getGroupFrom() { + return new String[]{Constants.TITLE_PARAM_NAME, Constants.PRICE_PARAM_NAME}; + } + + @NonNull + public int[] getGroupTo() { + return new int[]{R.id.tv_category, R.id.tv_total}; + } + + @NonNull + public List>> getChildData() { + List>> childData = new ArrayList<>(); + + for (CategoryRecord categoryRecord : report.getSummary()) { + List> childDataItem = new ArrayList<>(); + for (SummaryRecord summaryRecord : categoryRecord.getSummaryRecordList()) { + Map m = new HashMap<>(); + m.put(Constants.TITLE_PARAM_NAME, summaryRecord.getTitle()); + m.put(Constants.PRICE_PARAM_NAME, Double.toString(summaryRecord.getAmount())); + + childDataItem.add(m); + } + + childData.add(childDataItem); + } + + return childData; + } + + @LayoutRes + public int getChildLayout() { + return R.layout.view_report_item; + } + + @NonNull + public String[] getChildFrom() { + return new String[]{Constants.TITLE_PARAM_NAME, Constants.PRICE_PARAM_NAME}; + } + + @NonNull + public int[] getChildTo() { + return new int[]{R.id.tv_category, R.id.tv_total}; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/ReportMaker.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/ReportMaker.java new file mode 100644 index 0000000..4ee32dd --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/ReportMaker.java @@ -0,0 +1,55 @@ +package com.blogspot.e_kanivets.moneytracker.report; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.blogspot.e_kanivets.moneytracker.controller.ExchangeRateController; +import com.blogspot.e_kanivets.moneytracker.model.ExchangeRate; +import com.blogspot.e_kanivets.moneytracker.model.Period; +import com.blogspot.e_kanivets.moneytracker.model.Record; +import com.blogspot.e_kanivets.moneytracker.report.base.IExchangeRateProvider; +import com.blogspot.e_kanivets.moneytracker.report.base.IReport; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; + +/** + * Util class to encapsulate {@link Report} generation logic. + * Created on 2/26/16. + * + * @author Evgenii Kanivets + */ +public class ReportMaker { + private final ExchangeRateController rateController; + + public ReportMaker(ExchangeRateController exchangeRateController) { + this.rateController = exchangeRateController; + } + + @Nullable + public IReport getReport(String currency, Period period, List recordList) { + if (currencyNeeded(currency, recordList).size() != 0) return null; + + IExchangeRateProvider rateProvider = new ExchangeRateProvider(currency, rateController); + return new Report(currency, period, recordList, rateProvider); + } + + @NonNull + public List currencyNeeded(String currency, List recordList) { + Set currencies = new TreeSet<>(); + + for (Record record : recordList) { + currencies.add(record.getCurrency()); + } + + currencies.remove(currency); + + for (ExchangeRate rate : rateController.readAll()) { + if (rate.getToCurrency().equals(currency)) currencies.remove(rate.getFromCurrency()); + } + + return new ArrayList<>(currencies); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/base/IExchangeRateProvider.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/base/IExchangeRateProvider.java new file mode 100644 index 0000000..04d9035 --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/base/IExchangeRateProvider.java @@ -0,0 +1,23 @@ +package com.blogspot.e_kanivets.moneytracker.report.base; + +import android.support.annotation.Nullable; + +import com.blogspot.e_kanivets.moneytracker.model.ExchangeRate; +import com.blogspot.e_kanivets.moneytracker.model.Record; + +/** + * Interface that represents a contract of access to currency exchange rate. + * Created on 2/25/16. + * + * @author Evgenii Kanivets + */ +public interface IExchangeRateProvider { + /** + * Gives an exchange rate for given record. + * + * @param record to request an exchange rate for + * @return exchange rate + */ + @Nullable + ExchangeRate getRate(@Nullable Record record); +} \ No newline at end of file diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/base/IReport.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/base/IReport.java new file mode 100644 index 0000000..14850d5 --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/base/IReport.java @@ -0,0 +1,46 @@ +package com.blogspot.e_kanivets.moneytracker.report.base; + +import android.support.annotation.NonNull; + +import com.blogspot.e_kanivets.moneytracker.model.Period; +import com.blogspot.e_kanivets.moneytracker.report.model.CategoryRecord; + +import java.util.List; + +/** + * Interface that represents a contract of access to report data. + * Created on 2/25/16. + * + * @author Evgenii Kanivets + */ +public interface IReport { + /** + * @return code of report currency + */ + @NonNull String getCurrency(); + + /** + * @return period of report + */ + @NonNull Period getPeriod(); + + /** + * @return total sum in given currency for given period + */ + double getTotal(); + + /** + * @return total of all incomes for given period + */ + double getTotalIncome(); + + /** + * @return total of all expenses for given period + */ + double getTotalExpense(); + + /** + * @return summary list + */ + @NonNull List getSummary(); +} \ No newline at end of file diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/model/CategoryRecord.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/model/CategoryRecord.java new file mode 100644 index 0000000..306a59d --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/model/CategoryRecord.java @@ -0,0 +1,57 @@ +package com.blogspot.e_kanivets.moneytracker.report.model; + +import android.support.annotation.NonNull; + +import java.util.ArrayList; +import java.util.List; + +/** + * Entity class. + * Created on 2/25/16. + * + * @author Evgenii Kanivets + */ +public class CategoryRecord { + private String title; + private String currency; + private double amount; + private List summaryRecordList; + + public CategoryRecord(String title, String currency, double amount) { + this.title = title; + this.currency = currency; + this.amount = amount; + this.summaryRecordList = new ArrayList<>(); + } + + public void add(@NonNull SummaryRecord summaryRecord) { + summaryRecordList.add(summaryRecord); + } + + public String getTitle() { + return title; + } + + public String getCurrency() { + return currency; + } + + public double getAmount() { + return amount; + } + + public List getSummaryRecordList() { + return summaryRecordList; + } + + @Override + public boolean equals(Object o) { + if (o instanceof CategoryRecord) { + CategoryRecord categoryRecord = (CategoryRecord) o; + return this.title.equals(categoryRecord.getTitle()) + && this.currency.equals(categoryRecord.getCurrency()) + && this.amount == categoryRecord.getAmount() + && this.summaryRecordList.equals(categoryRecord.getSummaryRecordList()); + } else return false; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/model/SummaryRecord.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/model/SummaryRecord.java new file mode 100644 index 0000000..065d11d --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/model/SummaryRecord.java @@ -0,0 +1,58 @@ +package com.blogspot.e_kanivets.moneytracker.report.model; + +import android.support.annotation.NonNull; + +import com.blogspot.e_kanivets.moneytracker.model.Record; + +import java.util.ArrayList; +import java.util.List; + +/** + * Entity class. + * Created on 2/25/16. + * + * @author Evgenii Kanivets + */ +public class SummaryRecord { + private String title; + private String currency; + private double amount; + private List recordList; + + public SummaryRecord(String title, String currency, double amount) { + this.title = title; + this.currency = currency; + this.amount = amount; + recordList = new ArrayList<>(); + } + + public void add(@NonNull Record record) { + recordList.add(record); + } + + public String getTitle() { + return title; + } + + public String getCurrency() { + return currency; + } + + public double getAmount() { + return amount; + } + + public List getRecordList() { + return recordList; + } + + @Override + public boolean equals(Object o) { + if (o instanceof SummaryRecord) { + SummaryRecord summaryRecord = (SummaryRecord) o; + return this.currency.equals(summaryRecord.getCurrency()) + && this.amount == summaryRecord.getAmount() + && this.recordList.equals(summaryRecord.getRecordList()); + } else return false; + } +} \ No newline at end of file diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/ui/TotalReportViewCreator.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/ui/TotalReportViewCreator.java new file mode 100644 index 0000000..436c19f --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/ui/TotalReportViewCreator.java @@ -0,0 +1,86 @@ +package com.blogspot.e_kanivets.moneytracker.ui; + +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.TextView; + +import com.blogspot.e_kanivets.moneytracker.R; +import com.blogspot.e_kanivets.moneytracker.report.base.IReport; + +import java.util.Locale; + +import butterknife.Bind; +import butterknife.ButterKnife; + +/** + * Util class to encapsulate Total view creation. + * Created on 2/26/16. + * + * @author Evgenii Kanivets + */ +public class TotalReportViewCreator { + private final IReport report; + private final LayoutInflater layoutInflater; + + private int whiteRed; + private int whiteGreen; + private int red; + private int green; + + @SuppressWarnings("deprecation") + public TotalReportViewCreator(Context context, IReport report) { + this.report = report; + layoutInflater = LayoutInflater.from(context); + + whiteRed = context.getResources().getColor(R.color.white_red); + whiteGreen = context.getResources().getColor(R.color.white_green); + red = context.getResources().getColor(R.color.red); + green = context.getResources().getColor(R.color.green); + } + + public View create() { + View view = layoutInflater.inflate(R.layout.view_summary_report, null); + + ViewHolder viewHolder = new ViewHolder(view); + + viewHolder.llTotalIncome.setBackgroundColor(report.getTotalIncome() < 0 ? whiteRed : whiteGreen); + viewHolder.tvTotalIncome.setTextColor(report.getTotalIncome() >= 0 ? green : red); + viewHolder.tvTotalIncome.setText(format(report.getTotalIncome())); + + viewHolder.llTotalExpense.setBackgroundColor(report.getTotalExpense() < 0 ? whiteRed : whiteGreen); + viewHolder.tvTotalExpense.setTextColor(report.getTotalExpense() >= 0 ? green : red); + viewHolder.tvTotalExpense.setText(format(report.getTotalExpense())); + + viewHolder.llTotal.setBackgroundColor(report.getTotal() < 0 ? whiteRed : whiteGreen); + viewHolder.tvTotal.setTextColor(report.getTotal() >= 0 ? green : red); + viewHolder.tvTotal.setText(format(report.getTotal())); + + return view; + } + + private String format(double amount) { + return (amount >= 0 ? "+ " : "- ") + String.format(Locale.getDefault(), "%.0f", Math.abs(amount)); + } + + public static class ViewHolder { + @Bind(R.id.ll_total_income) + View llTotalIncome; + @Bind(R.id.tv_total_income) + TextView tvTotalIncome; + + @Bind(R.id.ll_total_expense) + View llTotalExpense; + @Bind(R.id.tv_total_expense) + TextView tvTotalExpense; + + @Bind(R.id.ll_total) + View llTotal; + @Bind(R.id.tv_total) + TextView tvTotal; + + public ViewHolder(View view) { + ButterKnife.bind(this, view); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/Constants.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/Constants.java index 8fa18bb..6ddf9bb 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/Constants.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/Constants.java @@ -15,7 +15,6 @@ public interface Constants { String KEY_USED_PERIOD = "key_used_period"; int DEFAULT_USED_PERIOD = 1; - String TITLE_PARAM_NAME = "title"; String PRICE_PARAM_NAME = "price"; diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/CurrencyProvider.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/CurrencyProvider.java index 61a779a..ab8987f 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/CurrencyProvider.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/CurrencyProvider.java @@ -1,5 +1,7 @@ package com.blogspot.e_kanivets.moneytracker.util; +import com.blogspot.e_kanivets.moneytracker.DbHelper; + import java.util.ArrayList; import java.util.Collections; import java.util.Currency; @@ -32,6 +34,8 @@ public static List getAllCurrencies() { currencyList.add(currency.getCurrencyCode()); } + currencyList.add(DbHelper.DEFAULT_ACCOUNT_CURRENCY); + Collections.sort(currencyList); return currencyList; diff --git a/app/src/main/res/layout/activity_report.xml b/app/src/main/res/layout/activity_report.xml index 43b1e7c..b608af0 100644 --- a/app/src/main/res/layout/activity_report.xml +++ b/app/src/main/res/layout/activity_report.xml @@ -6,13 +6,6 @@ android:background="@color/light_grey" android:orientation="vertical"> - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 673a825..4af9cb2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -48,5 +48,8 @@ To Exchange rates Add exchange rate + Can\'t make a report + Next exchange rates are needed: + "]]> diff --git a/app/src/test/java/com/blogspot/e_kanivets/moneytracker/report/ExchangeRateProviderTest.java b/app/src/test/java/com/blogspot/e_kanivets/moneytracker/report/ExchangeRateProviderTest.java new file mode 100644 index 0000000..741a374 --- /dev/null +++ b/app/src/test/java/com/blogspot/e_kanivets/moneytracker/report/ExchangeRateProviderTest.java @@ -0,0 +1,120 @@ +package com.blogspot.e_kanivets.moneytracker.report; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.blogspot.e_kanivets.moneytracker.controller.ExchangeRateController; +import com.blogspot.e_kanivets.moneytracker.model.ExchangeRate; +import com.blogspot.e_kanivets.moneytracker.model.Record; +import com.blogspot.e_kanivets.moneytracker.repo.base.IRepo; +import com.blogspot.e_kanivets.moneytracker.report.base.IExchangeRateProvider; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.*; + +/** + * JUnit4 test case. + * Created on 2/25/16. + * + * @author Evgenii Kanivets + */ +public class ExchangeRateProviderTest { + private ExchangeRateController rateController; + + @Before + public void setUp() throws Exception { + rateController = new ExchangeRateController(new TestRepo()); + } + + @After + public void tearDown() throws Exception { + rateController = null; + } + + @Test + public void testGetRate() throws Exception { + IExchangeRateProvider provider; + + try { + provider = new ExchangeRateProvider(null, rateController); + } catch (NullPointerException e) { + provider = null; + } + + assertNull(provider); + + try { + provider = new ExchangeRateProvider("", null); + } catch (NullPointerException e) { + provider = null; + } + + assertNull(provider); + + try { + provider = new ExchangeRateProvider(null, null); + } catch (NullPointerException e) { + provider = null; + } + + assertNull(provider); + + provider = new ExchangeRateProvider("USD", rateController); + + assertEquals(new ExchangeRate(1, "UAH", "USD", 4), + provider.getRate(new Record(0, 0, "", "", 0, 0, "UAH"))); + + assertEquals(new ExchangeRate(0, "AFN", "USD", 3), + provider.getRate(new Record(0, 0, "", "", 0, 0, "AFN"))); + + assertNull(provider.getRate(new Record(0, 0, "", "", 0, 0, "SMTH"))); + } + + private static class TestRepo implements IRepo { + @Nullable + @Override + public ExchangeRate create(ExchangeRate instance) { + return null; + } + + @Nullable + @Override + public ExchangeRate read(long id) { + return null; + } + + @Nullable + @Override + public ExchangeRate update(ExchangeRate instance) { + return null; + } + + @Override + public boolean delete(ExchangeRate instance) { + return false; + } + + @NonNull + @Override + public List readAll() { + List rateList = new ArrayList<>(); + rateList.add(new ExchangeRate(1, "UAH", "USD", 4)); + rateList.add(new ExchangeRate(0, "UAH", "USD", 2)); + rateList.add(new ExchangeRate(0, "AFN", "USD", 3)); + rateList.add(new ExchangeRate(0, "USD", "EUR", 20)); + return rateList; + } + + @NonNull + @Override + public List readWithCondition(String condition, String[] args) { + return new ArrayList<>(); + } + } +} \ No newline at end of file diff --git a/app/src/test/java/com/blogspot/e_kanivets/moneytracker/report/ReportTest.java b/app/src/test/java/com/blogspot/e_kanivets/moneytracker/report/ReportTest.java new file mode 100644 index 0000000..b571ac6 --- /dev/null +++ b/app/src/test/java/com/blogspot/e_kanivets/moneytracker/report/ReportTest.java @@ -0,0 +1,242 @@ +package com.blogspot.e_kanivets.moneytracker.report; + +import android.support.annotation.Nullable; + +import com.blogspot.e_kanivets.moneytracker.model.ExchangeRate; +import com.blogspot.e_kanivets.moneytracker.model.Period; +import com.blogspot.e_kanivets.moneytracker.model.Record; +import com.blogspot.e_kanivets.moneytracker.report.base.IExchangeRateProvider; +import com.blogspot.e_kanivets.moneytracker.report.base.IReport; +import com.blogspot.e_kanivets.moneytracker.report.model.CategoryRecord; +import com.blogspot.e_kanivets.moneytracker.report.model.SummaryRecord; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import static org.junit.Assert.*; + +/** + * JUnit4 test case. + * Created on 2/25/16. + * + * @author Evgenii Kanivets + */ +public class ReportTest { + private String currency; + private IExchangeRateProvider rateProvider; + + @Before + public void setUp() throws Exception { + currency = "UAH"; + rateProvider = new TestProvider(); + } + + @After + public void tearDown() throws Exception { + currency = null; + rateProvider = null; + } + + @Test + public void testForNulls() throws Exception { + IReport report; + + Period period = new Period(new Date(1), new Date()); + List recordList = new ArrayList<>(); + + try { + report = new Report(null, period, recordList, rateProvider); + } catch (NullPointerException e) { + report = null; + } + + assertNull(report); + + try { + report = new Report(currency, null, recordList, rateProvider); + } catch (NullPointerException e) { + report = null; + } + + assertNull(report); + + try { + report = new Report(currency, period, null, rateProvider); + } catch (NullPointerException e) { + report = null; + } + + assertNull(report); + + try { + report = new Report(currency, period, recordList, null); + } catch (NullPointerException e) { + report = null; + } + + assertNull(report); + + try { + report = new Report(null, null, null, null); + } catch (NullPointerException e) { + report = null; + } + + assertNull(report); + + try { + report = new Report(currency, period, recordList, rateProvider); + } catch (NullPointerException e) { + report = null; + } + + assertNotNull(report); + } + + @Test + public void testGetCurrency() throws Exception { + Period period = new Period(new Date(1), new Date()); + List recordList = new ArrayList<>(); + + IReport report = new Report(currency, period, recordList, rateProvider); + + assertEquals(currency, report.getCurrency()); + + currency = "KHI"; + report = new Report(currency, period, recordList, rateProvider); + + assertEquals(currency, report.getCurrency()); + } + + @Test + public void testGetPeriod() throws Exception { + Period period = new Period(new Date(1), new Date()); + List recordList = new ArrayList<>(); + + IReport report = new Report(currency, period, recordList, rateProvider); + + assertEquals(period, report.getPeriod()); + + period = new Period(new Date(3), new Date(100)); + report = new Report(currency, period, recordList, rateProvider); + + assertEquals(period, report.getPeriod()); + } + + @Test + public void testGetTotal() throws Exception { + Period period = new Period(new Date(1), new Date()); + + List recordList = new ArrayList<>(); + recordList.add(new Record(0, Record.TYPE_INCOME, "1", "1", 10, 1, "USD")); + recordList.add(new Record(1, Record.TYPE_EXPENSE, "2", "1", 2, 2, "UAH")); + recordList.add(new Record(2, Record.TYPE_INCOME, "3", "1", 5, 1, "UAH")); + recordList.add(new Record(3, Record.TYPE_EXPENSE, "4", "1", 10, 2, "USD")); + + IReport report = new Report(currency, period, recordList, rateProvider); + + double expectedTotal = 10 * 4 - 2 + 5 - 10 * 4; + assertEquals(expectedTotal, report.getTotal(), 0.0000000001); + } + + @Test + public void testGetTotalIncome() throws Exception { + Period period = new Period(new Date(1), new Date()); + + List recordList = new ArrayList<>(); + recordList.add(new Record(0, Record.TYPE_INCOME, "1", "1", 10, 1, "USD")); + recordList.add(new Record(1, Record.TYPE_EXPENSE, "2", "1", 2, 2, "UAH")); + recordList.add(new Record(2, Record.TYPE_INCOME, "3", "1", 5, 1, "UAH")); + recordList.add(new Record(3, Record.TYPE_EXPENSE, "4", "1", 10, 2, "USD")); + + IReport report = new Report(currency, period, recordList, rateProvider); + + double expectedTotal = 10 * 4 + 5; + assertEquals(expectedTotal, report.getTotalIncome(), 0.0000000001); + } + + @Test + public void testGetTotalExpense() throws Exception { + Period period = new Period(new Date(1), new Date()); + + List recordList = new ArrayList<>(); + recordList.add(new Record(0, Record.TYPE_INCOME, "1", "1", 10, 1, "USD")); + recordList.add(new Record(1, Record.TYPE_EXPENSE, "2", "1", 2, 2, "UAH")); + recordList.add(new Record(2, Record.TYPE_INCOME, "3", "1", 5, 1, "UAH")); + recordList.add(new Record(3, Record.TYPE_EXPENSE, "4", "1", 10, 2, "USD")); + + IReport report = new Report(currency, period, recordList, rateProvider); + + double expectedTotal = -2 - 10 * 4; + assertEquals(expectedTotal, report.getTotalExpense(), 0.0000000001); + } + + @Test + public void testGetSummary() throws Exception { + Period period = new Period(new Date(1), new Date()); + + List recordList = new ArrayList<>(); + Record record1 = new Record(0, Record.TYPE_INCOME, "1", "1", 10, 1, "USD"); + recordList.add(record1); + Record record2 = new Record(1, Record.TYPE_EXPENSE, "1", "1", 2, 2, "UAH"); + recordList.add(record2); + Record record3 = new Record(2, Record.TYPE_INCOME, "3", "1", 5, 1, "UAH"); + recordList.add(record3); + Record record4 = new Record(3, Record.TYPE_EXPENSE, "4", "1", 10, 2, "USD"); + recordList.add(record4); + + IReport report = new Report(currency, period, recordList, rateProvider); + + List categoryRecordList = new ArrayList<>(); + + CategoryRecord categoryRecord = new CategoryRecord("1", currency, 10 * 4 - 2 + 5 - 10 * 4); + + SummaryRecord summaryRecord1 = new SummaryRecord("1", currency, 38); + Record convertedRecord1 = new Record(record1); + convertedRecord1.setPrice(40); + summaryRecord1.add(convertedRecord1); + summaryRecord1.add(record2); + categoryRecord.add(summaryRecord1); + + SummaryRecord summaryRecord2 = new SummaryRecord("3", currency, 5); + summaryRecord2.add(record3); + categoryRecord.add(summaryRecord2); + + SummaryRecord summaryRecord3 = new SummaryRecord("4", currency, -40); + Record convertedRecord4 = new Record(record4); + convertedRecord4.setPrice(40); + summaryRecord3.add(convertedRecord4); + categoryRecord.add(summaryRecord3); + + categoryRecordList.add(categoryRecord); + + assertEquals(categoryRecordList, report.getSummary()); + } + + private static class TestProvider implements IExchangeRateProvider { + + @Nullable + @Override + public ExchangeRate getRate(@Nullable Record record) { + if (record == null) return null; + + String fromCurrency = record.getCurrency(); + switch (fromCurrency) { + case "USD": + return new ExchangeRate(1, "USD", "UAH", 4); + + case "AFN": + return new ExchangeRate(0, "AFN", "UAH", 3); + + default: + return null; + + } + } + } +} \ No newline at end of file diff --git a/build.gradle b/build.gradle index 8d36b58..75bc9f6 100644 --- a/build.gradle +++ b/build.gradle @@ -3,7 +3,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:1.3.0' + classpath 'com.android.tools.build:gradle:1.5.0' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' } }