diff --git a/app/build.gradle b/app/build.gradle index 293cfbb..1eac5f3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -18,15 +18,15 @@ repositories { } android { - compileSdkVersion 27 + compileSdkVersion 29 buildToolsVersion '27.0.3' defaultConfig { applicationId 'com.blogspot.e_kanivets.moneytracker' - minSdkVersion 17 - targetSdkVersion 26 + minSdkVersion 21 + targetSdkVersion 29 - versionCode 31 - versionName '2.0.1' + versionCode 32 + versionName '2.1.0' } signingConfigs { releaseConfig { @@ -99,4 +99,5 @@ dependencies { annotationProcessor 'com.google.dagger:dagger-compiler:2.11' provided 'org.glassfish:javax.annotation:10.0-b28' compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" + implementation 'com.android.support:cardview-v7:27.1.1' } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 0b50798..26d811e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,9 +2,6 @@ - - - + android:windowSoftInputMode="adjustResize" /> - - + + + + - - + + \ No newline at end of file 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 deleted file mode 100644 index 667806e..0000000 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/ReportActivity.java +++ /dev/null @@ -1,121 +0,0 @@ -package com.blogspot.e_kanivets.moneytracker.activity; - -import android.support.v7.widget.AppCompatSpinner; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.ExpandableListView; - -import com.blogspot.e_kanivets.moneytracker.R; -import com.blogspot.e_kanivets.moneytracker.activity.base.BaseBackActivity; -import com.blogspot.e_kanivets.moneytracker.adapter.ExpandableListReportAdapter; -import com.blogspot.e_kanivets.moneytracker.controller.CurrencyController; -import com.blogspot.e_kanivets.moneytracker.controller.data.ExchangeRateController; -import com.blogspot.e_kanivets.moneytracker.controller.data.RecordController; -import com.blogspot.e_kanivets.moneytracker.entity.Period; -import com.blogspot.e_kanivets.moneytracker.entity.data.Record; -import com.blogspot.e_kanivets.moneytracker.report.record.RecordReportConverter; -import com.blogspot.e_kanivets.moneytracker.report.ReportMaker; -import com.blogspot.e_kanivets.moneytracker.report.record.IRecordReport; -import com.blogspot.e_kanivets.moneytracker.ui.presenter.ShortSummaryPresenter; - -import java.util.List; - -import javax.inject.Inject; - -import butterknife.BindView; - -public class ReportActivity extends BaseBackActivity { - @SuppressWarnings("unused") - private static final String TAG = "ReportActivity"; - - public static final String KEY_PERIOD = "key_period"; - - @Inject - RecordController recordController; - @Inject - ExchangeRateController rateController; - @Inject - CurrencyController currencyController; - - private List recordList; - private Period period; - - private ShortSummaryPresenter shortSummaryPresenter; - - @BindView(R.id.spinner_currency) - AppCompatSpinner spinnerCurrency; - @BindView(R.id.exp_list_view) - ExpandableListView expandableListView; - - @Override - protected int getContentViewId() { - return R.layout.activity_report; - } - - @SuppressWarnings("RedundantIfStatement") - @Override - protected boolean initData() { - super.initData(); - getAppComponent().inject(ReportActivity.this); - - period = getIntent().getParcelableExtra(KEY_PERIOD); - if (period == null) return false; - - recordList = recordController.getRecordsForPeriod(period); - - return true; - } - - @Override - protected void initViews() { - super.initViews(); - - initSpinnerCurrency(); - - shortSummaryPresenter = new ShortSummaryPresenter(ReportActivity.this); - expandableListView.addHeaderView(shortSummaryPresenter.create(false, null)); - } - - private void update(String currency) { - ReportMaker reportMaker = new ReportMaker(rateController); - IRecordReport report = reportMaker.getRecordReport(currency, period, recordList); - - ExpandableListReportAdapter adapter = null; - - if (report != null) { - RecordReportConverter recordReportConverter = new RecordReportConverter(report); - adapter = new ExpandableListReportAdapter(ReportActivity.this, recordReportConverter); - } - - expandableListView.setAdapter(adapter); - shortSummaryPresenter.update(report, currency, reportMaker.currencyNeeded(currency, recordList)); - } - - private void initSpinnerCurrency() { - List currencyList = currencyController.readAll(); - - spinnerCurrency.setAdapter(new ArrayAdapter<>(ReportActivity.this, - R.layout.view_spinner_item, currencyList)); - spinnerCurrency.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView parent, View view, int position, long id) { - update((String) spinnerCurrency.getSelectedItem()); - } - - @Override - public void onNothingSelected(AdapterView parent) { - - } - }); - - String currency = currencyController.readDefaultCurrency(); - - for (int i = 0; i < currencyList.size(); i++) { - if (currency.equals(currencyList.get(i))) { - spinnerCurrency.setSelection(i); - break; - } - } - } -} \ No newline at end of file diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/ReportActivity.kt b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/ReportActivity.kt new file mode 100644 index 0000000..a8cc845 --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/ReportActivity.kt @@ -0,0 +1,136 @@ +package com.blogspot.e_kanivets.moneytracker.activity + +import android.view.View +import android.widget.AdapterView +import android.widget.ArrayAdapter +import com.blogspot.e_kanivets.moneytracker.MtApp +import com.blogspot.e_kanivets.moneytracker.R +import com.blogspot.e_kanivets.moneytracker.activity.base.BaseBackActivity +import com.blogspot.e_kanivets.moneytracker.adapter.RecordReportAdapter +import com.blogspot.e_kanivets.moneytracker.controller.CurrencyController +import com.blogspot.e_kanivets.moneytracker.controller.FormatController +import com.blogspot.e_kanivets.moneytracker.controller.data.ExchangeRateController +import com.blogspot.e_kanivets.moneytracker.controller.data.RecordController +import com.blogspot.e_kanivets.moneytracker.entity.Period +import com.blogspot.e_kanivets.moneytracker.entity.RecordReportItem +import com.blogspot.e_kanivets.moneytracker.entity.data.Record +import com.blogspot.e_kanivets.moneytracker.report.ReportMaker +import com.blogspot.e_kanivets.moneytracker.report.record.IRecordReport +import com.blogspot.e_kanivets.moneytracker.ui.presenter.ShortSummaryPresenter +import kotlinx.android.synthetic.main.activity_report.* +import java.util.* +import javax.inject.Inject + +class ReportActivity : BaseBackActivity() { + + @Inject + lateinit var recordController: RecordController + @Inject + lateinit var rateController: ExchangeRateController + @Inject + lateinit var currencyController: CurrencyController + + private var recordList: List = listOf() + private var period: Period? = null + private lateinit var adapter: RecordReportAdapter + private lateinit var recordReportConverter: RecordReportConverter + + private lateinit var shortSummaryPresenter: ShortSummaryPresenter + + override fun getContentViewId() = R.layout.activity_report + + override fun initData(): Boolean { + super.initData() + appComponent.inject(this) + + period = intent.getParcelableExtra(KEY_PERIOD) + if (period == null) return false + + recordList = recordController.getRecordsForPeriod(period) + shortSummaryPresenter = ShortSummaryPresenter(this) + adapter = RecordReportAdapter(mutableListOf(), hashMapOf(), this) + recordReportConverter = RecordReportConverter() + + return true + } + + override fun initViews() { + super.initViews() + + initSpinnerCurrency() + + adapter.setSummaryView(shortSummaryPresenter.create(false, null)) + recyclerView.adapter = adapter + } + + private fun update(currency: String) { + val reportMaker = ReportMaker(rateController) + val report = reportMaker.getRecordReport(currency, period, recordList) + + adapter.setData(recordReportConverter.getItemsFromReport(report), recordReportConverter.getDataFromReport(report)) + shortSummaryPresenter.update(report, currency, reportMaker.currencyNeeded(currency, recordList)) + } + + private fun initSpinnerCurrency() { + val currencyList = currencyController.readAll() + + spinnerCurrency.adapter = ArrayAdapter(this, R.layout.view_spinner_item, currencyList) + spinnerCurrency.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) = + update(spinnerCurrency.selectedItem.toString()) + + override fun onNothingSelected(p0: AdapterView<*>?) {} + } + + val currency = currencyController.readDefaultCurrency() + + spinnerCurrency.setSelection(currencyList.indexOf(currency)) + } + + class RecordReportConverter { + + @Inject + lateinit var formatController: FormatController + + init { + MtApp.get().appComponent.inject(this) + } + + fun getItemsFromReport(report: IRecordReport?): MutableList { + val items: MutableList = mutableListOf() + + if (report == null) return items + + for (categoryRecord in report.summary) { + val parentRow = RecordReportItem.ParentRow(categoryRecord.title, formatController.formatSignedAmount(categoryRecord.amount), false) + items.add(parentRow) + } + return items + } + + fun getDataFromReport(report: IRecordReport?): HashMap> { + val data: HashMap> = hashMapOf() + + if (report == null) return data + + for (categoryRecord in report.summary) { + val parentRow = RecordReportItem.ParentRow(categoryRecord.title, formatController.formatSignedAmount(categoryRecord.amount), false) + val childRows: MutableList = mutableListOf() + + for (summaryRecord in categoryRecord.summaryRecordList) { + val childRow = RecordReportItem.ChildRow(summaryRecord.title, formatController.formatSignedAmount(summaryRecord.amount)) + childRows.add(childRow) + } + + data[parentRow] = childRows + } + return data + } + + } + + companion object { + + const val KEY_PERIOD = "key_period" + } +} diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/SettingsActivity.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/SettingsActivity.java index eac0415..ee71bfc 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/SettingsActivity.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/SettingsActivity.java @@ -32,7 +32,7 @@ public class SettingsActivity extends BaseBackActivity { super.initViews(); // Display the fragment as the main content. - getFragmentManager().beginTransaction().replace(R.id.content, new SettingsFragment()).commit(); + getFragmentManager().beginTransaction().replace(R.id.contentView, new SettingsFragment()).commit(); } public static class SettingsFragment extends PreferenceFragment { diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/AccountsActivity.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/AccountsActivity.java index bbc3692..aa8c50f 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/AccountsActivity.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/AccountsActivity.java @@ -104,9 +104,6 @@ public void onActivityResult(int requestCode, int resultCode, Intent data) { break; case REQUEST_TRANSFER: - update(); - setResult(RESULT_OK); - break; case REQUEST_EDIT_ACCOUNT: update(); diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/AddAccountActivity.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/AddAccountActivity.java index c61b2b8..706be80 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/AddAccountActivity.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/AddAccountActivity.java @@ -33,9 +33,9 @@ public class AddAccountActivity extends BaseBackActivity { private IValidator accountValidator; - @BindView(R.id.content) + @BindView(R.id.contentView) View contentView; - @BindView(R.id.et_title) + @BindView(R.id.etTitle) EditText etTitle; @BindView(R.id.et_init_sum) EditText etInitSum; diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/TransferActivity.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/TransferActivity.java index f44615b..8ccf29e 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/TransferActivity.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/TransferActivity.java @@ -37,7 +37,7 @@ public class TransferActivity extends BaseBackActivity { private List accountList; - @BindView(R.id.content) + @BindView(R.id.contentView) View contentView; @BindView(R.id.spinner_from) AppCompatSpinner spinnerFrom; diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/edit/fragment/AccountOperationsFragment.kt b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/edit/fragment/AccountOperationsFragment.kt index 93d7726..0de51ed 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/edit/fragment/AccountOperationsFragment.kt +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/edit/fragment/AccountOperationsFragment.kt @@ -5,13 +5,16 @@ import android.view.View import com.blogspot.e_kanivets.moneytracker.R import com.blogspot.e_kanivets.moneytracker.activity.base.BaseFragment import com.blogspot.e_kanivets.moneytracker.adapter.RecordAdapter +import com.blogspot.e_kanivets.moneytracker.controller.FormatController import com.blogspot.e_kanivets.moneytracker.controller.data.AccountController import com.blogspot.e_kanivets.moneytracker.controller.data.RecordController import com.blogspot.e_kanivets.moneytracker.controller.data.TransferController +import com.blogspot.e_kanivets.moneytracker.entity.RecordItem import com.blogspot.e_kanivets.moneytracker.entity.data.Account import com.blogspot.e_kanivets.moneytracker.entity.data.Category import com.blogspot.e_kanivets.moneytracker.entity.data.Record import com.blogspot.e_kanivets.moneytracker.entity.data.Transfer +import com.blogspot.e_kanivets.moneytracker.util.RecordItemsBuilder import kotlinx.android.synthetic.main.fragment_account_operations.* import javax.inject.Inject @@ -23,6 +26,8 @@ class AccountOperationsFragment : BaseFragment() { internal lateinit var recordController: RecordController @Inject internal lateinit var transferController: TransferController + @Inject + internal lateinit var formatController: FormatController private lateinit var account: Account @@ -34,17 +39,17 @@ class AccountOperationsFragment : BaseFragment() { } override fun initViews(view: View) { - recyclerView.adapter = RecordAdapter(requireContext(), getRecords(), false, null) + recyclerView.adapter = RecordAdapter(requireContext(), getRecordItems(), false) } - private fun getRecords(): List { + private fun getRecordItems(): List { val accountRecords = recordController.getRecordsForAccount(account) val accountTransfers = transferController.getTransfersForAccount(account) accountRecords += obtainRecordsFromTransfers(accountTransfers) accountRecords.sortByDescending { it.time } - return accountRecords + return RecordItemsBuilder().getRecordItems(accountRecords) } private fun obtainRecordsFromTransfers(transfers: List): List { diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/exchange_rate/AddExchangeRateActivity.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/exchange_rate/AddExchangeRateActivity.java index 47d3f31..bad88bc 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/exchange_rate/AddExchangeRateActivity.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/exchange_rate/AddExchangeRateActivity.java @@ -44,7 +44,7 @@ public class AddExchangeRateActivity extends BaseBackActivity { @Nullable private ExchangeRatePair exchangeRatePair; - @BindView(R.id.content) + @BindView(R.id.contentView) View contentView; @BindView(R.id.spinner_from_currency) AppCompatSpinner spinnerFromCurrency; diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/record/AddRecordActivity.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/record/AddRecordActivity.java deleted file mode 100644 index 6081301..0000000 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/record/AddRecordActivity.java +++ /dev/null @@ -1,383 +0,0 @@ -package com.blogspot.e_kanivets.moneytracker.activity.record; - -import android.annotation.SuppressLint; -import android.app.DatePickerDialog; -import android.app.TimePickerDialog; -import android.support.annotation.Nullable; -import android.support.v7.widget.AppCompatSpinner; -import android.text.InputFilter; -import android.text.Spanned; -import android.text.format.DateFormat; -import android.view.KeyEvent; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.inputmethod.EditorInfo; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.AutoCompleteTextView; -import android.widget.DatePicker; -import android.widget.EditText; -import android.widget.TextView; -import android.widget.TimePicker; - -import com.blogspot.e_kanivets.moneytracker.R; -import com.blogspot.e_kanivets.moneytracker.activity.base.BaseBackActivity; -import com.blogspot.e_kanivets.moneytracker.adapter.CategoryAutoCompleteAdapter; -import com.blogspot.e_kanivets.moneytracker.controller.FormatController; -import com.blogspot.e_kanivets.moneytracker.controller.PreferenceController; -import com.blogspot.e_kanivets.moneytracker.controller.data.AccountController; -import com.blogspot.e_kanivets.moneytracker.controller.data.CategoryController; -import com.blogspot.e_kanivets.moneytracker.controller.data.RecordController; -import com.blogspot.e_kanivets.moneytracker.entity.data.Account; -import com.blogspot.e_kanivets.moneytracker.entity.data.Category; -import com.blogspot.e_kanivets.moneytracker.entity.data.Record; -import com.blogspot.e_kanivets.moneytracker.ui.AddRecordUiDecorator; -import com.blogspot.e_kanivets.moneytracker.util.CrashlyticsProxy; -import com.blogspot.e_kanivets.moneytracker.util.CategoryAutoCompleter; -import com.blogspot.e_kanivets.moneytracker.util.validator.IValidator; -import com.blogspot.e_kanivets.moneytracker.util.validator.RecordValidator; - -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Date; -import java.util.List; - -import javax.inject.Inject; - -import butterknife.BindView; -import butterknife.OnClick; - -/** - * Created on 1/26/16. - * - * @author Evgenii Kanivets - */ -public class AddRecordActivity extends BaseBackActivity { - @SuppressWarnings("unused") - private static final String TAG = "AddRecordActivity"; - - public static final String KEY_RECORD = "key_record"; - public static final String KEY_MODE = "key_mode"; - public static final String KEY_TYPE = "key_type"; - - @Nullable - private Record record; - private Mode mode; - private int type; - - private List accountList; - private long timestamp; - - @Inject - CategoryController categoryController; - @Inject - RecordController recordController; - @Inject - AccountController accountController; - @Inject - FormatController formatController; - @Inject - PreferenceController preferenceController; - - private IValidator recordValidator; - private AddRecordUiDecorator uiDecorator; - private CategoryAutoCompleter autoCompleter; - - @BindView(R.id.content) - View contentView; - @BindView(R.id.tv_date) - TextView tvDate; - @BindView(R.id.tv_time) - TextView tvTime; - @BindView(R.id.et_title) - EditText etTitle; - @BindView(R.id.et_category) - AutoCompleteTextView etCategory; - @BindView(R.id.et_price) - EditText etPrice; - @BindView(R.id.spinner_account) - AppCompatSpinner spinnerAccount; - - @Override - protected int getContentViewId() { - return R.layout.activity_add_record; - } - - @Override - protected boolean initData() { - super.initData(); - getAppComponent().inject(AddRecordActivity.this); - - uiDecorator = new AddRecordUiDecorator(AddRecordActivity.this); - - record = getIntent().getParcelableExtra(KEY_RECORD); - mode = (Mode) getIntent().getSerializableExtra(KEY_MODE); - type = getIntent().getIntExtra(KEY_TYPE, -1); - accountList = accountController.readActiveAccounts(); - - if (mode == Mode.MODE_EDIT && record != null) timestamp = record.getTime(); - else timestamp = new Date().getTime(); - - return mode != null && (type == Record.TYPE_INCOME || type == Record.TYPE_EXPENSE) - && (!mode.equals(Mode.MODE_EDIT) || record != null); - } - - @SuppressWarnings("deprecation") - @SuppressLint("SetTextI18n") - @Override - protected void initViews() { - super.initViews(); - - recordValidator = new RecordValidator(AddRecordActivity.this, contentView); - autoCompleter = new CategoryAutoCompleter(categoryController, preferenceController); - - // Add texts to dialog if it's edit dialog - if (mode == Mode.MODE_EDIT) { - etTitle.setText(record.getTitle()); - if (record.getCategory() != null) etCategory.setText(record.getCategory().getName()); - etPrice.setText(formatController.formatPrecisionNone(record.getFullPrice())); - } - - uiDecorator.decorateActionBar(getSupportActionBar(), mode, type); - presentSpinnerAccount(); - - - final CategoryAutoCompleteAdapter categoryAutoCompleteAdapter = new CategoryAutoCompleteAdapter( - AddRecordActivity.this, R.layout.view_category_item, autoCompleter); - etCategory.setAdapter(categoryAutoCompleteAdapter); - etCategory.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView parent, View view, int position, long id) { - etCategory.setText((String) parent.getAdapter().getItem(position)); - etCategory.setSelection(etCategory.getText().length()); - } - }); - etCategory.setOnEditorActionListener(new TextView.OnEditorActionListener() { - @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - if (actionId == EditorInfo.IME_ACTION_DONE) tryRecord(); - return false; - } - }); - etCategory.setOnFocusChangeListener(new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View view, boolean hasFocus) { - if (hasFocus && etCategory.getText().toString().trim().isEmpty()) { - String title = etTitle.getText().toString().trim(); - String prediction = autoCompleter.completeByRecordTitle(title); - if (prediction != null) { - etCategory.setAdapter(null); - etCategory.setText(prediction); - etCategory.selectAll(); - etCategory.setAdapter(categoryAutoCompleteAdapter); - } - } - } - }); - - // Restrict ';' for input, because it's used as delimiter when exporting - etTitle.setFilters(new InputFilter[]{new SemicolonInputFilter()}); - etCategory.setFilters(new InputFilter[]{new SemicolonInputFilter()}); - - updateDateAndTime(); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.menu_add_record, menu); - return true; - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - switch (mode) { - case MODE_ADD: - menu.removeItem(R.id.action_delete); - break; - - default: - break; - } - - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_done: - tryRecord(); - return true; - - case R.id.action_delete: - if (recordController.delete(record)) { - setResult(RESULT_OK); - finish(); - } - return true; - - default: - return super.onOptionsItemSelected(item); - } - } - - @OnClick(R.id.tv_date) - public void selectDate() { - CrashlyticsProxy.get().logButton("Select Date"); - Calendar calendar = Calendar.getInstance(); - calendar.setTimeInMillis(timestamp); - DatePickerDialog dialog = new DatePickerDialog(AddRecordActivity.this, uiDecorator.getTheme(type), - new DatePickerDialog.OnDateSetListener() { - @Override - public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) { - Calendar calendar = Calendar.getInstance(); - calendar.setTimeInMillis(timestamp); - calendar.set(Calendar.YEAR, year); - calendar.set(Calendar.MONTH, monthOfYear); - calendar.set(Calendar.DAY_OF_MONTH, dayOfMonth); - - if (calendar.getTimeInMillis() < new Date().getTime()) { - timestamp = calendar.getTimeInMillis(); - updateDateAndTime(); - } else { - showToast(R.string.record_in_future); - } - } - }, calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), - calendar.get(Calendar.DAY_OF_MONTH)); - dialog.show(); - } - - @OnClick(R.id.tv_time) - public void selectTime() { - CrashlyticsProxy.get().logButton("Show Time"); - Calendar calendar = Calendar.getInstance(); - calendar.setTimeInMillis(timestamp); - TimePickerDialog dialog = new TimePickerDialog(AddRecordActivity.this, uiDecorator.getTheme(type), - new TimePickerDialog.OnTimeSetListener() { - @Override - public void onTimeSet(TimePicker view, int hourOfDay, int minute) { - Calendar calendar = Calendar.getInstance(); - calendar.setTimeInMillis(timestamp); - calendar.set(Calendar.HOUR_OF_DAY, hourOfDay); - calendar.set(Calendar.MINUTE, minute); - - if (calendar.getTimeInMillis() < new Date().getTime()) { - timestamp = calendar.getTimeInMillis(); - updateDateAndTime(); - } else { - showToast(R.string.record_in_future); - } - } - }, calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), - DateFormat.is24HourFormat(AddRecordActivity.this)); - dialog.show(); - } - - private void presentSpinnerAccount() { - List accounts = new ArrayList<>(); - for (Account account : accountList) { - accounts.add(account.getTitle()); - } - - int selectedAccountIndex = -1; - - if (mode == Mode.MODE_EDIT) { - if (record != null && record.getAccount() != null) { - for (int i = 0; i < accountList.size(); i++) { - Account account = accountList.get(i); - if (account.getId() == record.getAccount().getId()) selectedAccountIndex = i; - } - } - } else if (mode == Mode.MODE_ADD) { - Account defaultAccount = accountController.readDefaultAccount(); - if (defaultAccount != null) { - for (int i = 0; i < accountList.size(); i++) { - Account account = accountList.get(i); - if (account.getId() == defaultAccount.getId()) selectedAccountIndex = i; - } - } - } - - if (selectedAccountIndex == -1) { - selectedAccountIndex = 0; - spinnerAccount.setEnabled(false); - - if (accounts.size() == 0) { - accounts.add(getString(R.string.none)); - } else { - accounts.add(getString(R.string.account_was_removed)); - } - } - - spinnerAccount.setAdapter(new ArrayAdapter<>(AddRecordActivity.this, - R.layout.view_spinner_item, accounts)); - spinnerAccount.setSelection(selectedAccountIndex); - } - - private void tryRecord() { - CrashlyticsProxy.get().logButton("Done Record"); - if (addRecord()) { - CrashlyticsProxy.get().logEvent("Done Record"); - setResult(RESULT_OK); - finish(); - } - } - - @SuppressWarnings("SimplifiableIfStatement") - private boolean addRecord() { - if (recordValidator.validate()) { - long now = new Date().getTime(); - if (timestamp > now) { - showToast(R.string.record_in_future); - return false; - } - - String title = etTitle.getText().toString().trim(); - String category = etCategory.getText().toString().trim(); - double price = Double.parseDouble(etPrice.getText().toString()); - Account account = accountList.get(spinnerAccount.getSelectedItemPosition()); - - if (title.isEmpty()) { - title = category; - } - - Record newRecord = null; - - if (mode == Mode.MODE_ADD) { - newRecord = recordController.create(new Record(timestamp, type, title, - new Category(category), price, account, account.getCurrency())); - } else if (mode == Mode.MODE_EDIT) { - long recordId = record == null ? -1 : record.getId(); - newRecord = recordController.update(new Record(recordId, timestamp, type, title, - new Category(category), price, account, account.getCurrency())); - } - - if (newRecord == null) { - return false; - } else { - autoCompleter.addRecordTitleCategoryPair(title, category); - return true; - } - } else { - return false; - } - } - - private void updateDateAndTime() { - tvDate.setText(formatController.formatDate(timestamp)); - tvTime.setText(formatController.formatTime(timestamp)); - } - - public enum Mode {MODE_ADD, MODE_EDIT} - - private static class SemicolonInputFilter implements InputFilter { - - @Override - public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { - if (source != null && ";".equals(source.toString())) return ""; - else return null; - } - } -} diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/record/AddRecordActivity.kt b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/record/AddRecordActivity.kt new file mode 100644 index 0000000..39b87f4 --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/record/AddRecordActivity.kt @@ -0,0 +1,306 @@ +package com.blogspot.e_kanivets.moneytracker.activity.record + +import android.annotation.SuppressLint +import android.app.Activity +import android.app.DatePickerDialog +import android.app.TimePickerDialog +import android.content.res.ColorStateList +import android.support.v4.content.ContextCompat +import android.text.InputFilter +import android.text.Spanned +import android.text.format.DateFormat +import android.view.Menu +import android.view.MenuItem +import android.view.View +import android.view.inputmethod.EditorInfo +import android.widget.AdapterView +import android.widget.ArrayAdapter +import com.blogspot.e_kanivets.moneytracker.R +import com.blogspot.e_kanivets.moneytracker.activity.base.BaseBackActivity +import com.blogspot.e_kanivets.moneytracker.adapter.CategoryAutoCompleteAdapter +import com.blogspot.e_kanivets.moneytracker.controller.FormatController +import com.blogspot.e_kanivets.moneytracker.controller.PreferenceController +import com.blogspot.e_kanivets.moneytracker.controller.data.AccountController +import com.blogspot.e_kanivets.moneytracker.controller.data.CategoryController +import com.blogspot.e_kanivets.moneytracker.controller.data.RecordController +import com.blogspot.e_kanivets.moneytracker.entity.data.Account +import com.blogspot.e_kanivets.moneytracker.entity.data.Category +import com.blogspot.e_kanivets.moneytracker.entity.data.Record +import com.blogspot.e_kanivets.moneytracker.ui.AddRecordUiDecorator +import com.blogspot.e_kanivets.moneytracker.util.CategoryAutoCompleter +import com.blogspot.e_kanivets.moneytracker.util.CrashlyticsProxy +import com.blogspot.e_kanivets.moneytracker.util.validator.IValidator +import com.blogspot.e_kanivets.moneytracker.util.validator.RecordValidator +import kotlinx.android.synthetic.main.activity_add_record.* +import kotlinx.android.synthetic.main.content_add_record.* +import java.util.* +import javax.inject.Inject + +class AddRecordActivity : BaseBackActivity() { + + private var record: Record? = null + private var mode: Mode? = null + private var type: Int = 0 + + private var accountList: List = listOf() + private var timestamp: Long = 0 + + @Inject + lateinit var categoryController: CategoryController + @Inject + lateinit var recordController: RecordController + @Inject + lateinit var accountController: AccountController + @Inject + lateinit var formatController: FormatController + @Inject + lateinit var preferenceController: PreferenceController + + private lateinit var recordValidator: IValidator + private lateinit var uiDecorator: AddRecordUiDecorator + private lateinit var autoCompleter: CategoryAutoCompleter + + override fun getContentViewId() = R.layout.activity_add_record + + + override fun initData(): Boolean { + super.initData() + appComponent.inject(this) + + record = intent.getParcelableExtra(KEY_RECORD) + mode = intent.getSerializableExtra(KEY_MODE) as Mode + type = intent.getIntExtra(KEY_TYPE, -1) + accountList = accountController.readActiveAccounts() + + timestamp = record?.time ?: Date().time + + return (mode != null && (type == Record.TYPE_INCOME || type == Record.TYPE_EXPENSE) + && ((mode == Mode.MODE_EDIT && record != null) || (mode == Mode.MODE_ADD && record == null))) + } + + @SuppressLint("SetTextI18n") + override fun initViews() { + super.initViews() + + recordValidator = RecordValidator(this, contentView) + autoCompleter = CategoryAutoCompleter(categoryController, preferenceController) + uiDecorator = AddRecordUiDecorator(this) + + uiDecorator.decorateActionBar(supportActionBar, mode, type) + + if (mode == Mode.MODE_EDIT) { + record?.let { record -> + etTitle.setText(record.title) + etCategory.setText(record.category?.name.orEmpty()) + etPrice.setText(formatController.formatPrecisionNone(record.fullPrice)) + } + } + + initCategoryAutocomplete() + presentSpinnerAccount() + + // Restrict ';' for input, because it's used as delimiter when exporting + etTitle.filters = arrayOf(SemicolonInputFilter()) + etCategory.filters = arrayOf(SemicolonInputFilter()) + + tvDate.setOnClickListener { selectDate() } + tvTime.setOnClickListener { selectTime() } + + if (type == Record.TYPE_EXPENSE) { + fabDone.backgroundTintList = (getColorForFab(R.color.red_light)) + } else { + fabDone.backgroundTintList = (getColorForFab(R.color.green_light)) + } + + fabDone.setOnClickListener { tryRecord() } + + updateDateAndTime() + } + + private fun getColorForFab(color: Int): ColorStateList { + return ColorStateList.valueOf(ContextCompat.getColor(this, color)) + } + + private fun initCategoryAutocomplete() { + val categoryAutoCompleteAdapter = CategoryAutoCompleteAdapter( + this, R.layout.view_category_item, autoCompleter) + etCategory.setAdapter(categoryAutoCompleteAdapter) + etCategory.onItemClickListener = AdapterView.OnItemClickListener { parent, view, position, id -> + etCategory.setText(parent.adapter.getItem(position) as String) + etCategory.setSelection(etCategory.text.length) + } + etCategory.setOnEditorActionListener { v, actionId, event -> + if (actionId == EditorInfo.IME_ACTION_DONE) tryRecord() + false + } + etCategory.onFocusChangeListener = View.OnFocusChangeListener { view, hasFocus -> + if (hasFocus && etCategory.text.toString().trim().isEmpty()) { + val title = etTitle.text.toString().trim() + autoCompleter.completeByRecordTitle(title)?.let { prediction -> + etCategory.setText(prediction) + etCategory.selectAll() + } + } + } + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.menu_add_record, menu) + return true + } + + override fun onPrepareOptionsMenu(menu: Menu): Boolean { + when (mode) { + Mode.MODE_ADD -> menu.removeItem(R.id.action_delete) + else -> { + } + } + + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.action_delete -> { + if (recordController.delete(record)) { + setResult(Activity.RESULT_OK) + finish() + } + true + } + + else -> super.onOptionsItemSelected(item) + } + } + + private fun selectDate() { + CrashlyticsProxy.get().logButton("Select Date") + val calendar = Calendar.getInstance() + calendar.timeInMillis = timestamp + val dialog = DatePickerDialog(this, uiDecorator.getTheme(type), + DatePickerDialog.OnDateSetListener { view, year, monthOfYear, dayOfMonth -> + val newCalendar = Calendar.getInstance() + newCalendar.timeInMillis = timestamp + newCalendar.set(Calendar.YEAR, year) + newCalendar.set(Calendar.MONTH, monthOfYear) + newCalendar.set(Calendar.DAY_OF_MONTH, dayOfMonth) + + if (newCalendar.timeInMillis < Date().time) { + timestamp = newCalendar.timeInMillis + updateDateAndTime() + } else { + showToast(R.string.record_in_future) + } + }, calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH), + calendar.get(Calendar.DAY_OF_MONTH)) + dialog.show() + } + + private fun selectTime() { + CrashlyticsProxy.get().logButton("Show Time") + val calendar = Calendar.getInstance() + calendar.timeInMillis = timestamp + val dialog = TimePickerDialog(this, uiDecorator.getTheme(type), + TimePickerDialog.OnTimeSetListener { view, hourOfDay, minute -> + val newCalendar = Calendar.getInstance() + newCalendar.timeInMillis = timestamp + newCalendar.set(Calendar.HOUR_OF_DAY, hourOfDay) + newCalendar.set(Calendar.MINUTE, minute) + + if (newCalendar.timeInMillis < Date().time) { + timestamp = newCalendar.timeInMillis + updateDateAndTime() + } else { + showToast(R.string.record_in_future) + } + }, calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), + DateFormat.is24HourFormat(this)) + dialog.show() + } + + private fun presentSpinnerAccount() { + val accounts = accountList.map { it.title }.toMutableList() + + var selectedAccountIndex = -1 + + if (mode == Mode.MODE_EDIT) { + if (record?.account != null) { + selectedAccountIndex = accountList.indexOf(accountList.find { it.id == record?.account?.id }) + } + } else if (mode == Mode.MODE_ADD) { + val defaultAccount = accountController.readDefaultAccount() + selectedAccountIndex = accountList.indexOf(accountList.find { it.id == defaultAccount?.id }) + } + + if (selectedAccountIndex == -1) { + spinnerAccount.isEnabled = false + + accounts.clear() + accounts.add(getString(R.string.account_removed)) + } + + spinnerAccount.adapter = ArrayAdapter(this, R.layout.view_spinner_item, accounts) + spinnerAccount.setSelection(selectedAccountIndex) + } + + private fun tryRecord() { + CrashlyticsProxy.get().logButton("Done Record") + if (addRecord()) { + CrashlyticsProxy.get().logEvent("Done Record") + setResult(Activity.RESULT_OK) + finish() + } + } + + private fun addRecord(): Boolean { + if (recordValidator.validate()) { + val now = Date().time + if (timestamp > now) { + showToast(R.string.record_in_future) + return false + } + + var title = etTitle.text.toString().trim() + val category = etCategory.text.toString().trim() + val price = etPrice.text.toString().toDouble() + val account = accountList[spinnerAccount.selectedItemPosition] + + if (title.isEmpty()) { + title = category + } + + if (mode == Mode.MODE_ADD) { + recordController.create(Record(timestamp, type, title, + Category(category), price, account, account.currency)) + } else if (mode == Mode.MODE_EDIT) { + recordController.update(Record(record?.id ?: -1, + timestamp, type, title, Category(category), price, account, account.currency)) + } + + autoCompleter.addRecordTitleCategoryPair(title, category) + return true + } else { + return false + } + } + + private fun updateDateAndTime() { + tvDate.text = formatController.formatDateToNumber(timestamp) + tvTime.text = formatController.formatTime(timestamp) + } + + enum class Mode { MODE_ADD, MODE_EDIT } + + private class SemicolonInputFilter : InputFilter { + + override fun filter(source: CharSequence?, start: Int, end: Int, dest: Spanned, dstart: Int, dend: Int): CharSequence? { + return if (source != null && ";" == source.toString()) "" else null + } + } + + companion object { + const val KEY_RECORD = "key_record" + const val KEY_MODE = "key_mode" + const val KEY_TYPE = "key_type" + } +} diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/record/MainActivity.kt b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/record/MainActivity.kt index fa96f1b..242dbd2 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/record/MainActivity.kt +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/record/MainActivity.kt @@ -2,6 +2,7 @@ package com.blogspot.e_kanivets.moneytracker.activity.record import android.content.Intent import android.support.v7.app.AppCompatActivity +import android.support.v7.widget.RecyclerView import android.widget.TextView import com.blogspot.e_kanivets.moneytracker.R import com.blogspot.e_kanivets.moneytracker.activity.ReportActivity @@ -15,10 +16,13 @@ import com.blogspot.e_kanivets.moneytracker.controller.data.AccountController import com.blogspot.e_kanivets.moneytracker.controller.data.ExchangeRateController import com.blogspot.e_kanivets.moneytracker.controller.data.RecordController import com.blogspot.e_kanivets.moneytracker.entity.Period +import com.blogspot.e_kanivets.moneytracker.entity.RecordItem import com.blogspot.e_kanivets.moneytracker.entity.data.Record import com.blogspot.e_kanivets.moneytracker.report.ReportMaker import com.blogspot.e_kanivets.moneytracker.ui.AppRateDialog +import com.blogspot.e_kanivets.moneytracker.ui.presenter.ShortSummaryPresenter import com.blogspot.e_kanivets.moneytracker.util.CrashlyticsProxy +import com.blogspot.e_kanivets.moneytracker.util.RecordItemsBuilder import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.content_main.* import javax.inject.Inject @@ -26,6 +30,7 @@ import javax.inject.Inject class MainActivity : BaseDrawerActivity() { private lateinit var recordList: List + private var recordItems: List = listOf() private lateinit var period: Period private lateinit var recordAdapter: RecordAdapter @@ -47,6 +52,7 @@ class MainActivity : BaseDrawerActivity() { private lateinit var tvDefaultAccountTitle: TextView private lateinit var tvDefaultAccountSum: TextView private lateinit var tvCurrency: TextView + private lateinit var summaryPresenter: ShortSummaryPresenter override fun getContentViewId(): Int = R.layout.activity_main @@ -70,10 +76,13 @@ class MainActivity : BaseDrawerActivity() { tvDefaultAccountSum = navigationView.getHeaderView(0).findViewById(R.id.tvDefaultAccountSum) tvCurrency = navigationView.getHeaderView(0).findViewById(R.id.tvCurrency) - recordAdapter = RecordAdapter(this, listOf(), true) { position -> - if (position == 0) showReport() - else editRecord(position) - } + recordAdapter = RecordAdapter(this, listOf(), true) + recordAdapter.itemClickListener = { position -> editRecord(getPositionWithoutSummary(position)) } + + summaryPresenter = ShortSummaryPresenter(this) + val summaryViewHolder = summaryPresenter.create(true) { showReport() }.tag as RecyclerView.ViewHolder + recordAdapter.summaryViewHolder = summaryViewHolder + recyclerView.adapter = recordAdapter spinner.setPeriodSelectedListener { period -> @@ -88,11 +97,13 @@ class MainActivity : BaseDrawerActivity() { btnAddIncome.setOnClickListener { addIncome() } } + private fun getPositionWithoutSummary(position: Int) = position - 1 + private fun editRecord(position: Int) { + CrashlyticsProxy.get().logButton("Edit Record") + val record = recordList[getRecordPosition(position)] - // Minus one because of list view's header view - val record = recordList[position - 1] startAddRecordActivity(record, AddRecordActivity.Mode.MODE_EDIT, record.type) } @@ -134,17 +145,30 @@ class MainActivity : BaseDrawerActivity() { override fun update() { recordList = recordController.getRecordsForPeriod(period) recordList = recordList.reversed() + recordItems = RecordItemsBuilder().getRecordItems(recordList) val currency = currencyController.readDefaultCurrency() val reportMaker = ReportMaker(rateController) val report = reportMaker.getRecordReport(currency, period, recordList) - recordAdapter.setRecords(recordList, report, currency, reportMaker.currencyNeeded(currency, recordList)) + summaryPresenter.update(report, currency, reportMaker.currencyNeeded(currency, recordList)) + recordAdapter.setRecords(recordItems) fillDefaultAccount() } + private fun getRecordPosition(position: Int): Int { + var recordPosition = 0 + + for (indexOfItem in 0 until position) { + if (recordItems[indexOfItem] is RecordItem.Record) { + recordPosition++ + } + } + return recordPosition + } + private fun showAppRateDialog() { CrashlyticsProxy.get().logEvent("Show App Rate Dialog") val dialog = AppRateDialog(this) diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/CategoryAutoCompleteAdapter.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/CategoryAutoCompleteAdapter.java index a7d0673..8f47160 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/CategoryAutoCompleteAdapter.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/CategoryAutoCompleteAdapter.java @@ -12,6 +12,8 @@ import com.blogspot.e_kanivets.moneytracker.R; import com.blogspot.e_kanivets.moneytracker.util.CategoryAutoCompleter; +import org.jetbrains.annotations.NotNull; + import java.util.ArrayList; import java.util.List; @@ -26,7 +28,6 @@ */ public class CategoryAutoCompleteAdapter extends ArrayAdapter implements Filterable { private final CategoryAutoCompleter autoCompleter; - private List resultList; public CategoryAutoCompleteAdapter(Context context, int resource, CategoryAutoCompleter autoCompleter) { super(context, resource); @@ -34,7 +35,7 @@ public CategoryAutoCompleteAdapter(Context context, int resource, CategoryAutoCo } @Override - public View getView(int position, View convertView, ViewGroup parent) { + public View getView(int position, View convertView, @NotNull ViewGroup parent) { ViewHolder viewHolder; if (convertView == null) { @@ -43,14 +44,14 @@ public View getView(int position, View convertView, ViewGroup parent) { convertView.setTag(viewHolder); } else viewHolder = (ViewHolder) convertView.getTag(); - final String category = resultList.get(position); + final String category = getItem(position); viewHolder.tvCategory.setText(category); viewHolder.ivCancel.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { autoCompleter.removeFromAutoComplete(category); - resultList.remove(category); + remove(category); notifyDataSetChanged(); } }); @@ -58,16 +59,7 @@ public void onClick(View v) { return convertView; } - @Override - public int getCount() { - return resultList.size(); - } - - @Override - public String getItem(int index) { - return resultList.get(index); - } - + @NotNull @Override public Filter getFilter() { return new Filter() { @@ -77,7 +69,8 @@ protected FilterResults performFiltering(CharSequence constraint) { List tempList; - if (constraint != null) tempList = autoCompleter.completeByPart(constraint.toString()); + if (constraint != null) + tempList = autoCompleter.completeByPart(constraint.toString()); else tempList = new ArrayList<>(); filterResults.values = tempList; @@ -88,11 +81,14 @@ protected FilterResults performFiltering(CharSequence constraint) { @Override protected void publishResults(CharSequence constraint, FilterResults results) { - if (results != null) //noinspection unchecked - resultList = (List) results.values; - - if (results != null && results.count > 0) notifyDataSetChanged(); - else notifyDataSetInvalidated(); + clear(); + + if (results != null && results.count > 0) { + addAll((List) results.values); + notifyDataSetChanged(); + } else { + notifyDataSetInvalidated(); + } } }; } 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 deleted file mode 100644 index be2caaf..0000000 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/ExpandableListReportAdapter.java +++ /dev/null @@ -1,107 +0,0 @@ -package com.blogspot.e_kanivets.moneytracker.adapter; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.view.View; -import android.view.ViewGroup; -import android.widget.SimpleExpandableListAdapter; -import android.widget.TextView; - -import com.blogspot.e_kanivets.moneytracker.MtApp; -import com.blogspot.e_kanivets.moneytracker.R; -import com.blogspot.e_kanivets.moneytracker.controller.FormatController; -import com.blogspot.e_kanivets.moneytracker.report.record.RecordReportConverter; - -import java.util.List; -import java.util.Map; - -import javax.inject.Inject; - -import butterknife.BindView; -import butterknife.ButterKnife; - -/** - * Created on 12/29/14. - * - * @author Evgenii Kanivets - */ -public class ExpandableListReportAdapter extends SimpleExpandableListAdapter { - @Inject - FormatController formatController; - - private List> groupData; - private List>> childData; - - private int whiteRed; - private int whiteGreen; - private int white; - private int red; - private int green; - - public ExpandableListReportAdapter(Context context, RecordReportConverter 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, - List>> childData, - int childLayout, String[] childFrom, int[] childTo) { - super(context, groupData, groupLayout, groupFrom, groupTo, childData, childLayout, childFrom, childTo); - MtApp.get().getAppComponent().inject(ExpandableListReportAdapter.this); - - this.groupData = groupData; - this.childData = childData; - - whiteRed = context.getResources().getColor(R.color.white_red); - whiteGreen = context.getResources().getColor(R.color.white_green); - white = context.getResources().getColor(R.color.white); - red = context.getResources().getColor(R.color.red); - green = context.getResources().getColor(R.color.green); - } - - @Override - public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { - View view = super.getGroupView(groupPosition, isExpanded, convertView, parent); - customizeView(view, groupData.get(groupPosition), true); - return view; - } - - @Override - public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { - View view = super.getChildView(groupPosition, childPosition, isLastChild, convertView, parent); - customizeView(view, childData.get(groupPosition).get(childPosition), false); - return view; - } - - @SuppressLint("SetTextI18n") - private void customizeView(View view, Map values, boolean groupView) { - ViewHolder viewHolder = (ViewHolder) view.getTag(); - if (viewHolder == null) viewHolder = new ViewHolder(view); - - /* Customize view to fit to model and UI */ - Double price = Double.parseDouble(values.get(RecordReportConverter.PRICE_PARAM_NAME)); - - if (groupView) view.setBackgroundColor(price < 0 ? whiteRed : whiteGreen); - else view.setBackgroundColor(white); - - //Set color of total - viewHolder.tvTotal.setTextColor(price >= 0 ? green : red); - - viewHolder.tvCategory.setText(values.get(RecordReportConverter.TITLE_PARAM_NAME)); - viewHolder.tvTotal.setText(formatController.formatSignedAmount(price)); - } - - public static class ViewHolder { - @BindView(R.id.tvCategory) - TextView tvCategory; - @BindView(R.id.tvTotal) - 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/adapter/RecordAdapter.kt b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/RecordAdapter.kt index f07066d..86781ae 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/RecordAdapter.kt +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/RecordAdapter.kt @@ -6,16 +6,13 @@ import android.support.v7.widget.RecyclerView import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.LinearLayout import android.widget.TextView import com.blogspot.e_kanivets.moneytracker.MtApp import com.blogspot.e_kanivets.moneytracker.R import com.blogspot.e_kanivets.moneytracker.controller.FormatController -import com.blogspot.e_kanivets.moneytracker.entity.data.Record -import com.blogspot.e_kanivets.moneytracker.report.record.IRecordReport -import com.blogspot.e_kanivets.moneytracker.ui.presenter.ShortSummaryPresenter +import com.blogspot.e_kanivets.moneytracker.entity.RecordItem +import kotlinx.android.synthetic.main.view_header_date.view.* import kotlinx.android.synthetic.main.view_record.view.* -import kotlinx.android.synthetic.main.view_summary_records.view.* import javax.inject.Inject class RecordAdapter : RecyclerView.Adapter { @@ -23,23 +20,22 @@ class RecordAdapter : RecyclerView.Adapter { @Inject lateinit var formatController: FormatController - private var itemClickListener: ((Int) -> Unit)? = null + var itemClickListener: ((Int) -> Unit)? = null private var whiteRed: Int private var whiteGreen: Int private var red: Int private var green: Int - private var records: List + private var items: List private var context: Context - private var summaryPresenter: ShortSummaryPresenter - private var isHeaderViewNeeded: Boolean = false - private var headerViewHolder: HeaderViewHolder + private var isSummaryViewNeeded: Boolean = false + lateinit var summaryViewHolder: RecyclerView.ViewHolder - constructor(context: Context, records: List, isHeaderViewNeeded: Boolean, itemClickListener: ((Int) -> Unit)?) { + constructor(context: Context, items: List, isSummaryViewNeeded: Boolean) { this.context = context - this.records = records + this.items = items MtApp.get().appComponent.inject(this) @@ -48,71 +44,65 @@ class RecordAdapter : RecyclerView.Adapter { red = ContextCompat.getColor(context, R.color.red) green = ContextCompat.getColor(context, R.color.green) - summaryPresenter = ShortSummaryPresenter(context) - - this.itemClickListener = itemClickListener - this.isHeaderViewNeeded = isHeaderViewNeeded - - headerViewHolder = HeaderViewHolder(LayoutInflater.from(context).inflate(R.layout.view_summary_records, null), itemClickListener) - summaryPresenter.create(true, headerViewHolder) + this.isSummaryViewNeeded = isSummaryViewNeeded } - override fun getItemCount() = records.size + if (isHeaderViewNeeded) 1 else 0 + override fun getItemCount() = items.size + if (isSummaryViewNeeded) 1 else 0 - override fun getItemViewType(position: Int): Int = if (position == 0 && isHeaderViewNeeded) { + override fun getItemViewType(position: Int): Int = if (position == 0 && isSummaryViewNeeded) { + TYPE_SUMMARY + } else if (items[position - if (isSummaryViewNeeded) 1 else 0] is RecordItem.Header) { TYPE_HEADER } else { - TYPE_ITEM + TYPE_RECORD } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder = - if (viewType == TYPE_HEADER) { - headerViewHolder - } else { - ViewHolder(LayoutInflater.from(context).inflate(R.layout.view_record, parent, false), itemClickListener) + when (viewType) { + TYPE_RECORD -> RecordViewHolder(LayoutInflater.from(context).inflate(R.layout.view_record, parent, false), itemClickListener) + TYPE_HEADER -> HeaderViewHolder(LayoutInflater.from(context).inflate(R.layout.view_header_date, parent, false)) + else -> summaryViewHolder } - override fun onBindViewHolder(rvViewHolder: RecyclerView.ViewHolder, position: Int) { - if (position == 0 && isHeaderViewNeeded) { - //adapter already bound to view + override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) { + if (position == 0 && isSummaryViewNeeded) { + //view holder already bound to view return } - val viewHolder = rvViewHolder as ViewHolder - val record: Record = records[position - if (isHeaderViewNeeded) 1 else 0] - viewHolder.container.setBackgroundColor(if (record.isIncome) whiteGreen else whiteRed) - viewHolder.tvPrice.setTextColor(if (record.isIncome) green else red) - - viewHolder.tvDateAndTime.text = formatController.formatDateAndTime(record.time) - val price = (if (record.isIncome) record.fullPrice else getNegative(record.fullPrice)) - viewHolder.tvPrice.text = formatController.formatSignedAmount(price) - viewHolder.tvTitle.text = record.title - viewHolder.tvCategory.text = record.category?.name - viewHolder.tvCurrency.text = record.currency + if (viewHolder is RecordViewHolder) { + val record = items[position - if (isSummaryViewNeeded) 1 else 0] as RecordItem.Record + viewHolder.tvPrice.setTextColor(if (record.isIncome) green else red) + + val price = (if (record.isIncome) record.fullPrice else getNegative(record.fullPrice)) + viewHolder.tvPrice.text = formatController.formatSignedAmount(price) + viewHolder.tvTitle.text = record.title + viewHolder.tvCategory.text = record.categoryName + viewHolder.tvCurrency.text = record.currency + } else { + val headerViewHolder = viewHolder as HeaderViewHolder + val header = items[position - if (isSummaryViewNeeded) 1 else 0] as RecordItem.Header + headerViewHolder.tvDate.text = header.date + } } private fun getNegative(number: Double): Double { return -1 * number } - fun setRecords(recordsList: List, report: IRecordReport?, currency: String, ratesNeeded: List) { - records = recordsList - summaryPresenter.update(report, currency, ratesNeeded) + fun setRecords(itemsList: List) { + items = itemsList notifyDataSetChanged() } - class ViewHolder : RecyclerView.ViewHolder { + class RecordViewHolder : RecyclerView.ViewHolder { - var container: LinearLayout - var tvDateAndTime: TextView var tvPrice: TextView var tvTitle: TextView var tvCategory: TextView var tvCurrency: TextView constructor(view: View, itemClickListener: ((Int) -> Unit)?) : super(view) { - container = view.container - tvDateAndTime = view.tvDateAndTime tvPrice = view.tvPrice tvTitle = view.tvTitle tvCategory = view.tvCategory @@ -124,36 +114,13 @@ class RecordAdapter : RecyclerView.Adapter { } } - class HeaderViewHolder : RecyclerView.ViewHolder, ShortSummaryPresenter.SummaryViewInterface { - - private var tvPeriod: TextView - private var tvTotalIncome: TextView - private var tvTotalExpense: TextView - private var tvTotal: TextView - - override fun getTvPeriod(): TextView = tvPeriod - - override fun getTvTotalIncome(): TextView = tvTotalIncome - - override fun getTvTotalExpense(): TextView = tvTotalExpense - - override fun getTvTotal(): TextView = tvTotal - - constructor(view: View, itemClickListener: ((Int) -> Unit)?) : super(view) { - tvPeriod = view.tvPeriod - tvTotalIncome = view.tvTotalIncome - tvTotalExpense = view.tvTotalExpense - tvTotal = view.tvTotal - - view.setOnClickListener { - itemClickListener?.invoke(0) - } - } + class HeaderViewHolder(view: View) : RecyclerView.ViewHolder(view) { + val tvDate: TextView = view.tvDate } companion object { - - private const val TYPE_HEADER = 0 - private const val TYPE_ITEM = 1 + private const val TYPE_SUMMARY = 0 + private const val TYPE_HEADER = 1 + private const val TYPE_RECORD = 2 } } diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/RecordReportAdapter.kt b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/RecordReportAdapter.kt new file mode 100644 index 0000000..cc3f034 --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/RecordReportAdapter.kt @@ -0,0 +1,146 @@ +package com.blogspot.e_kanivets.moneytracker.adapter + +import android.content.Context +import android.support.v4.content.ContextCompat +import android.support.v7.widget.RecyclerView +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.TextView +import com.blogspot.e_kanivets.moneytracker.R +import com.blogspot.e_kanivets.moneytracker.entity.RecordReportItem +import kotlinx.android.synthetic.main.view_report_item_exp.view.* + +class RecordReportAdapter( + private var items: MutableList, + private var data: HashMap>, + private val context: Context +) : RecyclerView.Adapter() { + + private var red: Int = ContextCompat.getColor(context, R.color.red) + private var green: Int = ContextCompat.getColor(context, R.color.green) + + private lateinit var summaryViewHolder: RecyclerView.ViewHolder + + override fun getItemCount() = items.size + 1 + + override fun getItemViewType(position: Int): Int = when { + position == 0 -> TYPE_SUMMARY + items[getPositionWithoutSummary(position)] is RecordReportItem.ChildRow -> TYPE_CHILD + else -> TYPE_PARENT + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = when (viewType) { + TYPE_PARENT -> ParentViewHolder(LayoutInflater.from(context).inflate(R.layout.view_report_item_exp, parent, false), ::changeItems, context) + TYPE_CHILD -> ChildViewHolder(LayoutInflater.from(context).inflate(R.layout.view_report_item, parent, false)) + else -> summaryViewHolder + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + val posWithoutSummary = getPositionWithoutSummary(position) + if (posWithoutSummary < 0) return + + when (holder) { + is ChildViewHolder -> { + val row = items[posWithoutSummary] as RecordReportItem.ChildRow + holder.tvCategory.text = row.title + holder.tvTotal.text = row.amount + holder.tvTotal.setTextColor(if (row.amount.first() != '-') green else red) + } + is ParentViewHolder -> { + val row = items[posWithoutSummary] as RecordReportItem.ParentRow + holder.tvCategory.text = row.category + holder.tvTotal.text = row.totalAmount + holder.tvTotal.setTextColor(if (row.totalAmount.first() != '-') green else red) + } + } + } + + fun setData(items: MutableList, data: HashMap>) { + this.items = items + this.data = data + notifyDataSetChanged() + } + + fun setSummaryView(summaryView: View) { + this.summaryViewHolder = summaryView.tag as RecyclerView.ViewHolder + } + + private fun changeItems(position: Int) { + val positionWithoutSummary = getPositionWithoutSummary(position) + if (items[positionWithoutSummary] is RecordReportItem.ParentRow) { + val parentRow = items[positionWithoutSummary] as RecordReportItem.ParentRow + + if (parentRow.isOpen) { + closeParentRow(parentRow, positionWithoutSummary) + } else { + openParentRow(parentRow, positionWithoutSummary) + } + } + } + + private fun closeParentRow(parentRow: RecordReportItem.ParentRow, position: Int) { + val item = items.filterIndexed { index, _ -> index > position } + .find { it is RecordReportItem.ParentRow } + + val lastChildInd = if (item != null) items.indexOf(item) else items.size + + items.subList(position + 1, lastChildInd).clear() + val itemCount = lastChildInd - position - 1 + notifyItemRangeRemoved(getPositionWithSummary(position + 1), itemCount) + + parentRow.isOpen = false + } + + private fun openParentRow(parentRow: RecordReportItem.ParentRow, position: Int) { + data[parentRow]?.let { childRows -> + var lastChildInd = position + 1 + for (childRow in childRows) { + items.add(lastChildInd, childRow) + lastChildInd++ + } + notifyItemRangeInserted(getPositionWithSummary(position + 1), childRows.size) + } + + parentRow.isOpen = true + } + + private fun getPositionWithSummary(position: Int) = position + 1 + + private fun getPositionWithoutSummary(position: Int) = position - 1 + + class ParentViewHolder(view: View, changeItems: ((Int) -> Unit), context: Context) : RecyclerView.ViewHolder(view) { + + var tvCategory: TextView = view.tvCategory + var tvTotal: TextView = view.tvTotal + + private var isOpen: Boolean = false + + init { + view.setOnClickListener { + if (isOpen) { + view.lowerDivider.visibility = View.GONE + view.ivArrow.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_arrow_downward_outline)) + } else { + view.lowerDivider.visibility = View.VISIBLE + view.ivArrow.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.ic_arrow_upward_outline)) + } + isOpen = !isOpen + changeItems(adapterPosition) + } + } + } + + class ChildViewHolder(view: View) : RecyclerView.ViewHolder(view) { + val tvCategory: TextView = view.tvCategory + val tvTotal: TextView = view.tvTotal + } + + companion object { + + private const val TYPE_SUMMARY = 0 + private const val TYPE_PARENT = 1 + private const val TYPE_CHILD = 2 + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/FormatController.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/FormatController.java index 33e73bf..e4e05e9 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/FormatController.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/FormatController.java @@ -20,7 +20,9 @@ public class FormatController { public static final String PRECISION_NONE = "precision_none"; @SuppressLint("SimpleDateFormat") - private static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + private static final SimpleDateFormat shortDateFormat = new SimpleDateFormat("yyyy-MM-dd"); + @SuppressLint("SimpleDateFormat") + private static final SimpleDateFormat fullDateFormat = new SimpleDateFormat("d MMMM yyyy"); @SuppressLint("SimpleDateFormat") private static final SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm"); @SuppressLint("SimpleDateFormat") @@ -72,8 +74,12 @@ public String formatExpense(double amount, String currency) { return (amount > 0 ? "+ " : "- ") + formatAmount(Math.abs(amount)) + " " + currency; } - public String formatDate(long timestamp) { - return dateFormat.format(new Date(timestamp)); + public String formatDateToNumber(long timestamp) { + return shortDateFormat.format(new Date(timestamp)); + } + + public String formatDateToString(long timestamp) { + return fullDateFormat.format(new Date(timestamp)); } public String formatTime(long timestamp) { diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/PeriodController.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/PeriodController.java index ab947c7..d634e9b 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/PeriodController.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/PeriodController.java @@ -43,7 +43,7 @@ public Period readLastUsedPeriod() { return allTimePeriod(); case Period.TYPE_CUSTOM: - return weekPeriod(); + return customPeriod(first, last); default: return weekPeriod(); @@ -58,6 +58,16 @@ public void writeLastUsedPeriod(Period period) { preferenceController.writePeriodType(period.getType()); } + public Period customPeriod(long firstTs, long lastTs) { + Calendar cal = Calendar.getInstance(); + cal.setTimeInMillis(firstTs); + Date first = cal.getTime(); + cal.setTimeInMillis(lastTs); + Date last = cal.getTime(); + + return new Period(first, last, Period.TYPE_CUSTOM); + } + public Period dayPeriod() { Calendar cal = Calendar.getInstance(); diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/data/AccountController.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/data/AccountController.java index f383cd7..e633460 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/data/AccountController.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/data/AccountController.java @@ -21,7 +21,8 @@ * @author Evgenii Kanivets */ public class AccountController extends BaseController { - @SuppressWarnings("unused") private static final String TAG = "AccountController"; + @SuppressWarnings("unused") + private static final String TAG = "AccountController"; private final PreferenceController preferenceController; @@ -30,11 +31,15 @@ public AccountController(IRepo accountRepo, PreferenceController prefer this.preferenceController = preferenceController; } - @Nullable @Override public Account read(long id) { + @Nullable + @Override + public Account read(long id) { return substituteCurrency(super.read(id)); } - @NonNull @Override public List readAll() { + @NonNull + @Override + public List readAll() { List accountList = super.readAll(); List result = new ArrayList<>(); @@ -45,7 +50,8 @@ public AccountController(IRepo accountRepo, PreferenceController prefer return result; } - @NonNull public List readActiveAccounts() { + @NonNull + public List readActiveAccounts() { List result = new ArrayList<>(); for (Account account : readAll()) { @@ -57,7 +63,8 @@ public AccountController(IRepo accountRepo, PreferenceController prefer return result; } - @NonNull public List readArchivedAccounts() { + @NonNull + public List readArchivedAccounts() { List result = new ArrayList<>(); for (Account account : readAll()) { @@ -69,7 +76,7 @@ public AccountController(IRepo accountRepo, PreferenceController prefer return result; } - public boolean recordAdded(@Nullable Record record) { + boolean recordAdded(@Nullable Record record) { if (record == null || record.getAccount() == null) return false; Account account = repo.read(record.getAccount().getId()); @@ -93,8 +100,10 @@ public boolean recordAdded(@Nullable Record record) { return true; } - public boolean recordDeleted(@Nullable Record record) { - if (record == null || record.getAccount() == null) return false; + boolean recordDeleted(@Nullable Record record) { + if (record == null) return false; + + if (record.getAccount() == null) return true; Account account = repo.read(record.getAccount().getId()); if (account == null) return false; @@ -141,7 +150,8 @@ public boolean transferDone(@Nullable Transfer transfer) { return true; } - @Nullable public Account readDefaultAccount() { + @Nullable + public Account readDefaultAccount() { long defaultAccountId = preferenceController.readDefaultAccountId(); if (defaultAccountId == -1) { diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/di/AppComponent.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/di/AppComponent.java index 1f0b100..064033b 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/di/AppComponent.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/di/AppComponent.java @@ -17,7 +17,6 @@ import com.blogspot.e_kanivets.moneytracker.activity.record.MainActivity; import com.blogspot.e_kanivets.moneytracker.adapter.AccountAdapter; import com.blogspot.e_kanivets.moneytracker.adapter.ExchangeRateAdapter; -import com.blogspot.e_kanivets.moneytracker.adapter.ExpandableListReportAdapter; import com.blogspot.e_kanivets.moneytracker.adapter.MonthSummaryAdapter; import com.blogspot.e_kanivets.moneytracker.adapter.RecordAdapter; import com.blogspot.e_kanivets.moneytracker.di.module.repo.CachedRepoModule; @@ -26,6 +25,7 @@ import com.blogspot.e_kanivets.moneytracker.ui.PeriodSpinner; import com.blogspot.e_kanivets.moneytracker.ui.presenter.AccountsSummaryPresenter; import com.blogspot.e_kanivets.moneytracker.ui.presenter.ShortSummaryPresenter; +import com.blogspot.e_kanivets.moneytracker.util.RecordItemsBuilder; import javax.inject.Singleton; @@ -74,8 +74,6 @@ public interface AppComponent { void inject(AccountAdapter accountAdapter); - void inject(ExpandableListReportAdapter expandableListReportAdapter); - void inject(ShortSummaryPresenter shortSummaryPresenter); void inject(ExchangeRateAdapter exchangeRateAdapter); @@ -87,4 +85,8 @@ public interface AppComponent { void inject(EditAccountFragment editAccountFragment); void inject(AccountOperationsFragment accountRecordsFragment); + + void inject(RecordItemsBuilder recordItemsBuilder); + + void inject(ReportActivity.RecordReportConverter recordReportConverter); } diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/entity/RecordItem.kt b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/entity/RecordItem.kt new file mode 100644 index 0000000..9a4f625 --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/entity/RecordItem.kt @@ -0,0 +1,10 @@ +package com.blogspot.e_kanivets.moneytracker.entity + +sealed class RecordItem { + + data class Header(val date: String) : RecordItem() + + data class Record(val title: String, val categoryName: String, val fullPrice: Double, val currency: String, val isIncome: Boolean) : RecordItem() { + constructor(record: com.blogspot.e_kanivets.moneytracker.entity.data.Record) : this(record.title, record.category?.name?.toString().orEmpty(), record.fullPrice, record.currency, record.isIncome) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/entity/RecordReportItem.kt b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/entity/RecordReportItem.kt new file mode 100644 index 0000000..7e8ac01 --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/entity/RecordReportItem.kt @@ -0,0 +1,9 @@ +package com.blogspot.e_kanivets.moneytracker.entity + +sealed class RecordReportItem { + + data class ChildRow(val title: String, val amount: String) : RecordReportItem() + + data class ParentRow(val category: String, val totalAmount: String, var isOpen: Boolean) : RecordReportItem() + +} \ No newline at end of file diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/record/RecordReport.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/record/RecordReport.java index c9cf415..1e7dc8a 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/record/RecordReport.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/record/RecordReport.java @@ -190,20 +190,7 @@ private SummaryRecord createSummaryRecord(String title, List 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; + return new SummaryRecord(title, currency, amount, recordList.size()); } private double getAmount(Record record) { diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/record/RecordReportConverter.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/record/RecordReportConverter.java deleted file mode 100644 index 02acc31..0000000 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/record/RecordReportConverter.java +++ /dev/null @@ -1,100 +0,0 @@ -package com.blogspot.e_kanivets.moneytracker.report.record; - -import android.support.annotation.LayoutRes; -import android.support.annotation.NonNull; - -import com.blogspot.e_kanivets.moneytracker.R; -import com.blogspot.e_kanivets.moneytracker.report.record.model.CategoryRecord; -import com.blogspot.e_kanivets.moneytracker.report.record.model.SummaryRecord; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Util class to convert {@link RecordReport} to {@link android.widget.ExpandableListView} input data. - * Created on 2/26/16. - * - * @author Evgenii Kanivets - */ -public class RecordReportConverter { - public static final String TITLE_PARAM_NAME = "title"; - public static final String PRICE_PARAM_NAME = "price"; - - private final IRecordReport report; - - public RecordReportConverter(@NonNull IRecordReport report) { - this.report = report; - } - - public List> getGroupData() { - List> groupData = new ArrayList<>(); - - for (CategoryRecord categoryRecord : report.getSummary()) { - Map m = new HashMap<>(); - m.put(TITLE_PARAM_NAME, categoryRecord.getTitle()); - m.put(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[]{TITLE_PARAM_NAME, PRICE_PARAM_NAME}; - } - - @NonNull - public int[] getGroupTo() { - return new int[]{R.id.tvCategory, R.id.tvTotal}; - } - - @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(TITLE_PARAM_NAME, getTitle(summaryRecord)); - m.put(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[]{TITLE_PARAM_NAME, PRICE_PARAM_NAME}; - } - - @NonNull - public int[] getChildTo() { - return new int[]{R.id.tvCategory, R.id.tvTotal}; - } - - private String getTitle(@NonNull SummaryRecord record) { - int count = record.getRecordList().size(); - if (count <= 1) return record.getTitle(); - else return record.getTitle() + " (" + count + ")"; - } -} diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/record/model/SummaryRecord.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/record/model/SummaryRecord.java index e60ab03..92ee65e 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/record/model/SummaryRecord.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/report/record/model/SummaryRecord.java @@ -1,11 +1,7 @@ package com.blogspot.e_kanivets.moneytracker.report.record.model; -import android.support.annotation.NonNull; - -import com.blogspot.e_kanivets.moneytracker.entity.data.Record; - -import java.util.ArrayList; -import java.util.List; +import com.blogspot.e_kanivets.moneytracker.MtApp; +import com.blogspot.e_kanivets.moneytracker.R; /** * Entity class. @@ -17,17 +13,11 @@ 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; + public SummaryRecord(String title, String currency, double amount, int recordsCount) { + this.title = buildTitle(title, recordsCount); this.currency = currency; this.amount = amount; - recordList = new ArrayList<>(); - } - - public void add(@NonNull Record record) { - recordList.add(record); } public String getTitle() { @@ -42,17 +32,10 @@ public double getAmount() { return amount; } - public List getRecordList() { - return recordList; + private String buildTitle(String title, int recordsCount) { + if (recordsCount <= 1) return title; + else + return MtApp.get().getResources().getString(R.string.title_summary_record, title, recordsCount); } - @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/PeriodSpinner.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/ui/PeriodSpinner.java index 1f7e286..2f276bc 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/ui/PeriodSpinner.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/ui/PeriodSpinner.java @@ -6,6 +6,7 @@ import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; +import android.widget.Toast; import com.blogspot.e_kanivets.moneytracker.MtApp; import com.blogspot.e_kanivets.moneytracker.R; @@ -48,11 +49,14 @@ public PeriodSpinner(Context context, AttributeSet attrs, int defStyleAttr) { init(context); } - public void setPeriod(Period period) { + public void updatePeriod(Period period) { if (lastPeriod != null && lastPeriod.equals(period)) return; if (periodSelectedListener != null) periodSelectedListener.onPeriodSelected(period); lastPeriod = period; + } + + public void setPeriod(Period period) { switch (period.getType()) { case Period.TYPE_DAY: @@ -74,7 +78,13 @@ public void setPeriod(Period period) { case Period.TYPE_ALL_TIME: setSelection(4); break; + + case Period.TYPE_CUSTOM: + super.setSelection(5); + updatePeriod(period); + break; } + } public void setPeriodSelectedListener(OnPeriodSelectedListener periodSelectedListener) { @@ -107,23 +117,23 @@ public void onItemSelected(AdapterView parent, View view, int position, long switch (position) { case 0: - setPeriod(periodController.dayPeriod()); + updatePeriod(periodController.dayPeriod()); break; case 1: - setPeriod(periodController.weekPeriod()); + updatePeriod(periodController.weekPeriod()); break; case 2: - setPeriod(periodController.monthPeriod()); + updatePeriod(periodController.monthPeriod()); break; case 3: - setPeriod(periodController.yearPeriod()); + updatePeriod(periodController.yearPeriod()); break; case 4: - setPeriod(periodController.allTimePeriod()); + updatePeriod(periodController.allTimePeriod()); break; case 5: @@ -176,7 +186,11 @@ public void OnDataChanged(Date toDate) { cal.set(Calendar.SECOND, 59); cal.set(Calendar.MILLISECOND, 999); - setPeriod(new Period(fromDate, cal.getTime(), Period.TYPE_CUSTOM)); + if (cal.getTime().getTime() < fromDate.getTime()) { + Toast.makeText(context, R.string.start_earlier_end, Toast.LENGTH_SHORT).show(); + } else { + updatePeriod(new Period(fromDate, cal.getTime(), Period.TYPE_CUSTOM)); + } } }); dialog.show(); diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/ui/presenter/AccountsSummaryPresenter.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/ui/presenter/AccountsSummaryPresenter.java index 9417fe5..bdb2fc5 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/ui/presenter/AccountsSummaryPresenter.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/ui/presenter/AccountsSummaryPresenter.java @@ -116,7 +116,7 @@ public void update() { } public static class ViewHolder { - @BindView(R.id.spinner_currency) + @BindView(R.id.spinnerCurrency) AppCompatSpinner spinnerCurrency; @BindView(R.id.tvTotal) TextView tvTotal; diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/ui/presenter/ShortSummaryPresenter.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/ui/presenter/ShortSummaryPresenter.java index 65e9893..19229bb 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/ui/presenter/ShortSummaryPresenter.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/ui/presenter/ShortSummaryPresenter.java @@ -1,13 +1,13 @@ package com.blogspot.e_kanivets.moneytracker.ui.presenter; import android.content.Context; +import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.widget.TextView; import com.blogspot.e_kanivets.moneytracker.MtApp; import com.blogspot.e_kanivets.moneytracker.R; -import com.blogspot.e_kanivets.moneytracker.adapter.RecordAdapter; import com.blogspot.e_kanivets.moneytracker.controller.FormatController; import com.blogspot.e_kanivets.moneytracker.entity.Period; import com.blogspot.e_kanivets.moneytracker.report.record.IRecordReport; @@ -36,7 +36,6 @@ public class ShortSummaryPresenter extends BaseSummaryPresenter { private int green; private View view; - @SuppressWarnings("deprecation") public ShortSummaryPresenter(Context context) { this.context = context; MtApp.get().getAppComponent().inject(ShortSummaryPresenter.this); @@ -46,39 +45,44 @@ public ShortSummaryPresenter(Context context) { green = context.getResources().getColor(R.color.green); } - public View create(boolean shortSummary, RecordAdapter.HeaderViewHolder mainViewHolder) { + public interface ItemClickListener { + void invoke(); + } + + public View create(boolean shortSummary, ItemClickListener itemClickListener) { view = layoutInflater.inflate(R.layout.view_summary_records, null); view.findViewById(R.id.iv_more).setVisibility(shortSummary ? View.VISIBLE : View.INVISIBLE); - - view.setTag(mainViewHolder != null ? mainViewHolder : new ViewHolder(view)); + view.setEnabled(false); + view.findViewById(R.id.lvSummary).setClickable(false); + view.findViewById(R.id.cvSummary).setClickable(true); + view.setTag(new ViewHolder(view, itemClickListener)); return view; } public void update(IRecordReport report, String currency, List ratesNeeded) { - SummaryViewInterface viewHolder = (SummaryViewInterface) view.getTag(); + ViewHolder viewHolder = (ViewHolder) view.getTag(); if (report == null) { - viewHolder.getTvTotalIncome().setText(""); - viewHolder.getTvTotalExpense().setText(""); + viewHolder.tvTotalIncome.setText(""); + viewHolder.tvTotalExpense.setText(""); - viewHolder.getTvTotal().setTextColor(red); - viewHolder.getTvTotal().setText(createRatesNeededList(currency, ratesNeeded)); + viewHolder.tvTotal.setTextColor(red); + viewHolder.tvTotal.setText(createRatesNeededList(currency, ratesNeeded)); } else { - viewHolder.getTvPeriod().setText(formatPeriod(report.getPeriod())); + viewHolder.tvPeriod.setText(formatPeriod(report.getPeriod())); - viewHolder.getTvTotalIncome().setTextColor(report.getTotalIncome() >= 0 ? green : red); - viewHolder.getTvTotalIncome().setText(formatController.formatIncome(report.getTotalIncome(), + viewHolder.tvTotalIncome.setTextColor(report.getTotalIncome() >= 0 ? green : red); + viewHolder.tvTotalIncome.setText(formatController.formatIncome(report.getTotalIncome(), report.getCurrency())); - viewHolder.getTvTotalExpense().setTextColor(report.getTotalExpense() > 0 ? green : red); - viewHolder.getTvTotalExpense().setText(formatController.formatExpense(report.getTotalExpense(), + viewHolder.tvTotalExpense.setTextColor(report.getTotalExpense() > 0 ? green : red); + viewHolder.tvTotalExpense.setText(formatController.formatExpense(report.getTotalExpense(), report.getCurrency())); - viewHolder.getTvTotal().setTextColor(report.getTotal() >= 0 ? green : red); - viewHolder.getTvTotal().setText(formatController.formatIncome(report.getTotal(), + viewHolder.tvTotal.setTextColor(report.getTotal() >= 0 ? green : red); + viewHolder.tvTotal.setText(formatController.formatIncome(report.getTotal(), report.getCurrency())); } - } private String formatPeriod(Period period) { @@ -101,53 +105,28 @@ private String formatPeriod(Period period) { } } - public static class ViewHolder implements SummaryViewInterface { + public static class ViewHolder extends RecyclerView.ViewHolder { @BindView(R.id.tvPeriod) TextView tvPeriod; - - @Override - public TextView getTvPeriod() { - return tvPeriod; - } - @BindView(R.id.tvTotalIncome) TextView tvTotalIncome; - - @Override - public TextView getTvTotalIncome() { - return tvTotalIncome; - } - @BindView(R.id.tvTotalExpense) TextView tvTotalExpense; - - @Override - public TextView getTvTotalExpense() { - return tvTotalExpense; - } - @BindView(R.id.tvTotal) TextView tvTotal; - @Override - public TextView getTvTotal() { - return tvTotal; - } - - public ViewHolder(View view) { + public ViewHolder(View view, final ItemClickListener itemClickListener) { + super(view); ButterKnife.bind(this, view); + view.findViewById(R.id.cvSummary).setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + if (itemClickListener != null) + itemClickListener.invoke(); + } + }); } } - public interface SummaryViewInterface { - TextView getTvPeriod(); - - TextView getTvTotalIncome(); - - TextView getTvTotalExpense(); - - TextView getTvTotal(); - } - } \ No newline at end of file diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/RecordItemsBuilder.kt b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/RecordItemsBuilder.kt new file mode 100644 index 0000000..5b4f61a --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/RecordItemsBuilder.kt @@ -0,0 +1,34 @@ +package com.blogspot.e_kanivets.moneytracker.util + +import com.blogspot.e_kanivets.moneytracker.MtApp +import com.blogspot.e_kanivets.moneytracker.controller.FormatController +import com.blogspot.e_kanivets.moneytracker.entity.RecordItem +import com.blogspot.e_kanivets.moneytracker.entity.data.Record +import javax.inject.Inject + +class RecordItemsBuilder { + + @Inject + lateinit var formatController: FormatController + + constructor() { + MtApp.get().appComponent.inject(this) + } + + fun getRecordItems(recordList: List): List { + val recordItems: MutableList = mutableListOf() + + var lastDate: String? = null + for (record in recordList) { + if (formatController.formatDateToString(record.time) != lastDate) { + lastDate = formatController.formatDateToString(record.time) + recordItems.add(RecordItem.Header(lastDate)) + } + + recordItems.add(RecordItem.Record(record)) + } + + return recordItems + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/validator/AccountValidator.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/validator/AccountValidator.java index 0d095ed..e05d6d1 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/validator/AccountValidator.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/validator/AccountValidator.java @@ -27,7 +27,7 @@ public class AccountValidator implements IValidator { @BindView(R.id.til_title) TextInputLayout tilTitle; - @BindView(R.id.et_title) + @BindView(R.id.etTitle) EditText etTitle; @BindView(R.id.til_init_sum) TextInputLayout tilInitSum; diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/validator/RecordValidator.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/validator/RecordValidator.java index 3a842a9..8d9fd94 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/validator/RecordValidator.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/validator/RecordValidator.java @@ -28,17 +28,17 @@ public class RecordValidator implements IValidator { @BindView(R.id.til_title) TextInputLayout tilTitle; - @BindView(R.id.et_title) + @BindView(R.id.etTitle) EditText etTitle; @BindView(R.id.til_category) TextInputLayout tilCategory; - @BindView(R.id.et_category) + @BindView(R.id.etCategory) EditText etCategory; @BindView(R.id.til_price) TextInputLayout tilPrice; - @BindView(R.id.et_price) + @BindView(R.id.etPrice) EditText etPrice; - @BindView(R.id.spinner_account) + @BindView(R.id.spinnerAccount) AppCompatSpinner spinnerAccount; public RecordValidator(@NonNull Context context, @NonNull View view) { diff --git a/app/src/main/res/drawable/ic_arrow_downward_outline.xml b/app/src/main/res/drawable/ic_arrow_downward_outline.xml new file mode 100644 index 0000000..aeb74d4 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_downward_outline.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/drawable/ic_arrow_upward_outline.xml b/app/src/main/res/drawable/ic_arrow_upward_outline.xml new file mode 100644 index 0000000..f7be487 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_upward_outline.xml @@ -0,0 +1,14 @@ + + + + diff --git a/app/src/main/res/layout/activity_add_exchange_rate.xml b/app/src/main/res/layout/activity_add_exchange_rate.xml index 9a62337..5011497 100644 --- a/app/src/main/res/layout/activity_add_exchange_rate.xml +++ b/app/src/main/res/layout/activity_add_exchange_rate.xml @@ -21,7 +21,7 @@ - + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_report.xml b/app/src/main/res/layout/activity_report.xml index 6853333..faefa37 100644 --- a/app/src/main/res/layout/activity_report.xml +++ b/app/src/main/res/layout/activity_report.xml @@ -19,7 +19,7 @@ app:popupTheme="@style/AppTheme.PopupOverlay"> - + android:layout_height="match_parent" + android:layout_gravity="center_horizontal" + app:layoutManager="android.support.v7.widget.LinearLayoutManager" /> diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index 10d86e8..b697daa 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -22,7 +22,7 @@ diff --git a/app/src/main/res/layout/content_add_account.xml b/app/src/main/res/layout/content_add_account.xml index d29168f..52c5e30 100644 --- a/app/src/main/res/layout/content_add_account.xml +++ b/app/src/main/res/layout/content_add_account.xml @@ -2,7 +2,7 @@ @@ -87,7 +87,7 @@ android:layout_height="wrap_content"> + + + diff --git a/app/src/main/res/layout/view_record.xml b/app/src/main/res/layout/view_record.xml index f1f663c..4bed13d 100644 --- a/app/src/main/res/layout/view_record.xml +++ b/app/src/main/res/layout/view_record.xml @@ -1,5 +1,6 @@ @@ -12,9 +13,9 @@ android:gravity="center_vertical" android:orientation="horizontal" android:paddingStart="@dimen/activity_horizontal_margin" - android:paddingTop="@dimen/half_margin" + android:paddingTop="@dimen/quarter_margin" android:paddingEnd="@dimen/activity_horizontal_margin" - android:paddingBottom="@dimen/half_margin"> + android:paddingBottom="@dimen/quarter_margin"> + android:theme="@style/Text_Body2" + tools:text="New Text" /> - - - - - - - + android:layout_gravity="center_vertical|top" + android:gravity="start" + android:textAppearance="?android:attr/textAppearanceSmall" + android:theme="@style/Text_Caption" + tools:text="New Text" /> @@ -80,8 +55,8 @@ android:layout_height="wrap_content" android:background="@android:color/transparent" android:gravity="end" - android:text="New Text" - android:theme="@style/Text_Body2" /> + android:theme="@style/Text_Body2" + tools:text="New Text" /> + android:theme="@style/Text_Body1" + tools:text="New Text" /> diff --git a/app/src/main/res/layout/view_report_item.xml b/app/src/main/res/layout/view_report_item.xml index 3d75b26..7b03aa7 100644 --- a/app/src/main/res/layout/view_report_item.xml +++ b/app/src/main/res/layout/view_report_item.xml @@ -6,9 +6,9 @@ android:background="@android:color/transparent" android:orientation="horizontal" android:paddingStart="@dimen/activity_horizontal_margin" - android:paddingTop="@dimen/half_margin" + android:paddingTop="@dimen/quarter_margin" android:paddingEnd="@dimen/activity_horizontal_margin" - android:paddingBottom="@dimen/half_margin"> + android:paddingBottom="@dimen/quarter_margin"> - - + + + + - - + android:layout_marginTop="4dp" + android:layout_marginBottom="4dp" + android:orientation="horizontal"> + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_summary_accounts.xml b/app/src/main/res/layout/view_summary_accounts.xml index 098d14f..6216df9 100644 --- a/app/src/main/res/layout/view_summary_accounts.xml +++ b/app/src/main/res/layout/view_summary_accounts.xml @@ -27,7 +27,7 @@ android:text="@string/currency" /> diff --git a/app/src/main/res/layout/view_summary_records.xml b/app/src/main/res/layout/view_summary_records.xml index a7ee8cb..6bca902 100644 --- a/app/src/main/res/layout/view_summary_records.xml +++ b/app/src/main/res/layout/view_summary_records.xml @@ -1,97 +1,116 @@ - - - + + + card_view:cardCornerRadius="5dp" + card_view:cardElevation="5dp" + card_view:cardUseCompatPadding="true"> - - + + + android:orientation="horizontal"> - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + - \ No newline at end of file + + + + + \ No newline at end of file diff --git a/app/src/main/res/menu/menu_add_record.xml b/app/src/main/res/menu/menu_add_record.xml index da5eafd..bf39684 100644 --- a/app/src/main/res/menu/menu_add_record.xml +++ b/app/src/main/res/menu/menu_add_record.xml @@ -8,10 +8,4 @@ android:title="@string/delete" tools:showAsAction="ifRoom" /> - - \ No newline at end of file diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 4995132..0796ac7 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -44,7 +44,7 @@ На счет Курсы обмена Добавить курс обмена - Счет был удален + Счет удален Краткий отчет Другой @@ -129,4 +129,7 @@ График Информация Операции + + Дата конца не может быть раньше даты начала + diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index d828602..c2312ba 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -44,7 +44,7 @@ На рахунок Курси обміну Додати курс обміну - Рахунок було видалено + Рахунок видалено Короткий звіт Інший @@ -129,4 +129,6 @@ Графік Інформація Операції + + Дата початку не може бути раніше за дату кінця diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index a8fc9f1..7874beb 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -40,7 +40,7 @@ 转换汇率 添加汇率 - 账户已清空 + 账户已清空 简要汇总 个性化 @@ -118,4 +118,7 @@ 圖形 信息 操作 + + 开始日期不能早于结束日期 + diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 7413143..2a4e725 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -1,9 +1,10 @@ - 16dp 16dp 16dp 8dp 4dp + 2dp + 16dp 0dp 1dp 40dp diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 7d479d0..9f31824 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,82 +1,82 @@ - Money Tracker - Title - Category - Price - Add income - Add expense - Add - Report - CHANGE THE DATE - TOTAL - Total income - Total expense - Delete - Day - Week - Month - Year - All time - Do you like MoneyTracker? - Rate it on Google Play! - Maybe Later - No, Thank You - Yes! - - Records - Accounts - Add income - Add expense - Add account - - Open navigation drawer - Close navigation drawer - - Export - Add account - Initial sum - Account - Transfer - From - To - Exchange rates - Add exchange rate - " -> " - Account was removed - - Short summary - Custom - Summary - Currency - Error! Specify exchange rates. - Amount - Settings - Settings - Default account - pref_default_account - pref_default_currency - %1$s - %2$s - pref_non_substitution_currency - Default currency - NON substitution currency - - Results - Incomes - Expenses - - pref_display_precision - Display precision - 9.99$ = 10$ - 9.99$ = 9$ - 9.99$ = 9.99$ - - Import/Export - Import - %1$s records has been imported - Help - To import records into application:\n + Money Tracker + Title + Category + Price + Add income + Add expense + Add + Report + CHANGE THE DATE + TOTAL + Total income + Total expense + Delete + Day + Week + Month + Year + All time + Do you like MoneyTracker? + Rate it on Google Play! + Maybe Later + No, Thank You + Yes! + + Records + Accounts + Add income + Add expense + Add account + + Open navigation drawer + Close navigation drawer + + Export + Add account + Initial sum + Account + Transfer + From + To + Exchange rates + Add exchange rate + " -> " + Account removed + + Short summary + Custom + Summary + Currency + Error! Specify exchange rates. + Amount + Settings + Settings + Default account + pref_default_account + pref_default_currency + %1$s - %2$s + pref_non_substitution_currency + Default currency + NON substitution currency + + Results + Incomes + Expenses + + pref_display_precision + Display precision + 9.99$ = 10$ + 9.99$ = 9$ + 9.99$ = 9.99$ + + Import/Export + Import + %1$s records has been imported + Help + To import records into application:\n 1. Create CSV file in format - \n time;title;category;price;currency.\n 2. Copy its content to text field.\n @@ -85,62 +85,65 @@ Note: time - timestamp value in milliseconds; currency - 3 symbols code of currency.\n Example: 1466948795712;metro;transport;-20.0;UAH - / - Bank sells - Convert to - Bank buys - Convert from - - Date - Time - - Backup/Restore - Failed to create backup. - Failed to restore backup. - Chosen backup file %1$s has been restored successfully. - Backup is restored - Warning - Do you really want to erase all current data and restore backup file %1$s? Be sure to backup current data. - Backup now - - Edit income - Edit expense - - Importing records… - Making backup… - Restoring backup… - Fetching backups… - Removing backup… - - pref_about - Version %1$s (Android %2$s) - About - Open Money Tracker (OMT) is an accounting application. You can manage your incomes and expenses in easy way. Accounts and currency exchange rates are also available.\n\nTo help us make OMT better, please report bugs to the 4PDA forum; new feature ideas are very welcome too. Find answers or start a discussion on forum.\n\nOMT is open source software, so everyone is encouraged to become a contributor. Non-developers can also help. For instance, you can help translate OMT, provide screenshots or blog about OMT.\n\nOMT is released under the GNU-GPL v3 license and source code is available on GitHub.\n\nDeveloper profile on LinkedIn. - - Field can\'t be empty. - You can\'t be so rich or so poor. - You can\'t be so rich. - None - It\'s too much for transfer. - It\'s too much for exchange. - Can\'t convert between same currencies. - Can\'t create a record in future. - At least one account is needed. - - Edit Account - Color - Goal - Restore - Archive - You can\'t archive a default account. - Delete account - Are you sure about deleting this account? It can\'t be restored after. - - Graph - Information - Operations - - - Delete backup - Are you sure about deleting this backup file %1$s? It can\'t be restored after. + / + Bank sells + Convert to + Bank buys + Convert from + + Date + Time + + Backup/Restore + Failed to create backup. + Failed to restore backup. + Chosen backup file %1$s has been restored successfully. + Backup is restored + Warning + Do you really want to erase all current data and restore backup file %1$s? Be sure to backup current data. + Backup now + + Edit income + Edit expense + + Importing records… + Making backup… + Restoring backup… + Fetching backups… + Removing backup… + + pref_about + Version %1$s (Android %2$s) + About + Open Money Tracker (OMT) is an accounting application. You can manage your incomes and expenses in easy way. Accounts and currency exchange rates are also available.\n\nTo help us make OMT better, please report bugs to the 4PDA forum; new feature ideas are very welcome too. Find answers or start a discussion on forum.\n\nOMT is open source software, so everyone is encouraged to become a contributor. Non-developers can also help. For instance, you can help translate OMT, provide screenshots or blog about OMT.\n\nOMT is released under the GNU-GPL v3 license and source code is available on GitHub.\n\nDeveloper profile on LinkedIn. + + Field can\'t be empty. + You can\'t be so rich or so poor. + You can\'t be so rich. + None + It\'s too much for transfer. + It\'s too much for exchange. + Can\'t convert between same currencies. + Can\'t create a record in future. + At least one account is needed. + + Edit Account + Color + Goal + Restore + Archive + You can\'t archive a default account. + Delete account + Are you sure about deleting this account? It can\'t be restored after. + + Graph + Information + Operations + + + Delete backup + Are you sure about deleting this backup file %1$s? It can\'t be restored after. + + %1$s (%2$d) + End date can\'t be earlier than start date diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index b0a43c7..d283a56 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -59,6 +59,13 @@ @color/text_black + +