diff --git a/.travis.yml b/.travis.yml index 1337593..69a752b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,17 +3,17 @@ jdk: oraclejdk8 android: components: # The BuildTools version used by your project - - build-tools-26.0.2 + - build-tools-27.0.3 # The SDK version used to compile your project - android-21 - - android-26 + - android-27 # Additional components - extra-google-google_play_services - extra-google-m2repository - extra-android-m2repository - - addon-google_apis-google-26 + - addon-google_apis-google-27 # Specify at least one system image, # if you need to run emulator(s) during your tests diff --git a/app/build.gradle b/app/build.gradle index c13be0b..87e4167 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,19 +8,22 @@ buildscript { } } apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-android-extensions' apply plugin: 'io.fabric' repositories { maven { url 'https://maven.fabric.io/public' } + mavenCentral() } android { - compileSdkVersion 26 - buildToolsVersion '26.0.2' + compileSdkVersion 27 + buildToolsVersion '27.0.3' defaultConfig { applicationId 'com.blogspot.e_kanivets.moneytracker' minSdkVersion 17 - targetSdkVersion 26 + targetSdkVersion 27 versionCode 23 versionName '1.8.7' } @@ -65,22 +68,23 @@ dependencies { transitive = true } - compile 'com.android.support:support-v4:26.1.0' - compile 'com.android.support:appcompat-v7:26.1.0' - compile 'com.android.support:design:26.1.0' + compile 'com.android.support:support-v4:27.1.1' + compile 'com.android.support:appcompat-v7:27.1.1' + compile 'com.android.support:design:27.1.1' compile 'com.jakewharton:butterknife:8.5.1' // View annotation bindings annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1' // ButterKnife compiler - compile 'com.google.dagger:dagger:2.0.1' // Dependency injection tool + compile 'com.google.dagger:dagger:2.11' // Dependency injection tool compile 'com.github.PhilJay:MPAndroidChart:v2.2.4' // Charts compile 'com.jakewharton.timber:timber:4.1.2' // Advanced logging tool - compile 'com.android.support.constraint:constraint-layout:1.0.2' + compile 'com.android.support.constraint:constraint-layout:1.1.3' compile 'com.dropbox.core:dropbox-core-sdk:3.0.5' // Dropbox Core API testCompile 'junit:junit:4.12' - testCompile 'org.mockito:mockito-core:2.0.43-beta' + testCompile 'org.mockito:mockito-core:2.8.9' androidTestCompile 'com.crittercism.dexmaker:dexmaker:1.4' androidTestCompile 'com.crittercism.dexmaker:dexmaker-dx:1.4' androidTestCompile 'com.crittercism.dexmaker:dexmaker-mockito:1.4' - annotationProcessor 'com.google.dagger:dagger-compiler:2.0.1' + 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" } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2776004..0b50798 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -87,7 +87,7 @@ android:screenOrientation="portrait" android:theme="@style/Theme.Default" /> accountList = accountController.readActiveAccounts(); @@ -87,9 +74,9 @@ private void setupDefaultAccountPref() { } } - @SuppressWarnings("ToArrayCallWithZeroLengthArrayArgument") - private void setupDefaultCurrencyPref() { - ListPreference defaultCurrencyPref = (ListPreference) findPreference(getString(R.string.pref_default_currency)); + @SuppressWarnings("ToArrayCallWithZeroLengthArrayArgument") private void setupDefaultCurrencyPref() { + ListPreference defaultCurrencyPref = + (ListPreference) findPreference(getString(R.string.pref_default_currency)); defaultCurrencyPref.setOnPreferenceChangeListener(preferenceChangeListener); List currencyList = currencyController.readAll(); @@ -101,9 +88,23 @@ private void setupDefaultCurrencyPref() { defaultCurrencyPref.setSummary(defaultCurrency); } - @SuppressWarnings("ToArrayCallWithZeroLengthArrayArgument") - private void setupDisplayPrecision() { - ListPreference displayPrecisionPref = (ListPreference) findPreference(getString(R.string.pref_display_precision)); + @SuppressWarnings("ToArrayCallWithZeroLengthArrayArgument") private void setupNonSubstitutionCurrencyPref() { + ListPreference nonSubstitutionCurrencyPref = + (ListPreference) findPreference(getString(R.string.pref_non_substitution_currency)); + nonSubstitutionCurrencyPref.setOnPreferenceChangeListener(preferenceChangeListener); + + List currencyList = currencyController.readAll(); + nonSubstitutionCurrencyPref.setEntries(currencyList.toArray(new String[0])); + nonSubstitutionCurrencyPref.setEntryValues(currencyList.toArray(new String[0])); + + String nonSubstitutionCurrency = preferenceController.readNonSubstitutionCurrency(); + nonSubstitutionCurrencyPref.setDefaultValue(nonSubstitutionCurrency); + nonSubstitutionCurrencyPref.setSummary(nonSubstitutionCurrency); + } + + @SuppressWarnings("ToArrayCallWithZeroLengthArrayArgument") private void setupDisplayPrecision() { + ListPreference displayPrecisionPref = + (ListPreference) findPreference(getString(R.string.pref_display_precision)); displayPrecisionPref.setOnPreferenceChangeListener(preferenceChangeListener); List precisionListValues = new ArrayList<>(); @@ -124,6 +125,11 @@ private void setupDisplayPrecision() { } } + private void setupAboutPref() { + Preference preference = findPreference(getString(R.string.pref_about)); + preference.setSummary(getString(R.string.about_summary, BuildConfig.VERSION_NAME, Build.VERSION.RELEASE)); + } + @SuppressWarnings("ToArrayCallWithZeroLengthArrayArgument") private String[] getEntries(List accountList) { List result = new ArrayList<>(); @@ -146,16 +152,15 @@ private String[] getEntryValues(List accountList) { return result.toArray(new String[0]); } - private Preference.OnPreferenceChangeListener preferenceChangeListener - = new Preference.OnPreferenceChangeListener() { - @Override - public boolean onPreferenceChange(Preference preference, Object newValue) { - // Previously we could set summary to default value, - // but now it's needed to display selected entry - preference.setSummary("%s"); - getActivity().setResult(RESULT_OK); - return true; - } - }; + private Preference.OnPreferenceChangeListener preferenceChangeListener = + new Preference.OnPreferenceChangeListener() { + @Override public boolean onPreferenceChange(Preference preference, Object newValue) { + // Previously we could set summary to default value, + // but now it's needed to display selected entry + preference.setSummary("%s"); + getActivity().setResult(RESULT_OK); + return true; + } + }; } } 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 6f5e624..b89eb62 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 @@ -7,9 +7,11 @@ import android.widget.ListView; import com.blogspot.e_kanivets.moneytracker.R; +import com.blogspot.e_kanivets.moneytracker.activity.account.edit.EditAccountActivity; import com.blogspot.e_kanivets.moneytracker.activity.base.BaseBackActivity; import com.blogspot.e_kanivets.moneytracker.adapter.AccountAdapter; import com.blogspot.e_kanivets.moneytracker.controller.data.AccountController; +import com.blogspot.e_kanivets.moneytracker.entity.data.Account; import com.blogspot.e_kanivets.moneytracker.ui.presenter.AccountsSummaryPresenter; import com.blogspot.e_kanivets.moneytracker.util.AnswersProxy; @@ -20,36 +22,30 @@ import butterknife.OnItemClick; public class AccountsActivity extends BaseBackActivity { - @SuppressWarnings("unused") - private static final String TAG = "AccountsActivity"; + @SuppressWarnings("unused") private static final String TAG = "AccountsActivity"; private static final int REQUEST_ADD_ACCOUNT = 1; private static final int REQUEST_TRANSFER = 2; private static final int REQUEST_EDIT_ACCOUNT = 3; - @Inject - AccountController accountController; + @Inject AccountController accountController; private AccountsSummaryPresenter summaryPresenter; - @BindView(R.id.list_view) - ListView listView; + @BindView(R.id.list_view) ListView listView; - @Override - protected int getContentViewId() { + @Override protected int getContentViewId() { return R.layout.activity_accounts; } - @Override - protected boolean initData() { + @Override protected boolean initData() { boolean result = super.initData(); getAppComponent().inject(AccountsActivity.this); summaryPresenter = new AccountsSummaryPresenter(AccountsActivity.this); return result; } - @Override - protected void initViews() { + @Override protected void initViews() { super.initViews(); listView.addHeaderView(summaryPresenter.create()); @@ -58,14 +54,12 @@ protected void initViews() { update(); } - @Override - public boolean onCreateOptionsMenu(Menu menu) { + @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_accounts, menu); return true; } - @Override - public boolean onOptionsItemSelected(MenuItem item) { + @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_transfer: makeTransfer(); @@ -76,28 +70,23 @@ public boolean onOptionsItemSelected(MenuItem item) { } } - @OnItemClick(R.id.list_view) - public void onAccountClick(int position) { - Intent intent = new Intent(this, EditAccountActivity.class); - intent.putExtra(EditAccountActivity.KEY_ACCOUNT, accountController.readAll().get(position - 1)); - startActivityForResult(intent, REQUEST_EDIT_ACCOUNT); + @OnItemClick(R.id.list_view) public void onAccountClick(int position) { + Account account = accountController.readAll().get(position - 1); + startActivityForResult(EditAccountActivity.Companion.newIntent(this, account), REQUEST_EDIT_ACCOUNT); } public void makeTransfer() { AnswersProxy.get().logButton("Add Transfer"); - startActivityForResult(new Intent(AccountsActivity.this, TransferActivity.class), - REQUEST_TRANSFER); + startActivityForResult(new Intent(AccountsActivity.this, TransferActivity.class), REQUEST_TRANSFER); } - @OnClick(R.id.btn_add_account) - public void addAccount() { + @OnClick(R.id.btn_add_account) public void addAccount() { AnswersProxy.get().logButton("Add Account"); Intent intent = new Intent(AccountsActivity.this, AddAccountActivity.class); startActivityForResult(intent, REQUEST_ADD_ACCOUNT); } - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { + @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (resultCode == AppCompatActivity.RESULT_OK) { diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/EditAccountActivity.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/EditAccountActivity.java deleted file mode 100644 index 2860d54..0000000 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/EditAccountActivity.java +++ /dev/null @@ -1,139 +0,0 @@ -package com.blogspot.e_kanivets.moneytracker.activity.account; - -import android.app.AlertDialog; -import android.content.DialogInterface; -import android.support.design.widget.TextInputLayout; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.EditText; - -import com.blogspot.e_kanivets.moneytracker.R; -import com.blogspot.e_kanivets.moneytracker.activity.base.BaseBackActivity; -import com.blogspot.e_kanivets.moneytracker.controller.data.AccountController; -import com.blogspot.e_kanivets.moneytracker.entity.data.Account; - -import javax.inject.Inject; - -import butterknife.BindView; -import butterknife.OnClick; - -public class EditAccountActivity extends BaseBackActivity { - - public static final String KEY_ACCOUNT = "key_account"; - - @Inject - AccountController accountController; - - private Account account; - - @BindView(R.id.til_title) - TextInputLayout tilTitle; - @BindView(R.id.et_title) - EditText etTitle; - @BindView(R.id.et_goal) - EditText etGoal; - @BindView(R.id.view_color) - View viewColor; - - @Override - protected int getContentViewId() { - return R.layout.activity_edit_account; - } - - @Override - protected boolean initData() { - getAppComponent().inject(EditAccountActivity.this); - account = getIntent().getParcelableExtra(KEY_ACCOUNT); - return account != null && super.initData(); - } - - @Override - protected void initViews() { - super.initViews(); - - etTitle.setText(account.getTitle()); - etGoal.setText(Double.toString(account.getGoal())); - viewColor.setBackgroundColor(account.getColor()); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - if (account.isArchived()) { - getMenuInflater().inflate(R.menu.menu_archived_account, menu); - } else { - getMenuInflater().inflate(R.menu.menu_account, menu); - } - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.action_archive: - archive(); - return true; - - case R.id.action_restore: - restore(); - return true; - - case R.id.action_delete: - delete(); - return true; - - default: - return super.onOptionsItemSelected(item); - } - } - - @OnClick(R.id.fabDone) - void done() { - String title = etTitle.getText().toString().trim(); - - if (title.isEmpty()) { - tilTitle.setError(getString(R.string.field_cant_be_empty)); - } else { - Account newAccount = new Account(account.getId(), title, account.getCurSum(), - account.getCurrency(), account.getGoal(), account.isArchived(), account.getColor()); - boolean updated = accountController.update(newAccount) != null; - if (updated) { - setResult(RESULT_OK); - finish(); - } - } - } - - private void archive() { - if (account.equals(accountController.readDefaultAccount())) { - showToast(R.string.cant_archive_default_account); - } else { - accountController.archive(account); - setResult(RESULT_OK); - finish(); - } - } - - private void restore() { - accountController.restore(account); - setResult(RESULT_OK); - finish(); - } - - private void delete() { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.delete_account_title); - builder.setMessage(R.string.delete_account_message); - builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - accountController.delete(account); - setResult(RESULT_OK); - finish(); - } - }); - builder.setNegativeButton(android.R.string.cancel, null); - builder.show(); - } - -} diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/edit/EditAccountActivity.kt b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/edit/EditAccountActivity.kt new file mode 100644 index 0000000..3f10199 --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/edit/EditAccountActivity.kt @@ -0,0 +1,124 @@ +package com.blogspot.e_kanivets.moneytracker.activity.account.edit + +import android.app.Activity +import android.app.AlertDialog +import android.content.Context +import android.content.Intent +import android.support.v4.view.ViewPager.OnPageChangeListener +import android.view.Menu +import android.view.MenuItem +import com.blogspot.e_kanivets.moneytracker.R +import com.blogspot.e_kanivets.moneytracker.activity.account.edit.fragment.AccountOperationsFragment +import com.blogspot.e_kanivets.moneytracker.activity.account.edit.fragment.EditAccountFragment +import com.blogspot.e_kanivets.moneytracker.activity.base.BaseBackActivity +import com.blogspot.e_kanivets.moneytracker.adapter.GeneralViewPagerAdapter +import com.blogspot.e_kanivets.moneytracker.controller.data.AccountController +import com.blogspot.e_kanivets.moneytracker.entity.data.Account +import kotlinx.android.synthetic.main.activity_edit_account.fabDone +import kotlinx.android.synthetic.main.activity_edit_account.tabLayout +import kotlinx.android.synthetic.main.activity_edit_account.viewPager +import javax.inject.Inject + +class EditAccountActivity : BaseBackActivity() { + + @Inject + internal lateinit var accountController: AccountController + + private lateinit var account: Account + + override fun getContentViewId(): Int = R.layout.activity_edit_account + + override fun initData(): Boolean { + appComponent.inject(this@EditAccountActivity) + val accountFromParcel: Account? = intent.getParcelableExtra(KEY_ACCOUNT) + + return if (accountFromParcel == null) false + else { + account = accountFromParcel + super.initData() + } + } + + override fun initViews() { + super.initViews() + + tabLayout.setupWithViewPager(viewPager) + + val adapter = GeneralViewPagerAdapter(supportFragmentManager) + adapter.addFragment(EditAccountFragment.newInstance(account), getString(R.string.information)) + adapter.addFragment(AccountOperationsFragment.newInstance(account), getString(R.string.operations)) + viewPager.adapter = adapter + + viewPager.addOnPageChangeListener(object : OnPageChangeListener { + override fun onPageScrollStateChanged(state: Int) {} + + override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {} + + override fun onPageSelected(position: Int) { + if (position == 0) fabDone.show() else fabDone.hide() + } + + }) + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(if (account.isArchived) R.menu.menu_archived_account else R.menu.menu_account, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.action_archive -> archive() + R.id.action_restore -> restore() + R.id.action_delete -> delete() + else -> super.onOptionsItemSelected(item) + } + } + + private fun archive(): Boolean { + if (account == accountController.readDefaultAccount()) { + showToast(R.string.cant_archive_default_account) + } else { + accountController.archive(account) + setResult(Activity.RESULT_OK) + finish() + } + + return true + } + + private fun restore(): Boolean { + accountController.restore(account) + setResult(Activity.RESULT_OK) + finish() + + return true + } + + private fun delete(): Boolean { + val builder = AlertDialog.Builder(this) + builder.setTitle(R.string.delete_account_title) + builder.setMessage(R.string.delete_account_message) + builder.setPositiveButton(android.R.string.ok) { _, _ -> + accountController.delete(account) + setResult(Activity.RESULT_OK) + finish() + } + builder.setNegativeButton(android.R.string.cancel, null) + builder.show() + + return true + } + + companion object { + + private const val KEY_ACCOUNT = "key_account" + + fun newIntent(context: Context, account: Account): Intent { + val intent = Intent(context, EditAccountActivity::class.java) + intent.putExtra(KEY_ACCOUNT, account) + return intent + } + } + +} 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 new file mode 100644 index 0000000..2d189d7 --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/edit/fragment/AccountOperationsFragment.kt @@ -0,0 +1,86 @@ +package com.blogspot.e_kanivets.moneytracker.activity.account.edit.fragment + +import android.os.Bundle +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.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.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 kotlinx.android.synthetic.main.fragment_account_operations.* +import javax.inject.Inject + +class AccountOperationsFragment : BaseFragment() { + + @Inject + internal lateinit var accountController: AccountController + @Inject + internal lateinit var recordController: RecordController + @Inject + internal lateinit var transferController: TransferController + + private lateinit var account: Account + + override val contentViewId: Int = R.layout.fragment_account_operations + + override fun initData() { + appComponent.inject(this@AccountOperationsFragment) + arguments?.let { arguments -> account = arguments.getParcelable(AccountOperationsFragment.KEY_ACCOUNT) } + } + + override fun initViews(view: View) { + listView.adapter = RecordAdapter(activity, getRecords()) + } + + private fun getRecords(): List { + val accountRecords = recordController.getRecordsForAccount(account) + val accountTransfers = transferController.getTransfersForAccount(account) + + accountRecords += obtainRecordsFromTransfers(accountTransfers) + accountRecords.sortByDescending { it.time } + + return accountRecords + } + + private fun obtainRecordsFromTransfers(transfers: List): List { + val records = mutableListOf() + + transfers.forEach { + val type = if (it.fromAccountId == account.id) Record.TYPE_EXPENSE else Record.TYPE_INCOME + val title = constructRecordTitle(type, it) + val category = Category(getString(R.string.transfer).toLowerCase()) + val price = if (type == Record.TYPE_EXPENSE) it.fromAmount else it.toAmount + val decimals = if (type == Record.TYPE_EXPENSE) it.fromDecimals else it.toDecimals + + records += Record(it.id, it.time, type, title, category, price, account, account.currency, decimals) + } + + return records.toList() + } + + private fun constructRecordTitle(type: Int, transfer: Transfer): String { + val titlePrefix = getString(if (type == Record.TYPE_EXPENSE) R.string.to else R.string.from) + val oppositeAccountId = if (type == Record.TYPE_EXPENSE) transfer.toAccountId else transfer.fromAccountId + val oppositeAccountTitle = "$titlePrefix ${accountController.read(oppositeAccountId)?.title}" + return "${getString(R.string.transfer)} $oppositeAccountTitle".toLowerCase() + } + + companion object { + + private const val KEY_ACCOUNT = "key_account" + + fun newInstance(account: Account): AccountOperationsFragment { + val fragment = AccountOperationsFragment() + val arguments = Bundle() + arguments.putParcelable(KEY_ACCOUNT, account) + fragment.arguments = arguments + return fragment + } + + } +} diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/edit/fragment/EditAccountFragment.kt b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/edit/fragment/EditAccountFragment.kt new file mode 100644 index 0000000..9d0fc17 --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/account/edit/fragment/EditAccountFragment.kt @@ -0,0 +1,81 @@ +package com.blogspot.e_kanivets.moneytracker.activity.account.edit.fragment + +import android.app.Activity +import android.os.Bundle +import android.support.design.widget.FloatingActionButton +import android.view.View +import com.blogspot.e_kanivets.moneytracker.R +import com.blogspot.e_kanivets.moneytracker.R.layout +import com.blogspot.e_kanivets.moneytracker.activity.base.BaseFragment +import com.blogspot.e_kanivets.moneytracker.controller.FormatController +import com.blogspot.e_kanivets.moneytracker.controller.data.AccountController +import com.blogspot.e_kanivets.moneytracker.entity.data.Account +import com.blogspot.e_kanivets.moneytracker.util.AnswersProxy +import com.blogspot.e_kanivets.moneytracker.util.validator.EditAccountValidator +import com.blogspot.e_kanivets.moneytracker.util.validator.IValidator +import kotlinx.android.synthetic.main.fragment_edit_account.* +import javax.inject.Inject + +class EditAccountFragment : BaseFragment() { + + @Inject + internal lateinit var accountController: AccountController + @Inject + internal lateinit var formatController: FormatController + + private lateinit var accountValidator: IValidator + + private lateinit var account: Account + + override val contentViewId: Int = layout.fragment_edit_account + + override fun initData() { + appComponent.inject(this@EditAccountFragment) + arguments?.let { arguments -> account = arguments.getParcelable(KEY_ACCOUNT) } + } + + override fun initViews(view: View) { + etTitle.setText(account.title) + etGoal.setText(formatController.formatPrecisionNone(account.goal)) + viewColor.setBackgroundColor(account.color) + + val fabDone = view.rootView.findViewById(R.id.fabDone) + fabDone.setOnClickListener { done() } + + accountValidator = EditAccountValidator(context!!, view) + } + + private fun done() { + AnswersProxy.get().logButton("Edit Account") + if (accountValidator.validate()) { + val title = etTitle.text.toString().trim { it <= ' ' } + val goal = etGoal.text.toString().toDouble() + + val newAccount = Account( + account.id, title, account.curSum.toDouble(), + account.currency, goal, account.isArchived, account.color + ) + val updated = accountController.update(newAccount) != null + if (updated) { + AnswersProxy.get().logEvent("Edit Account") + activity?.setResult(Activity.RESULT_OK) + activity?.finish() + } + } + } + + companion object { + + private const val KEY_ACCOUNT = "key_account" + + fun newInstance(account: Account): EditAccountFragment { + val fragment = EditAccountFragment() + val arguments = Bundle() + arguments.putParcelable(KEY_ACCOUNT, account) + fragment.arguments = arguments + return fragment + } + + } + +} diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/base/BaseFragment.kt b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/base/BaseFragment.kt new file mode 100644 index 0000000..d3d5a73 --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/base/BaseFragment.kt @@ -0,0 +1,33 @@ +package com.blogspot.e_kanivets.moneytracker.activity.base + +import android.os.Bundle +import android.support.v4.app.Fragment +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import com.blogspot.e_kanivets.moneytracker.MtApp +import com.blogspot.e_kanivets.moneytracker.di.AppComponent + +abstract class BaseFragment : Fragment() { + + protected val appComponent: AppComponent = MtApp.get().appComponent + + protected abstract val contentViewId: Int + + protected abstract fun initData() + + protected abstract fun initViews(view: View) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + initData() + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = + inflater.inflate(contentViewId, container, false) + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initViews(view) + } +} diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/charts/ChartsActivity.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/charts/ChartsActivity.java index 918cb11..16b6136 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/charts/ChartsActivity.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/charts/ChartsActivity.java @@ -1,16 +1,14 @@ package com.blogspot.e_kanivets.moneytracker.activity.charts; -import android.graphics.Color; import android.support.design.widget.TabLayout; import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentPagerAdapter; import android.support.v4.view.ViewPager; import com.blogspot.e_kanivets.moneytracker.R; import com.blogspot.e_kanivets.moneytracker.activity.base.BaseBackActivity; import com.blogspot.e_kanivets.moneytracker.activity.charts.fragment.GraphFragment; import com.blogspot.e_kanivets.moneytracker.activity.charts.fragment.SummaryFragment; +import com.blogspot.e_kanivets.moneytracker.adapter.GeneralViewPagerAdapter; 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; @@ -18,7 +16,6 @@ import com.blogspot.e_kanivets.moneytracker.report.ReportMaker; import com.blogspot.e_kanivets.moneytracker.report.chart.IMonthReport; -import java.util.ArrayList; import java.util.List; import javax.inject.Inject; @@ -27,32 +24,24 @@ public class ChartsActivity extends BaseBackActivity { - @Inject - RecordController recordController; - @Inject - ExchangeRateController exchangeRateController; - @Inject - CurrencyController currencyController; + @Inject RecordController recordController; + @Inject ExchangeRateController exchangeRateController; + @Inject CurrencyController currencyController; - @BindView(R.id.tabs) - TabLayout tabLayout; - @BindView(R.id.view_pager) - ViewPager viewPager; + @BindView(R.id.tabs) TabLayout tabLayout; + @BindView(R.id.view_pager) ViewPager viewPager; - @Override - protected int getContentViewId() { + @Override protected int getContentViewId() { return R.layout.activity_charts; } - @Override - protected boolean initData() { + @Override protected boolean initData() { boolean result = super.initData(); getAppComponent().inject(ChartsActivity.this); return result; } - @Override - protected void initViews() { + @Override protected void initViews() { super.initViews(); setupViewPager(viewPager); @@ -85,38 +74,9 @@ private void setupViewPager(ViewPager viewPager) { graphFragment = GraphFragment.newInstance(monthReport); } - ViewPagerAdapter adapter = new ViewPagerAdapter(getSupportFragmentManager()); - adapter.addFragment(SummaryFragment.newInstance(monthReport), "Summary"); - adapter.addFragment(graphFragment, "Graph"); + GeneralViewPagerAdapter adapter = new GeneralViewPagerAdapter(getSupportFragmentManager()); + adapter.addFragment(SummaryFragment.newInstance(monthReport), getString(R.string.summary)); + adapter.addFragment(graphFragment, getString(R.string.graph)); viewPager.setAdapter(adapter); } - - class ViewPagerAdapter extends FragmentPagerAdapter { - private final List mFragmentList = new ArrayList<>(); - private final List mFragmentTitleList = new ArrayList<>(); - - ViewPagerAdapter(FragmentManager manager) { - super(manager); - } - - @Override - public Fragment getItem(int position) { - return mFragmentList.get(position); - } - - @Override - public int getCount() { - return mFragmentList.size(); - } - - void addFragment(Fragment fragment, String title) { - mFragmentList.add(fragment); - mFragmentTitleList.add(title); - } - - @Override - public CharSequence getPageTitle(int position) { - return mFragmentTitleList.get(position); - } - } } diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/external/BackupActivity.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/external/BackupActivity.java index 8ea8e97..ca078be 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/external/BackupActivity.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/external/BackupActivity.java @@ -4,13 +4,13 @@ import android.support.annotation.NonNull; import android.support.v7.app.AlertDialog; import android.view.View; -import android.widget.ArrayAdapter; import android.widget.ListView; 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.controller.BackupController; +import com.blogspot.e_kanivets.moneytracker.adapter.BackupAdapter; +import com.blogspot.e_kanivets.moneytracker.controller.backup.BackupController; import com.blogspot.e_kanivets.moneytracker.controller.PreferenceController; import com.blogspot.e_kanivets.moneytracker.util.AnswersProxy; import com.dropbox.core.DbxRequestConfig; @@ -24,51 +24,48 @@ import butterknife.BindView; import butterknife.OnClick; import butterknife.OnItemClick; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import timber.log.Timber; -public class BackupActivity extends BaseBackActivity { +public class BackupActivity extends BaseBackActivity + implements BackupAdapter.OnBackupListener, BackupController.OnBackupListener { private static final String APP_KEY = "5lqugcckdy9y6lj"; - @Inject - PreferenceController preferenceController; - @Inject - BackupController backupController; + @Inject PreferenceController preferenceController; + @Inject BackupController backupController; private DbxClientV2 dbClient; - @BindView(R.id.btn_backup_now) - View btnBackupNow; - @BindView(R.id.list_view) - ListView listView; + @BindView(R.id.btn_backup_now) View btnBackupNow; + @BindView(R.id.list_view) ListView listView; - @Override - protected int getContentViewId() { + @Override protected int getContentViewId() { return R.layout.activity_backup; } - @Override - protected boolean initData() { + @Override protected boolean initData() { getAppComponent().inject(BackupActivity.this); String accessToken = preferenceController.readDropboxAccessToken(); - if (accessToken == null) Auth.startOAuth2Authentication(BackupActivity.this, APP_KEY); - else { + if (accessToken == null) { + Auth.startOAuth2Authentication(BackupActivity.this, APP_KEY); + } else { DbxRequestConfig config = new DbxRequestConfig("open_money_tracker"); dbClient = new DbxClientV2(config, accessToken); + backupController.setOnBackupListener(this); fetchBackups(); } return super.initData(); } - @Override - protected void initViews() { + @Override protected void initViews() { super.initViews(); btnBackupNow.setEnabled(preferenceController.readDropboxAccessToken() != null); } - @Override - protected void onResume() { + @Override protected void onResume() { super.onResume(); if (Auth.getOAuth2Token() != null) { @@ -84,37 +81,106 @@ protected void onResume() { } } - @OnClick(R.id.btn_backup_now) - public void backupNow() { - AnswersProxy.get().logButton("Make Backup"); - startProgress(getString(R.string.making_backup)); - backupController.makeBackup(dbClient, new BackupController.OnBackupListener() { - @Override - public void onBackupSuccess() { - AnswersProxy.get().logEvent("Backup success"); - Timber.d("Backup success."); - if (isFinishing()) return; - - stopProgress(); - fetchBackups(); + @Override public void onBackupDelete(@NotNull final String backupName) { + AlertDialog.Builder builder = new AlertDialog.Builder(BackupActivity.this); + builder.setTitle(getString(R.string.delete_backup_title)); + builder.setMessage(getString(R.string.delete_backup_message, backupName)); + builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { + @Override public void onClick(DialogInterface dialog, int which) { + removeBackup(backupName); } + }); + builder.setNegativeButton(android.R.string.cancel, null); + builder.show(); + } - @Override - public void onBackupFailure(String reason) { - AnswersProxy.get().logEvent("Backup failure"); - Timber.d("Backup failure."); - if (isFinishing()) return; + @Override public void onBackupsFetched(@NonNull List backupList) { + if (isFinishing()) return; + + stopProgress(); + + BackupAdapter backupAdapter = new BackupAdapter(BackupActivity.this, backupList); + backupAdapter.setOnBackupListener(BackupActivity.this); + listView.setAdapter(backupAdapter); + } - stopProgress(); - showToast(R.string.failed_create_backup); + @Override public void onBackupSuccess() { + AnswersProxy.get().logEvent("Backup success"); + Timber.d("Backup success."); + if (isFinishing()) return; - if (BackupController.OnBackupListener.ERROR_AUTHENTICATION.equals(reason)) logout(); + stopProgress(); + fetchBackups(); + } + + @Override public void onBackupFailure(String reason) { + AnswersProxy.get().logEvent("Backup failure"); + Timber.d("Backup failure."); + if (isFinishing()) return; + + stopProgress(); + showToast(R.string.failed_create_backup); + + if (BackupController.OnBackupListener.ERROR_AUTHENTICATION.equals(reason)) logout(); + } + + @Override public void onRestoreSuccess(@NonNull String backupName) { + AnswersProxy.get().logEvent("Restore Success"); + Timber.d("Restore success."); + if (isFinishing()) return; + + stopProgress(); + + AlertDialog.Builder builder = new AlertDialog.Builder(BackupActivity.this); + builder.setTitle(getString(R.string.backup_is_restored)); + builder.setMessage(getString(R.string.backup_restored, backupName)); + builder.setOnDismissListener(new DialogInterface.OnDismissListener() { + @Override public void onDismiss(DialogInterface dialog) { + MtApp.get().buildAppComponent(); + setResult(RESULT_OK); + finish(); } }); + builder.setPositiveButton(android.R.string.ok, null); + builder.show(); + } + + @Override public void onRestoreFailure(String reason) { + AnswersProxy.get().logEvent("Restore Failure"); + Timber.d("Restore failure."); + if (isFinishing()) return; + + stopProgress(); + showToast(R.string.failed_restore_backup); + + if (BackupController.OnBackupListener.ERROR_AUTHENTICATION.equals(reason)) logout(); + } + + @Override public void onRemoveSuccess() { + AnswersProxy.get().logEvent("Remove Success"); + Timber.d("Remove success."); + if (isFinishing()) return; + + stopProgress(); + fetchBackups(); } - @OnItemClick(R.id.list_view) - public void restoreBackupClicked(int position) { + @Override public void onRemoveFailure(@Nullable String reason) { + AnswersProxy.get().logEvent("Remove Failure"); + Timber.d("Remove failure."); + if (isFinishing()) return; + + stopProgress(); + showToast(reason); + } + + @OnClick(R.id.btn_backup_now) public void backupNow() { + AnswersProxy.get().logButton("Make Backup"); + startProgress(getString(R.string.making_backup)); + backupController.makeBackup(dbClient); + } + + @OnItemClick(R.id.list_view) public void restoreBackupClicked(int position) { AnswersProxy.get().logButton("Restore backup"); final String backupName = listView.getAdapter().getItem(position).toString(); @@ -122,8 +188,7 @@ public void restoreBackupClicked(int position) { builder.setTitle(getString(R.string.warning)); builder.setMessage(getString(R.string.want_erase_and_restore, backupName)); builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { + @Override public void onClick(DialogInterface dialog, int which) { restoreBackup(backupName); } }); @@ -133,58 +198,17 @@ public void onClick(DialogInterface dialog, int which) { private void restoreBackup(final String backupName) { startProgress(getString(R.string.restoring_backup)); - backupController.restoreBackup(dbClient, backupName, new BackupController.OnRestoreBackupListener() { - @Override - public void onRestoreSuccess() { - AnswersProxy.get().logEvent("Restore Success"); - Timber.d("Restore success."); - if (isFinishing()) return; - - stopProgress(); - - AlertDialog.Builder builder = new AlertDialog.Builder(BackupActivity.this); - builder.setTitle(getString(R.string.backup_is_restored)); - builder.setMessage(getString(R.string.backup_restored, backupName)); - builder.setOnDismissListener(new DialogInterface.OnDismissListener() { - @Override - public void onDismiss(DialogInterface dialog) { - MtApp.get().buildAppComponent(); - setResult(RESULT_OK); - finish(); - } - }); - builder.setPositiveButton(android.R.string.ok, null); - builder.show(); - } - - @Override - public void onRestoreFailure(String reason) { - AnswersProxy.get().logEvent("Restore Failure"); - Timber.d("Restore failure."); - if (isFinishing()) return; - - stopProgress(); - showToast(R.string.failed_restore_backup); - - if (BackupController.OnRestoreBackupListener.ERROR_AUTHENTICATION.equals(reason)) - logout(); - } - }); + backupController.restoreBackup(dbClient, backupName); } private void fetchBackups() { startProgress(getString(R.string.fetching_backups)); - backupController.fetchBackups(dbClient, new BackupController.OnFetchBackupListListener() { - @Override - public void onBackupsFetched(@NonNull List backupList) { - if (isFinishing()) return; - - stopProgress(); - ArrayAdapter adapter = new ArrayAdapter<>(BackupActivity.this, - android.R.layout.simple_list_item_1, backupList); - listView.setAdapter(adapter); - } - }); + backupController.fetchBackups(dbClient); + } + + private void removeBackup(String backupName) { + startProgress(getString(R.string.removing_backup)); + backupController.removeBackup(dbClient, backupName); } private void logout() { diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/record/MainActivity.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/record/MainActivity.java index 57dc083..3a155f8 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/record/MainActivity.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/activity/record/MainActivity.java @@ -94,14 +94,12 @@ protected void initViews() { setTitle(R.string.title_records); - tvDefaultAccountTitle = (TextView) navigationView.getHeaderView(0).findViewById(R.id.tv_default_account_title); - tvDefaultAccountSum = (TextView) navigationView.getHeaderView(0).findViewById(R.id.tv_default_account_sum); - tvCurrency = (TextView) navigationView.getHeaderView(0).findViewById(R.id.tv_currency); + tvDefaultAccountTitle = navigationView.getHeaderView(0).findViewById(R.id.tv_default_account_title); + tvDefaultAccountSum = navigationView.getHeaderView(0).findViewById(R.id.tv_default_account_sum); + tvCurrency = navigationView.getHeaderView(0).findViewById(R.id.tv_currency); if (preferenceController.checkRateDialog()) showAppRateDialog(); - //registerForContextMenu(listView); - View summaryView = summaryPresenter.create(true); listView.addHeaderView(summaryView); summaryView.setOnClickListener(new View.OnClickListener() { diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/BackupAdapter.kt b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/BackupAdapter.kt new file mode 100644 index 0000000..1b8e85d --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/BackupAdapter.kt @@ -0,0 +1,51 @@ +package com.blogspot.e_kanivets.moneytracker.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.BaseAdapter +import com.blogspot.e_kanivets.moneytracker.R +import kotlinx.android.synthetic.main.view_backup_item.view.ivDelete +import kotlinx.android.synthetic.main.view_backup_item.view.tvTitle + +class BackupAdapter(private val context: Context, private val backups: List) : BaseAdapter() { + + var onBackupListener: OnBackupListener? = null + + override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View { + var view = convertView + val viewHolder: ViewHolder? + + if (view == null) { + val layoutInflater = LayoutInflater.from(context) + + view = layoutInflater.inflate(R.layout.view_backup_item, parent, false) + viewHolder = ViewHolder(view) + + view.tag = viewHolder + } else { + viewHolder = view.tag as ViewHolder + } + + val backupItem = getItem(position) + + viewHolder.view.tvTitle.text = backupItem + viewHolder.view.ivDelete.setOnClickListener { onBackupListener?.onBackupDelete(backupItem) } + + return view!! + } + + override fun getItem(position: Int): String = backups[position] + + override fun getItemId(position: Int): Long = position.toLong() + + override fun getCount(): Int = backups.size + + private data class ViewHolder(val view: View) + + interface OnBackupListener { + fun onBackupDelete(backupName: String) + } + +} diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/GeneralViewPagerAdapter.kt b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/GeneralViewPagerAdapter.kt new file mode 100644 index 0000000..68325c7 --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/GeneralViewPagerAdapter.kt @@ -0,0 +1,29 @@ +package com.blogspot.e_kanivets.moneytracker.adapter + +import android.support.v4.app.Fragment +import android.support.v4.app.FragmentManager +import android.support.v4.app.FragmentPagerAdapter +import java.util.ArrayList + +class GeneralViewPagerAdapter(manager: FragmentManager) : FragmentPagerAdapter(manager) { + private val fragmentList = ArrayList() + private val fragmentTitleList = ArrayList() + + override fun getItem(position: Int): Fragment { + return fragmentList[position] + } + + override fun getCount(): Int { + return fragmentList.size + } + + fun addFragment(fragment: Fragment, title: String) { + fragmentList.add(fragment) + fragmentTitleList.add(title) + } + + override fun getPageTitle(position: Int): CharSequence? { + return fragmentTitleList[position] + } + +} diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/BackupController.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/BackupController.java deleted file mode 100644 index 9364329..0000000 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/BackupController.java +++ /dev/null @@ -1,273 +0,0 @@ -package com.blogspot.e_kanivets.moneytracker.controller; - -import android.os.AsyncTask; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.dropbox.core.DbxException; -import com.dropbox.core.v2.DbxClientV2; -import com.dropbox.core.v2.files.FileMetadata; -import com.dropbox.core.v2.files.Metadata; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Controller class to encapsulate backup logic. - * Created on 8/10/16. - * - * @author Evgenii Kanivets - */ -public class BackupController { - private FormatController formatController; - private String filesDir; - - public BackupController(FormatController formatController, String filesDir) { - this.formatController = formatController; - this.filesDir = filesDir; - } - - public void makeBackup(@NonNull DbxClientV2 dbClient, - @Nullable OnBackupListener listener) { - FileInputStream fileInputStream = readAppDb(); - long fileLength = readAppDbFileLength(); - if (fileInputStream == null) return; - - DropboxBackupAsyncTask asyncTask = new DropboxBackupAsyncTask(dbClient, - formatController.formatDateAndTime(System.currentTimeMillis()), - fileInputStream, fileLength, listener); - asyncTask.execute(); - } - - public void restoreBackup(@NonNull DbxClientV2 dbClient, @NonNull String backupName, - @Nullable final OnRestoreBackupListener listener) { - final File file = new File(getRestoreFileName()); - FileOutputStream outputStream = null; - try { - outputStream = new FileOutputStream(file); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } - - if (outputStream == null) { - if (listener != null) listener.onRestoreFailure(null); - } else { - final FileOutputStream finalOutputStream = outputStream; - DropboxRestoreBackupAsyncTask asyncTask = new DropboxRestoreBackupAsyncTask(dbClient, - backupName, outputStream, new OnRestoreBackupListener() { - @Override - public void onRestoreSuccess() { - try { - finalOutputStream.close(); - } catch (IOException e) { - if (listener != null) listener.onRestoreFailure(null); - e.printStackTrace(); - } - - if (file.exists() && file.length() != 0) { - boolean renamed = file.renameTo(new File(getAppDbFileName())); - if (listener != null) { - if (renamed) listener.onRestoreSuccess(); - else listener.onRestoreFailure(null); - } - } - } - - @Override - public void onRestoreFailure(String reason) { - if (listener != null) listener.onRestoreFailure(reason); - } - }); - asyncTask.execute(); - } - } - - public void fetchBackups(@NonNull DbxClientV2 dbClient, - @Nullable OnFetchBackupListListener listener) { - DropboxFetchBackupListAsyncTask asyncTask = new DropboxFetchBackupListAsyncTask(dbClient, listener); - asyncTask.execute(); - } - - @Nullable - private FileInputStream readAppDb() { - File dbFile = new File(getAppDbFileName()); - FileInputStream fis = null; - - try { - fis = new FileInputStream(dbFile); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } - - return fis; - } - - private long readAppDbFileLength() { - File dbFile = new File(getAppDbFileName()); - - if (dbFile.exists()) return dbFile.length(); - else return 0; - } - - @NonNull - private String getAppDbFileName() { - return filesDir + "/databases/database"; - } - - @NonNull - private String getRestoreFileName() { - return getAppDbFileName() + ".restore"; - } - - private static class DropboxBackupAsyncTask extends AsyncTask { - private DbxClientV2 dbClient; - private String fileName; - private FileInputStream fileInputStream; - private long fileLength; - - @Nullable - private OnBackupListener listener; - - public DropboxBackupAsyncTask(@NonNull DbxClientV2 dbClient, String fileName, - FileInputStream fileInputStream, long fileLength, - @Nullable OnBackupListener listener) { - this.dbClient = dbClient; - this.fileName = fileName; - this.fileInputStream = fileInputStream; - this.fileLength = fileLength; - this.listener = listener; - } - - @Override - protected String doInBackground(Void... params) { - FileMetadata info = null; - - try { - info = dbClient.files().upload("/" + fileName).uploadAndFinish(fileInputStream); - } catch (DbxException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); - } - - if (info == null) return null; - else return OnBackupListener.SUCCESS; - } - - @Override - protected void onPostExecute(String result) { - super.onPostExecute(result); - if (listener == null) return; - - if (OnBackupListener.SUCCESS.equals(result)) listener.onBackupSuccess(); - else listener.onBackupFailure(result); - } - } - - private static class DropboxFetchBackupListAsyncTask extends AsyncTask, List> { - private DbxClientV2 dbClient; - - @Nullable - private OnFetchBackupListListener listener; - - public DropboxFetchBackupListAsyncTask(DbxClientV2 dbClient, - @Nullable OnFetchBackupListListener listener) { - this.dbClient = dbClient; - this.listener = listener; - } - - @Override - protected List doInBackground(Void... params) { - List entryList = new ArrayList<>(); - List backupList = new ArrayList<>(); - - try { - entryList = dbClient.files().listFolder("").getEntries(); - } catch (DbxException e) { - e.printStackTrace(); - } - - for (Metadata entry : entryList) { - backupList.add(entry.getName()); - } - - return backupList; - } - - @Override - protected void onPostExecute(List backupList) { - super.onPostExecute(backupList); - if (listener == null) return; - - Collections.reverse(backupList); - listener.onBackupsFetched(backupList); - } - } - - private static class DropboxRestoreBackupAsyncTask extends AsyncTask { - private DbxClientV2 dbClient; - private String backupName; - private FileOutputStream outputStream; - - @Nullable - private OnRestoreBackupListener listener; - - public DropboxRestoreBackupAsyncTask(DbxClientV2 dbClient, String backupName, - FileOutputStream outputStream, - @Nullable OnRestoreBackupListener listener) { - this.dbClient = dbClient; - this.backupName = backupName; - this.outputStream = outputStream; - this.listener = listener; - } - - @Override - protected String doInBackground(Void... params) { - FileMetadata info = null; - try { - info = dbClient.files().download("/" + backupName).download(outputStream); - } catch (DbxException | IOException e) { - e.printStackTrace(); - } - - if (info == null) return null; - else return OnBackupListener.SUCCESS; - } - - @Override - protected void onPostExecute(String result) { - super.onPostExecute(result); - if (listener == null) return; - - if (OnBackupListener.SUCCESS.equals(result)) listener.onRestoreSuccess(); - else listener.onRestoreFailure(result); - } - } - - public interface OnBackupListener { - String SUCCESS = "success"; - String ERROR_AUTHENTICATION = "error_authentication"; - - void onBackupSuccess(); - - void onBackupFailure(String reason); - } - - public interface OnFetchBackupListListener { - void onBackupsFetched(@NonNull List backupList); - } - - public interface OnRestoreBackupListener { - String ERROR_AUTHENTICATION = "error_authentication"; - - void onRestoreSuccess(); - - void onRestoreFailure(String reason); - } -} diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/CurrencyController.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/CurrencyController.java index adbae7a..706d2e8 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/CurrencyController.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/CurrencyController.java @@ -25,8 +25,7 @@ public class CurrencyController { private AccountController accountController; private PreferenceController preferenceController; - @NonNull - private List currencyList; + @NonNull private List currencyList; public CurrencyController(AccountController accountController, PreferenceController preferenceController) { this.accountController = accountController; @@ -34,13 +33,11 @@ public CurrencyController(AccountController accountController, PreferenceControl currencyList = fetchCurrencies(); } - @NonNull - public List readAll() { + @NonNull public List readAll() { return currencyList; } - @NonNull - public String readDefaultCurrency() { + @NonNull public String readDefaultCurrency() { // First of all read from Prefs String currency = preferenceController.readDefaultCurrency(); @@ -54,8 +51,7 @@ public String readDefaultCurrency() { return currency; } - @NonNull - private List fetchCurrencies() { + @NonNull private List fetchCurrencies() { Set toret = new HashSet<>(); Locale[] locs = Locale.getAvailableLocales(); @@ -67,13 +63,15 @@ private List fetchCurrencies() { } } - List currencyList = new ArrayList<>(); + List currencySet = new ArrayList<>(); for (Currency currency : toret) { - currencyList.add(currency.getCurrencyCode()); + currencySet.add(currency.getCurrencyCode()); } - currencyList.add(DbHelper.DEFAULT_ACCOUNT_CURRENCY); - currencyList.add("BYN"); // New belorussian ruble + currencySet.add(DbHelper.DEFAULT_ACCOUNT_CURRENCY); + currencySet.add("BYN"); // New belorussian ruble + + currencyList = new ArrayList<>(currencySet); Collections.sort(currencyList); diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/PreferenceController.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/PreferenceController.java index 477e5e9..16d9d14 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/PreferenceController.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/PreferenceController.java @@ -8,6 +8,7 @@ import com.blogspot.e_kanivets.moneytracker.R; +import com.blogspot.e_kanivets.moneytracker.repo.DbHelper; import java.util.HashSet; import java.util.Map; import java.util.Set; @@ -33,8 +34,7 @@ public class PreferenceController { private static final int RATE_PERIOD = 5; - @NonNull - private Context context; + @NonNull private Context context; public PreferenceController(@NonNull Context context) { this.context = context; @@ -113,22 +113,27 @@ public long readDefaultAccountId() { return Long.parseLong(preferences.getString(defaultAccountPref, "-1")); } - @Nullable - public String readDefaultCurrency() { + @Nullable public String readDefaultCurrency() { String defaultCurrencyPref = context.getString(R.string.pref_default_currency); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); return preferences.getString(defaultCurrencyPref, null); } - @NonNull - public String readDisplayPrecision() { + @NonNull public String readDisplayPrecision() { String displayPrecisionPref = context.getString(R.string.pref_display_precision); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); return preferences.getString(displayPrecisionPref, FormatController.PRECISION_MATH); } + @Nullable public String readNonSubstitutionCurrency() { + String nonSubstitutionCurrencyPref = context.getString(R.string.pref_non_substitution_currency); + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + + return preferences.getString(nonSubstitutionCurrencyPref, DbHelper.DEFAULT_ACCOUNT_CURRENCY); + } + public long readFirstTs() { return getDefaultPrefs().getLong(KEY_FIRST_TS, -1); } @@ -137,28 +142,23 @@ public long readLastTs() { return getDefaultPrefs().getLong(KEY_LAST_TS, -1); } - @Nullable - public String readPeriodType() { + @Nullable public String readPeriodType() { return getDefaultPrefs().getString(KEY_PERIOD_TYPE, null); } - @Nullable - public String readDropboxAccessToken() { + @Nullable public String readDropboxAccessToken() { return getDefaultPrefs().getString(KEY_DROPBOX_ACCESS_TOKEN, null); } - @NonNull - public Set readFilteredCategories() { + @NonNull public Set readFilteredCategories() { // http://stackoverflow.com/questions/14034803/misbehavior-when-trying-to-store-a-string-set-using-sharedpreferences/14034804#14034804 return new HashSet<>(getDefaultPrefs().getStringSet(KEY_FILTERED_CATEGORIES, new HashSet())); } - @NonNull - public Map readRecordTitleCategoryPairs() { + @NonNull public Map readRecordTitleCategoryPairs() { Map map = new TreeMap<>(); - Set set = getDefaultPrefs().getStringSet(KEY_RECORD_TITLE_CATEGORY_PAIRS, - new HashSet()); + Set set = getDefaultPrefs().getStringSet(KEY_RECORD_TITLE_CATEGORY_PAIRS, new HashSet()); for (String entry : set) { String[] words = entry.split(";"); if (words.length == 2) { @@ -169,13 +169,11 @@ public Map readRecordTitleCategoryPairs() { return map; } - @NonNull - private SharedPreferences getDefaultPrefs() { + @NonNull private SharedPreferences getDefaultPrefs() { return context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE); } - @NonNull - private SharedPreferences.Editor getEditor() { + @NonNull private SharedPreferences.Editor getEditor() { return getDefaultPrefs().edit(); } } diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/backup/BackupController.kt b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/backup/BackupController.kt new file mode 100644 index 0000000..a9ffb3c --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/backup/BackupController.kt @@ -0,0 +1,61 @@ +package com.blogspot.e_kanivets.moneytracker.controller.backup + +import com.blogspot.e_kanivets.moneytracker.controller.FormatController +import com.blogspot.e_kanivets.moneytracker.controller.backup.tasks.DropboxBackupAsyncTask +import com.blogspot.e_kanivets.moneytracker.controller.backup.tasks.DropboxFetchBackupListAsyncTask +import com.blogspot.e_kanivets.moneytracker.controller.backup.tasks.DropboxRemoveBackupAsyncTask +import com.blogspot.e_kanivets.moneytracker.controller.backup.tasks.DropboxRestoreBackupAsyncTask +import com.dropbox.core.v2.DbxClientV2 + +/** + * Controller class to encapsulate backup logic. + * Created on 8/10/16. + * + * @author Evgenii Kanivets + */ +class BackupController(private val formatController: FormatController, filesDir: String) { + + var onBackupListener: OnBackupListener? = null + + private val appDbFileName: String = "$filesDir/databases/database" + + fun makeBackup(dbClient: DbxClientV2) { + val fileName = formatController.formatDateAndTime(System.currentTimeMillis()) + DropboxBackupAsyncTask(dbClient, fileName, appDbFileName, onBackupListener).execute() + } + + fun restoreBackup(dbClient: DbxClientV2, backupName: String) { + DropboxRestoreBackupAsyncTask(dbClient, appDbFileName, backupName, onBackupListener).execute() + } + + fun fetchBackups(dbClient: DbxClientV2) { + DropboxFetchBackupListAsyncTask(dbClient, onBackupListener).execute() + } + + fun removeBackup(dbClient: DbxClientV2, backupName: String) { + DropboxRemoveBackupAsyncTask(dbClient, backupName, onBackupListener).execute() + } + + interface OnBackupListener { + + fun onBackupsFetched(backupList: List) + + fun onBackupSuccess() + + fun onBackupFailure(reason: String?) + + fun onRestoreSuccess(backupName: String) + + fun onRestoreFailure(reason: String?) + + fun onRemoveSuccess() + + fun onRemoveFailure(reason: String?) + + companion object { + const val SUCCESS = "success" + const val ERROR_AUTHENTICATION = "error_authentication" + } + } + +} diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/backup/tasks/DropboxBackupAsyncTask.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/backup/tasks/DropboxBackupAsyncTask.java new file mode 100644 index 0000000..9174807 --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/backup/tasks/DropboxBackupAsyncTask.java @@ -0,0 +1,75 @@ +package com.blogspot.e_kanivets.moneytracker.controller.backup.tasks; + +import android.os.AsyncTask; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import com.blogspot.e_kanivets.moneytracker.controller.backup.BackupController; +import com.dropbox.core.DbxException; +import com.dropbox.core.v2.DbxClientV2; +import com.dropbox.core.v2.files.FileMetadata; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; + +public class DropboxBackupAsyncTask extends AsyncTask { + + private DbxClientV2 dbClient; + private String appDbFileName; + private String fileName; + + @Nullable private BackupController.OnBackupListener listener; + + public DropboxBackupAsyncTask(@NonNull DbxClientV2 dbClient, String fileName, String appDbFileName, + @Nullable BackupController.OnBackupListener listener) { + this.dbClient = dbClient; + this.fileName = fileName; + this.appDbFileName = appDbFileName; + this.listener = listener; + } + + @Override protected String doInBackground(Void... params) { + FileInputStream fileInputStream = readAppDb(); + if (fileInputStream == null) return null; + + FileMetadata info = null; + + try { + info = dbClient.files().upload("/" + fileName).uploadAndFinish(fileInputStream); + } catch (DbxException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + if (info == null) { + return null; + } else { + return BackupController.OnBackupListener.SUCCESS; + } + } + + @Override protected void onPostExecute(String result) { + super.onPostExecute(result); + if (listener == null) return; + + if (BackupController.OnBackupListener.SUCCESS.equals(result)) { + listener.onBackupSuccess(); + } else { + listener.onBackupFailure(result); + } + } + + @Nullable private FileInputStream readAppDb() { + File dbFile = new File(appDbFileName); + FileInputStream fis = null; + + try { + fis = new FileInputStream(dbFile); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + + return fis; + } +} diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/backup/tasks/DropboxFetchBackupListAsyncTask.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/backup/tasks/DropboxFetchBackupListAsyncTask.java new file mode 100644 index 0000000..1d35451 --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/backup/tasks/DropboxFetchBackupListAsyncTask.java @@ -0,0 +1,51 @@ +package com.blogspot.e_kanivets.moneytracker.controller.backup.tasks; + +import android.os.AsyncTask; +import android.support.annotation.Nullable; +import com.blogspot.e_kanivets.moneytracker.controller.backup.BackupController; +import com.dropbox.core.DbxException; +import com.dropbox.core.v2.DbxClientV2; +import com.dropbox.core.v2.files.Metadata; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class DropboxFetchBackupListAsyncTask extends AsyncTask, List> { + private DbxClientV2 dbClient; + + @Nullable + private BackupController.OnBackupListener listener; + + public DropboxFetchBackupListAsyncTask(DbxClientV2 dbClient, + @Nullable BackupController.OnBackupListener listener) { + this.dbClient = dbClient; + this.listener = listener; + } + + @Override + protected List doInBackground(Void... params) { + List entryList = new ArrayList<>(); + List backupList = new ArrayList<>(); + + try { + entryList = dbClient.files().listFolder("").getEntries(); + } catch (DbxException e) { + e.printStackTrace(); + } + + for (Metadata entry : entryList) { + backupList.add(entry.getName()); + } + + return backupList; + } + + @Override + protected void onPostExecute(List backupList) { + super.onPostExecute(backupList); + if (listener == null) return; + + Collections.reverse(backupList); + listener.onBackupsFetched(backupList); + } +} diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/backup/tasks/DropboxRemoveBackupAsyncTask.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/backup/tasks/DropboxRemoveBackupAsyncTask.java new file mode 100644 index 0000000..db42e7a --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/backup/tasks/DropboxRemoveBackupAsyncTask.java @@ -0,0 +1,51 @@ +package com.blogspot.e_kanivets.moneytracker.controller.backup.tasks; + +import android.os.AsyncTask; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import com.blogspot.e_kanivets.moneytracker.controller.backup.BackupController; +import com.dropbox.core.DbxException; +import com.dropbox.core.v2.DbxClientV2; +import com.dropbox.core.v2.files.DeleteResult; +import com.dropbox.core.v2.files.FileMetadata; +import com.dropbox.core.v2.files.Metadata; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; + +public class DropboxRemoveBackupAsyncTask extends AsyncTask { + + private DbxClientV2 dbClient; + private String backupName; + + @Nullable private BackupController.OnBackupListener listener; + + public DropboxRemoveBackupAsyncTask(DbxClientV2 dbClient, String backupName, + @Nullable BackupController.OnBackupListener listener) { + this.dbClient = dbClient; + this.backupName = backupName; + this.listener = listener; + } + + @Override protected String doInBackground(Void... params) { + try { + Metadata metadata = dbClient.files().deleteV2("/" + backupName).getMetadata(); + return metadata == null ? null : BackupController.OnBackupListener.SUCCESS; + } catch (DbxException e) { + e.printStackTrace(); + return e.getMessage(); + } + } + + @Override protected void onPostExecute(String result) { + super.onPostExecute(result); + if (listener == null) return; + + if (BackupController.OnBackupListener.SUCCESS.equals(result)) { + listener.onRemoveSuccess(); + } else { + listener.onRemoveFailure(result); + } + } +} diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/backup/tasks/DropboxRestoreBackupAsyncTask.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/backup/tasks/DropboxRestoreBackupAsyncTask.java new file mode 100644 index 0000000..c0c5f3b --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/backup/tasks/DropboxRestoreBackupAsyncTask.java @@ -0,0 +1,84 @@ +package com.blogspot.e_kanivets.moneytracker.controller.backup.tasks; + +import android.os.AsyncTask; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import com.blogspot.e_kanivets.moneytracker.controller.backup.BackupController; +import com.dropbox.core.DbxException; +import com.dropbox.core.v2.DbxClientV2; +import com.dropbox.core.v2.files.FileMetadata; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; + +public class DropboxRestoreBackupAsyncTask extends AsyncTask { + + private DbxClientV2 dbClient; + private String appDbFileName; + private String backupName; + + @Nullable private BackupController.OnBackupListener listener; + + public DropboxRestoreBackupAsyncTask(DbxClientV2 dbClient, String appDbFileName, String backupName, + @Nullable BackupController.OnBackupListener listener) { + this.dbClient = dbClient; + this.appDbFileName = appDbFileName; + this.backupName = backupName; + this.listener = listener; + } + + @Override protected String doInBackground(Void... params) { + final File file = new File(getRestoreFileName()); + FileOutputStream outputStream = null; + try { + outputStream = new FileOutputStream(file); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + + if (outputStream == null) { + return null; + } else { + FileMetadata info = null; + try { + info = dbClient.files().download("/" + backupName).download(outputStream); + } catch (DbxException | IOException e) { + e.printStackTrace(); + } + + if (info == null) { + return null; + } else { + try { + outputStream.close(); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + + if (file.exists() && file.length() != 0) { + boolean renamed = file.renameTo(new File(appDbFileName)); + return renamed ? BackupController.OnBackupListener.SUCCESS : null; + } else { + return null; + } + } + } + } + + @Override protected void onPostExecute(String result) { + super.onPostExecute(result); + if (listener == null) return; + + if (BackupController.OnBackupListener.SUCCESS.equals(result)) { + listener.onRestoreSuccess(backupName); + } else { + listener.onRestoreFailure(result); + } + } + + @NonNull private String getRestoreFileName() { + return appDbFileName + ".restore"; + } +} 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 75cf0db..f383cd7 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 @@ -8,6 +8,7 @@ import com.blogspot.e_kanivets.moneytracker.entity.data.Account; import com.blogspot.e_kanivets.moneytracker.entity.data.Record; import com.blogspot.e_kanivets.moneytracker.entity.data.Transfer; +import com.blogspot.e_kanivets.moneytracker.repo.DbHelper; import com.blogspot.e_kanivets.moneytracker.repo.base.IRepo; import java.util.ArrayList; @@ -20,8 +21,7 @@ * @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,8 +30,22 @@ public AccountController(IRepo accountRepo, PreferenceController prefer this.preferenceController = preferenceController; } - @NonNull - public List readActiveAccounts() { + @Nullable @Override public Account read(long id) { + return substituteCurrency(super.read(id)); + } + + @NonNull @Override public List readAll() { + List accountList = super.readAll(); + + List result = new ArrayList<>(); + for (Account account : accountList) { + result.add(substituteCurrency(account)); + } + + return result; + } + + @NonNull public List readActiveAccounts() { List result = new ArrayList<>(); for (Account account : readAll()) { @@ -43,8 +57,7 @@ public List readActiveAccounts() { return result; } - @NonNull - public List readArchivedAccounts() { + @NonNull public List readArchivedAccounts() { List result = new ArrayList<>(); for (Account account : readAll()) { @@ -128,15 +141,18 @@ public boolean transferDone(@Nullable Transfer transfer) { return true; } - @Nullable - public Account readDefaultAccount() { + @Nullable public Account readDefaultAccount() { long defaultAccountId = preferenceController.readDefaultAccountId(); - if (defaultAccountId == -1) return getFirstAccount(); - else { + if (defaultAccountId == -1) { + return getFirstAccount(); + } else { Account account = read(defaultAccountId); - if (account == null) return getFirstAccount(); - else return account; + if (account == null) { + return getFirstAccount(); + } else { + return account; + } } } @@ -162,7 +178,23 @@ public boolean restore(@Nullable Account account) { private Account getFirstAccount() { List accountList = readAll(); - if (accountList.size() == 0) return null; - else return accountList.get(0); + if (accountList.size() == 0) { + return null; + } else { + return accountList.get(0); + } + } + + private Account substituteCurrency(Account account) { + if (account == null) { + return null; + } else { + String currency = account.getCurrency(); + if (DbHelper.DEFAULT_ACCOUNT_CURRENCY.equals(currency)) { + currency = preferenceController.readNonSubstitutionCurrency(); + } + return new Account(account.getId(), account.getTitle(), account.getCurSum(), currency, + account.getDecimals(), account.getGoal(), account.isArchived(), account.getColor()); + } } } \ No newline at end of file diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/data/RecordController.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/data/RecordController.java index a17b984..c15d524 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/data/RecordController.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/data/RecordController.java @@ -3,6 +3,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import com.blogspot.e_kanivets.moneytracker.controller.PreferenceController; import com.blogspot.e_kanivets.moneytracker.repo.DbHelper; import com.blogspot.e_kanivets.moneytracker.controller.base.BaseController; import com.blogspot.e_kanivets.moneytracker.entity.data.Account; @@ -23,34 +24,34 @@ * @author Evgenii Kanivets */ public class RecordController extends BaseController { + private final CategoryController categoryController; private final AccountController accountController; + private final PreferenceController preferenceController; public RecordController(IRepo recordRepo, CategoryController categoryController, - AccountController accountController) { + AccountController accountController, PreferenceController preferenceController) { super(recordRepo); this.categoryController = categoryController; this.accountController = accountController; + this.preferenceController = preferenceController; } - @Override - @SuppressWarnings("SimplifiableIfStatement") - public Record create(@Nullable Record record) { + @Override @SuppressWarnings("SimplifiableIfStatement") public Record create(@Nullable Record record) { if (record == null) return null; record = validateRecord(record); Record createdRecord = repo.create(record); - if (createdRecord == null) return null; - else { + if (createdRecord == null) { + return null; + } else { accountController.recordAdded(createdRecord); return createdRecord; } } - @Override - @SuppressWarnings("SimplifiableIfStatement") - public Record update(@Nullable Record record) { + @Override @SuppressWarnings("SimplifiableIfStatement") public Record update(@Nullable Record record) { if (record == null) return null; record = validateRecord(record); @@ -58,44 +59,42 @@ record = validateRecord(record); Record oldRecord = read(record.getId()); Record updatedRecord = repo.update(record); - if (updatedRecord == null) return null; - else { + if (updatedRecord == null) { + return null; + } else { accountController.recordUpdated(oldRecord, updatedRecord); return updatedRecord; } } - @Override - @SuppressWarnings("SimplifiableIfStatement") - public boolean delete(@Nullable Record record) { - if (repo.delete(record)) return accountController.recordDeleted(record); - else return false; + @Override @SuppressWarnings("SimplifiableIfStatement") public boolean delete(@Nullable Record record) { + if (repo.delete(record)) { + return accountController.recordDeleted(record); + } else { + return false; + } } - @Nullable - @Override - public Record read(long id) { - List list = readWithCondition("id=?", new String[]{Long.toString(id)}); + @Nullable @Override public Record read(long id) { + List list = readWithCondition("id=?", new String[] { Long.toString(id) }); - if (list.size() == 1) return list.get(0); - else return null; + if (list.size() == 1) { + return list.get(0); + } else { + return null; + } } - @NonNull - @Override - public List readAll() { + @NonNull @Override public List readAll() { return readWithCondition(null, null); } - @NonNull - @Override - public List readWithCondition(String condition, String[] args) { + @NonNull @Override public List readWithCondition(String condition, String[] args) { List recordList = super.readWithCondition(condition, args); // Sort record list by time field from smallest to biggest Collections.sort(recordList, new Comparator() { - @Override - public int compare(Record lhs, Record rhs) { + @Override public int compare(Record lhs, Record rhs) { return lhs.getTime() < rhs.getTime() ? -1 : (lhs.getTime() == rhs.getTime() ? 0 : 1); } }); @@ -104,16 +103,19 @@ public int compare(Record lhs, Record rhs) { List completedRecordList = new ArrayList<>(); for (Record record : recordList) { Category category = null; - if (record.getCategory() != null) - category = categoryController.read(record.getCategory().getId()); + if (record.getCategory() != null) category = categoryController.read(record.getCategory().getId()); Account account = null; - if (record.getAccount() != null) - account = accountController.read(record.getAccount().getId()); + if (record.getAccount() != null) account = accountController.read(record.getAccount().getId()); + + String currency = record.getCurrency(); + if (DbHelper.DEFAULT_ACCOUNT_CURRENCY.equals(currency)) { + currency = preferenceController.readNonSubstitutionCurrency(); + } - completedRecordList.add(new Record(record.getId(), record.getTime(), record.getType(), - record.getTitle(), category, record.getPrice(), account, record.getCurrency(), - record.getDecimals())); + completedRecordList.add( + new Record(record.getId(), record.getTime(), record.getType(), record.getTitle(), category, + record.getPrice(), account, currency, record.getDecimals())); } return completedRecordList; @@ -121,8 +123,16 @@ public int compare(Record lhs, Record rhs) { public List getRecordsForPeriod(Period period) { String condition = DbHelper.TIME_COLUMN + " BETWEEN ? AND ?"; - String[] args = new String[]{Long.toString(period.getFirst().getTime()), - Long.toString(period.getLast().getTime())}; + String[] args = new String[] { + Long.toString(period.getFirst().getTime()), Long.toString(period.getLast().getTime()) + }; + + return readWithCondition(condition, args); + } + + public List getRecordsForAccount(Account account) { + String condition = DbHelper.ACCOUNT_ID_COLUMN + "=?"; + String[] args = new String[] { Long.toString(account.getId()) }; return readWithCondition(condition, args); } @@ -132,8 +142,7 @@ private Record validateRecord(@NonNull Record record) { Category category = categoryController.readOrCreate(record.getCategory().getName()); - return new Record(record.getId(), record.getTime(), record.getType(), record.getTitle(), - category, record.getPrice(), record.getAccount(), record.getCurrency(), - record.getDecimals()); + return new Record(record.getId(), record.getTime(), record.getType(), record.getTitle(), category, + record.getPrice(), record.getAccount(), record.getCurrency(), record.getDecimals()); } } \ No newline at end of file diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/data/TransferController.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/data/TransferController.java deleted file mode 100644 index 765a5f8..0000000 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/data/TransferController.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.blogspot.e_kanivets.moneytracker.controller.data; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.blogspot.e_kanivets.moneytracker.controller.base.BaseController; -import com.blogspot.e_kanivets.moneytracker.entity.data.Transfer; -import com.blogspot.e_kanivets.moneytracker.repo.base.IRepo; - -/** - * Controller class to encapsulate transfer handling logic. - * Created on 2/17/16. - * - * @author Evgenii Kanivets - */ -public class TransferController extends BaseController { - @SuppressWarnings("unused") - private static final String TAG = "TransferController"; - - @NonNull - private AccountController accountController; - - public TransferController(@NonNull IRepo transferRepo, - @NonNull AccountController accountController) { - super(transferRepo); - this.accountController = accountController; - } - - @Override - @SuppressWarnings("SimplifiableIfStatement") - public Transfer create(@Nullable Transfer transfer) { - Transfer createdTransfer = repo.create(transfer); - - if (createdTransfer == null) return null; - else { - accountController.transferDone(createdTransfer); - return createdTransfer; - } - } -} diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/data/TransferController.kt b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/data/TransferController.kt new file mode 100644 index 0000000..457901e --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/data/TransferController.kt @@ -0,0 +1,36 @@ +package com.blogspot.e_kanivets.moneytracker.controller.data + +import com.blogspot.e_kanivets.moneytracker.controller.base.BaseController +import com.blogspot.e_kanivets.moneytracker.entity.data.Account +import com.blogspot.e_kanivets.moneytracker.entity.data.Transfer +import com.blogspot.e_kanivets.moneytracker.repo.DbHelper +import com.blogspot.e_kanivets.moneytracker.repo.base.IRepo + +/** + * Controller class to encapsulate transfer handling logic. + * Created on 2/17/16. + * + * @author Evgenii Kanivets + */ +class TransferController( + transferRepo: IRepo, + private val accountController: AccountController +) : BaseController(transferRepo) { + + override fun create(transfer: Transfer?): Transfer? { + val createdTransfer = repo.create(transfer) + + return if (createdTransfer == null) null else { + accountController.transferDone(createdTransfer) + createdTransfer + } + } + + fun getTransfersForAccount(account: Account): List { + val condition = "${DbHelper.FROM_ACCOUNT_ID_COLUMN}=? OR ${DbHelper.TO_ACCOUNT_ID_COLUMN}=?" + val args = arrayOf(account.id.toString(), account.id.toString()) + + return readWithCondition(condition, args) + } + +} 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 6d31fa5..1f0b100 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 @@ -1,6 +1,8 @@ package com.blogspot.e_kanivets.moneytracker.di; -import com.blogspot.e_kanivets.moneytracker.activity.account.EditAccountActivity; +import com.blogspot.e_kanivets.moneytracker.activity.account.edit.EditAccountActivity; +import com.blogspot.e_kanivets.moneytracker.activity.account.edit.fragment.AccountOperationsFragment; +import com.blogspot.e_kanivets.moneytracker.activity.account.edit.fragment.EditAccountFragment; import com.blogspot.e_kanivets.moneytracker.activity.charts.ChartsActivity; import com.blogspot.e_kanivets.moneytracker.activity.external.BackupActivity; import com.blogspot.e_kanivets.moneytracker.activity.external.ImportExportActivity; @@ -81,4 +83,8 @@ public interface AppComponent { void inject(MonthSummaryAdapter monthSummaryAdapter); void inject(EditAccountActivity editAccountActivity); + + void inject(EditAccountFragment editAccountFragment); + + void inject(AccountOperationsFragment accountRecordsFragment); } diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/di/module/ControllerModule.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/di/module/ControllerModule.java index af86465..05297ff 100644 --- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/di/module/ControllerModule.java +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/di/module/ControllerModule.java @@ -3,7 +3,7 @@ import android.content.Context; import android.support.annotation.NonNull; -import com.blogspot.e_kanivets.moneytracker.controller.BackupController; +import com.blogspot.e_kanivets.moneytracker.controller.backup.BackupController; import com.blogspot.e_kanivets.moneytracker.controller.external.ExportController; import com.blogspot.e_kanivets.moneytracker.controller.FormatController; import com.blogspot.e_kanivets.moneytracker.controller.PeriodController; @@ -33,102 +33,69 @@ * * @author Evgenii Kanivets */ -@Module -public class ControllerModule { +@Module public class ControllerModule { private Context context; public ControllerModule(Context context) { this.context = context; } - @Provides - @NonNull - @Singleton - public AccountController providesAccountController(IRepo accountRepo, - PreferenceController preferenceController) { + @Provides @NonNull @Singleton public AccountController providesAccountController(IRepo accountRepo, + PreferenceController preferenceController) { return new AccountController(accountRepo, preferenceController); } - @Provides - @NonNull - @Singleton - public CategoryController providesCategoryController(IRepo categoryRepo, - PreferenceController preferenceController) { + @Provides @NonNull @Singleton public CategoryController providesCategoryController(IRepo categoryRepo, + PreferenceController preferenceController) { return new CategoryController(categoryRepo, preferenceController); } - @Provides - @NonNull - @Singleton + @Provides @NonNull @Singleton public ExchangeRateController providesExchangeRateController(IRepo exchangeRateRepo) { return new ExchangeRateController(exchangeRateRepo); } - @Provides - @NonNull - @Singleton - public RecordController providesRecordController(IRepo recordRepo, - CategoryController categoryController, - AccountController accountController) { - return new RecordController(recordRepo, categoryController, accountController); + @Provides @NonNull @Singleton + public RecordController providesRecordController(IRepo recordRepo, CategoryController categoryController, + AccountController accountController, PreferenceController preferenceController) { + return new RecordController(recordRepo, categoryController, accountController, preferenceController); } - @Provides - @NonNull - @Singleton - public TransferController providesTransferController(IRepo transferRepo, - AccountController accountController) { + @Provides @NonNull @Singleton public TransferController providesTransferController(IRepo transferRepo, + AccountController accountController) { return new TransferController(transferRepo, accountController); } - @Provides - @NonNull - @Singleton + @Provides @NonNull @Singleton public CurrencyController providesCurrencyController(AccountController accountController, - PreferenceController preferenceController) { + PreferenceController preferenceController) { return new CurrencyController(accountController, preferenceController); } - @Provides - @NonNull - @Singleton - public PreferenceController providesPreferenceController() { + @Provides @NonNull @Singleton public PreferenceController providesPreferenceController() { return new PreferenceController(context); } - @Provides - @NonNull - @Singleton + @Provides @NonNull @Singleton public PeriodController providesPeriodController(PreferenceController preferenceController) { return new PeriodController(preferenceController); } - @Provides - @NonNull - @Singleton + @Provides @NonNull @Singleton public FormatController providesFormatController(PreferenceController preferenceController) { return new FormatController(preferenceController); } - @Provides - @NonNull - @Singleton - public ExportController providesExportController(RecordController recordController, - CategoryController categoryController) { + @Provides @NonNull @Singleton public ExportController providesExportController(RecordController recordController, + CategoryController categoryController) { return new ExportController(recordController, categoryController); } - @Provides - @NonNull - @Singleton - public ImportController providesImportController(RecordController recordController) { + @Provides @NonNull @Singleton public ImportController providesImportController(RecordController recordController) { return new ImportController(recordController); } - @Provides - @NonNull - @Singleton - public BackupController providesBackupController(FormatController formatController) { + @Provides @NonNull @Singleton public BackupController providesBackupController(FormatController formatController) { return new BackupController(formatController, context.getApplicationInfo().dataDir); } } diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/validator/EditAccountValidator.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/validator/EditAccountValidator.java new file mode 100644 index 0000000..fc2596a --- /dev/null +++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/validator/EditAccountValidator.java @@ -0,0 +1,70 @@ +package com.blogspot.e_kanivets.moneytracker.util.validator; + +import android.content.Context; +import android.support.annotation.NonNull; +import android.support.design.widget.TextInputLayout; +import android.view.View; +import android.widget.EditText; +import butterknife.BindView; +import butterknife.ButterKnife; +import com.blogspot.e_kanivets.moneytracker.R; +import com.blogspot.e_kanivets.moneytracker.entity.data.Account; + +/** + * Util class for EditAccount validation. + * Created on 16.09.2018. + * + * @author Evgenii Kanivets + */ + +public class EditAccountValidator implements IValidator { + + @NonNull private final Context context; + + @BindView(R.id.tilTitle) TextInputLayout tilTitle; + @BindView(R.id.etTitle) EditText etTitle; + @BindView(R.id.tilGoal) TextInputLayout tilGoal; + @BindView(R.id.etGoal) EditText etGoal; + + public EditAccountValidator(@NonNull Context context, @NonNull View view) { + this.context = context; + ButterKnife.bind(this, view); + initTextWatchers(); + } + + @Override public boolean validate() { + String title = etTitle.getText().toString().trim(); + double goal = Double.MAX_VALUE; + + try { + goal = Double.parseDouble(etGoal.getText().toString().trim()); + } catch (NumberFormatException e) { + e.printStackTrace(); + } + + boolean valid = true; + + if (title.isEmpty()) { + tilTitle.setError(context.getString(R.string.field_cant_be_empty)); + valid = false; + } + + if (goal == Double.MAX_VALUE) { + tilGoal.setError(context.getString(R.string.field_cant_be_empty)); + goal = 0; + valid = false; + } + + if (Math.abs(goal) > MAX_ABS_VALUE) { + tilGoal.setError(context.getString(R.string.too_rich_or_poor)); + valid = false; + } + + return valid; + } + + private void initTextWatchers() { + etTitle.addTextChangedListener(new ClearErrorTextWatcher(tilTitle)); + etGoal.addTextChangedListener(new ClearErrorTextWatcher(tilGoal)); + } +} diff --git a/app/src/main/res/layout/activity_edit_account.xml b/app/src/main/res/layout/activity_edit_account.xml index 2d7da6c..4a73f71 100644 --- a/app/src/main/res/layout/activity_edit_account.xml +++ b/app/src/main/res/layout/activity_edit_account.xml @@ -6,97 +6,49 @@ android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" - tools:context="com.blogspot.e_kanivets.moneytracker.activity.account.EditAccountActivity"> + tools:context="com.blogspot.e_kanivets.moneytracker.activity.account.edit.EditAccountActivity" + > - - - - - + - - - - - - - - - - - + android:layout_height="?attr/actionBarSize" + android:background="?attr/colorPrimary" + app:popupTheme="@style/AppTheme.PopupOverlay" + /> - - - - - - - - - - - - - - + app:tabGravity="fill" + app:tabIndicatorColor="@color/white" + app:tabMode="fixed" + /> + + + + + + diff --git a/app/src/main/res/layout/fragment_account_operations.xml b/app/src/main/res/layout/fragment_account_operations.xml new file mode 100644 index 0000000..f4e5e9d --- /dev/null +++ b/app/src/main/res/layout/fragment_account_operations.xml @@ -0,0 +1,13 @@ + + + + + + diff --git a/app/src/main/res/layout/fragment_edit_account.xml b/app/src/main/res/layout/fragment_edit_account.xml new file mode 100644 index 0000000..b45d7ce --- /dev/null +++ b/app/src/main/res/layout/fragment_edit_account.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/view_backup_item.xml b/app/src/main/res/layout/view_backup_item.xml new file mode 100644 index 0000000..5118953 --- /dev/null +++ b/app/src/main/res/layout/view_backup_item.xml @@ -0,0 +1,28 @@ + + + + + + + + diff --git a/app/src/main/res/layout/view_record.xml b/app/src/main/res/layout/view_record.xml index f62fadb..4da9721 100644 --- a/app/src/main/res/layout/view_record.xml +++ b/app/src/main/res/layout/view_record.xml @@ -2,97 +2,107 @@ + android:background="@color/white" + > + + + android:layout_height="wrap_content" + android:layout_weight="1" + android:orientation="vertical" + > + + - + + - - - - - - - - - - + android:background="@android:color/transparent" + android:gravity="start" + android:paddingBottom="5dp" + android:text="New Text" + android:theme="@style/Text_Body2" + /> - - - + android:layout_gravity="center_vertical|top" + android:layout_marginStart="2dp" + android:layout_weight="1" + android:background="@android:color/transparent" + android:gravity="start" + android:paddingBottom="5dp" + android:text="New Text" + android:theme="@style/Text_Body1" + /> + + + + - + - + - + + + \ 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 7bb319a..4995132 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -1,73 +1,74 @@ - Money Tracker - - Название - Категория - Цена - Добавить доход - Добавить расход - Добавить - - Отчет - ИЗМЕНИТЬ ДАТУ - СУММА - Суммарный доход - Суммарный расход - Удалить - - День - Неделя - Месяц - Год - За все время - - Вам нравится MoneyTracker? - Оцените его на Google Play! - Может быть позже - Нет, спасибо - Да! - - Записи - Счета - Добавить доход - Добавить расход - Добавить счет - - Экспорт - Добавить счет - Начальная сумма - Счет - Перевод - Со счета - На счет - Курсы обмена - Добавить курс обмена - Счет был удален - - Краткий отчет - Другой - Сводка - Валюта - Ошибка! Укажите курсы обмена. - Количество - Настройки - Настройки - Счет по умолчанию - Валюта по умолчанию - - Итоги - Доходы - Расходы - - Точность отображения - - Импорт/Экспорт - Импорт - %1$s записей было импортировано - Справка - Для импорта записей в приложение:\n + Money Tracker + + Название + Категория + Цена + Добавить доход + Добавить расход + Добавить + + Отчет + ИЗМЕНИТЬ ДАТУ + СУММА + Суммарный доход + Суммарный расход + Удалить + + День + Неделя + Месяц + Год + За все время + + Вам нравится MoneyTracker? + Оцените его на Google Play! + Может быть позже + Нет, спасибо + Да! + + Записи + Счета + Добавить доход + Добавить расход + Добавить счет + + Экспорт + Добавить счет + Начальная сумма + Счет + Перевод + Со счета + На счет + Курсы обмена + Добавить курс обмена + Счет был удален + + Краткий отчет + Другой + Сводка + Валюта + Ошибка! Укажите курсы обмена. + Количество + Настройки + Настройки + Счет по умолчанию + Валюта по умолчанию + Замена валюты NON + + Итоги + Доходы + Расходы + + Точность отображения + + Импорт/Экспорт + Импорт + %1$s записей было импортировано + Справка + Для импорта записей в приложение:\n 1. Создайте CSV файл в формате - \n время;заголовок;категория;цена;валюта.\n 2. Скопируйте его контент в текстовое поле.\n @@ -76,51 +77,56 @@ Важно: время - метка времени в миллисекундах; валюта - 3-х символьный код валюты.\n Пример: 1466948795712;метро;транспорт;-20.0;UAH - Банк продает - Конвертировать в - Банк покупает - Конвертировать из - - Дата - Время - - Бэкап/Восстановление - Не удалось создать бэкап. - Не удалось восстановить бэкап. - Выбранный бэкап %1$s был успешно восстановлен. - Бэкап восстановлен - Предупреждение - Вы действительно хотите удалить все текущие данные и восстановить бэкап %1$s? Пожалуйста, сделайте бэкап текущих данных. - Сделать бэкап - - Редактировать доход - Редактировать расход - - Импортирование записей… - Создание бэкапа… - Восстановление бэкапа… - Загрузка бэкапов… - - Версия %1$s (Android %2$s) - О приложении - Open Money Tracker (OMT) это приложения для ведения личных финансов. Вы легко можете управлять доходами и расходами. Также доступны счета и курсы обмена.\n\nДля того чтобы помочь сделать OMT лучше, пишите о багах на форуме 4PDA; идеи новых функций также приветствуются. Ищите ответы или начинайте обсуждение на форуме.\n\nOMT это проект с открытым исходным кодом, так что каждый может помочь с его разработкой. Не-разработчики также могут помочь. Например, вы можете помочь с переводом, предоставить скриншоты или написать статью в блоге о OMT.\n\nOMT выпущен под GNU-GPL v3 лицензией и исходный код доступен на GitHub.\n\nПрофиль разработчика на LinkedIn. - - Поле не может быть пустым. - Вы не можете быть настолько богаты или бедны. - Вы не можете быть настолько богаты. - Нет - Слишком много для перевода. - Слишком много для обмена. - Нельзя конвертировать валюту в саму себя. - Нельзя создать запись в будущем. - Необходим, по крайней мере, один счет. - - Редактировать счет - Цвет - Цель - Восстановить - Архивировать - Нельзя архивировать счет по умолчанию. - Удалить счет - Вы уверены, что хотите удалить этот счет ? Вы не сможете восстановить его после удаления. + Банк продает + Конвертировать в + Банк покупает + Конвертировать из + + Дата + Время + + Бэкап/Восстановление + Не удалось создать бэкап. + Не удалось восстановить бэкап. + Выбранный бэкап %1$s был успешно восстановлен. + Бэкап восстановлен + Предупреждение + Вы действительно хотите удалить все текущие данные и восстановить бэкап %1$s? Пожалуйста, сделайте бэкап текущих данных. + Сделать бэкап + + Редактировать доход + Редактировать расход + + Импортирование записей… + Создание бэкапа… + Восстановление бэкапа… + Загрузка бэкапов… + Удаление бэкапа… + + Версия %1$s (Android %2$s) + О приложении + Open Money Tracker (OMT) это приложения для ведения личных финансов. Вы легко можете управлять доходами и расходами. Также доступны счета и курсы обмена.\n\nДля того чтобы помочь сделать OMT лучше, пишите о багах на форуме 4PDA; идеи новых функций также приветствуются. Ищите ответы или начинайте обсуждение на форуме.\n\nOMT это проект с открытым исходным кодом, так что каждый может помочь с его разработкой. Не-разработчики также могут помочь. Например, вы можете помочь с переводом, предоставить скриншоты или написать статью в блоге о OMT.\n\nOMT выпущен под GNU-GPL v3 лицензией и исходный код доступен на GitHub.\n\nПрофиль разработчика на LinkedIn. + + Поле не может быть пустым. + Вы не можете быть настолько богаты или бедны. + Вы не можете быть настолько богаты. + Нет + Слишком много для перевода. + Слишком много для обмена. + Нельзя конвертировать валюту в саму себя. + Нельзя создать запись в будущем. + Необходим, по крайней мере, один счет. + + Редактировать счет + Цвет + Цель + Восстановить + Архивировать + Нельзя архивировать счет по умолчанию. + Удалить счет + Вы уверены, что хотите удалить этот счет ? Вы не сможете восстановить его после удаления. + + График + Информация + Операции diff --git a/app/src/main/res/values-uk/strings.xml b/app/src/main/res/values-uk/strings.xml index 2398fdd..d828602 100644 --- a/app/src/main/res/values-uk/strings.xml +++ b/app/src/main/res/values-uk/strings.xml @@ -56,6 +56,7 @@ Налаштування Рахунок за замовчуванням Валюта за замовчуванням + Замiна валюти NON Підсумки Доходи @@ -100,6 +101,7 @@ Створення бекапу… Відновлення бекапу… Завантаження бекапів… + Видалення бекапу… Версія %1$s (Android %2$s) Про додаток @@ -123,4 +125,8 @@ Неможливо архівувати рахунок за замовчанням. Видалити рахунок Ви впевнені, що хочете видалити цей рахунок? Ви не зможете відновити його після видалення. + + Графік + Інформація + Операції diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index c98ea3a..a8fc9f1 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -52,6 +52,7 @@ 设置 默认账户 默认货币 + NON 替代貨幣 表格 收入 @@ -89,6 +90,7 @@ 生成备份…… 恢复备份中…… 正在获得备份…… + 刪除備份…… 版本 %1$s (安卓 %2$s) 关于 @@ -112,4 +114,8 @@ 您无法获得默认帐户。 删除帐户 你确定要删除这个帐户吗? 以后不能恢复。 + + 圖形 + 信息 + 操作 diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index f6aaa2c..43efe77 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -1,28 +1,29 @@ - #ffffff - #E0E0E0 + #ffffff + #E0E0E0 - #f1f8e9 - #8bc34a - #689f38 - #33691e + #f1f8e9 + #8bc34a + #689f38 + #33691e - #ffebee - #f44336 - #d32f2f - #b71c1c + #ffebee + #f44336 + #d32f2f + #b71c1c - #f6f6f6 + #f6f6f6 + #8A000000 - #ffbd21 - #ff8a00 + #ffbd21 + #ff8a00 - #ffffffff - #de000000 - #8A000000 + #ffffffff + #de000000 + #8A000000 - #ffffff + #ffffff - #20000000 + #20000000 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4552453..7d479d0 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,80 +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 - Default 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 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 1. Create CSV file in format - \n time;title;category;price;currency.\n 2. Copy its content to text field.\n @@ -83,55 +85,62 @@ 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… - - 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. - - + / + 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. diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 041c89a..cf1ac3a 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -1,26 +1,32 @@ - + - + - + + + + + + + - - - diff --git a/app/src/test/java/com/blogspot/e_kanivets/moneytracker/controller/data/AccountControllerTest.java b/app/src/test/java/com/blogspot/e_kanivets/moneytracker/controller/data/AccountControllerTest.java index 6a38f35..7e8d9af 100644 --- a/app/src/test/java/com/blogspot/e_kanivets/moneytracker/controller/data/AccountControllerTest.java +++ b/app/src/test/java/com/blogspot/e_kanivets/moneytracker/controller/data/AccountControllerTest.java @@ -174,11 +174,11 @@ public void testTransferDone() throws Exception { public void testReadDefaultAccount() throws Exception { assertNull(accountController.readDefaultAccount()); - Account account1 = new Account(1, "a1", 100, "NON", 0, 0, false, 0); + Account account1 = new Account(1, "a1", 100, "UAH", 0, 0, false, 0); repo.create(account1); assertEquals(account1, accountController.readDefaultAccount()); - Account account2 = new Account(2, "a2", 0, "NON", 0, 0, false, 0); + Account account2 = new Account(2, "a2", 0, "UAH", 0, 0, false, 0); repo.create(account2); assertEquals(account1, accountController.readDefaultAccount()); diff --git a/app/src/test/java/com/blogspot/e_kanivets/moneytracker/controller/data/RecordControllerTest.java b/app/src/test/java/com/blogspot/e_kanivets/moneytracker/controller/data/RecordControllerTest.java index 274299f..0e14b27 100644 --- a/app/src/test/java/com/blogspot/e_kanivets/moneytracker/controller/data/RecordControllerTest.java +++ b/app/src/test/java/com/blogspot/e_kanivets/moneytracker/controller/data/RecordControllerTest.java @@ -3,6 +3,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import com.blogspot.e_kanivets.moneytracker.controller.PreferenceController; import com.blogspot.e_kanivets.moneytracker.entity.Period; import com.blogspot.e_kanivets.moneytracker.entity.data.Account; import com.blogspot.e_kanivets.moneytracker.entity.data.Category; @@ -34,6 +35,7 @@ public class RecordControllerTest { private RecordController recordController; private CategoryController categoryMock; private AccountController accountMock; + private PreferenceController preferenceMock; private TestRepo repo; @Before @@ -41,8 +43,9 @@ public void setUp() throws Exception { repo = new TestRepo(); categoryMock = Mockito.mock(CategoryController.class); accountMock = Mockito.mock(AccountController.class); + preferenceMock = Mockito.mock(PreferenceController.class); - recordController = new RecordController(repo, categoryMock, accountMock); + recordController = new RecordController(repo, categoryMock, accountMock, preferenceMock); } @After diff --git a/build.gradle b/build.gradle index 1535c90..0213347 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,14 @@ buildscript { - repositories { + ext.kotlin_version = '1.2.61' + repositories { google() jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' + classpath 'com.android.tools.build:gradle:3.1.4' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.6.2' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index a9609a0..0948dc9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Nov 29 19:03:06 EET 2017 +#Wed Sep 05 21:01:23 CEST 2018 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip