diff --git a/README.md b/README.md
index a0d45a7..0c7c4da 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
# Money Tracker
+
+
## Short overview
Money Tracker is an Android native application with open source code.
Development of this project is guided by simplicity and effeciency for end user.
diff --git a/app/build.gradle b/app/build.gradle
index 4c2a881..69f17d5 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -8,8 +8,8 @@ android {
applicationId 'com.blogspot.e_kanivets.moneytracker'
minSdkVersion 17
targetSdkVersion 23
- versionCode 12
- versionName '1.7.0'
+ versionCode 14
+ versionName '1.7.6'
}
signingConfigs {
releaseConfig {
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 27c66e0..5a44458 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
@@ -94,6 +94,8 @@ public void backupNow() {
@Override
public void onBackupSuccess() {
Timber.d("Backup success.");
+ if (isFinishing()) return;
+
stopProgress();
fetchBackups();
}
@@ -101,6 +103,8 @@ public void onBackupSuccess() {
@Override
public void onBackupFailure(String reason) {
Timber.d("Backup failure.");
+ if (isFinishing()) return;
+
stopProgress();
showToast(R.string.failed_create_backup);
@@ -132,6 +136,8 @@ private void restoreBackup(final String backupName) {
@Override
public void onRestoreSuccess() {
Timber.d("Restore success.");
+ if (isFinishing()) return;
+
stopProgress();
AlertDialog.Builder builder = new AlertDialog.Builder(BackupActivity.this);
@@ -152,6 +158,8 @@ public void onDismiss(DialogInterface dialog) {
@Override
public void onRestoreFailure(String reason) {
Timber.d("Restore failure.");
+ if (isFinishing()) return;
+
stopProgress();
showToast(R.string.failed_restore_backup);
@@ -166,6 +174,8 @@ private void fetchBackups() {
backupController.fetchBackups(dbApi, 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);
diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/CategoryAutoCompleteAdapter.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/CategoryAutoCompleteAdapter.java
index 22103b7..234c1bc 100644
--- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/CategoryAutoCompleteAdapter.java
+++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/adapter/CategoryAutoCompleteAdapter.java
@@ -1,15 +1,23 @@
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.ArrayAdapter;
import android.widget.Filter;
import android.widget.Filterable;
+import android.widget.TextView;
+import com.blogspot.e_kanivets.moneytracker.R;
import com.blogspot.e_kanivets.moneytracker.util.CategoryAutoCompleter;
import java.util.ArrayList;
import java.util.List;
+import butterknife.Bind;
+import butterknife.ButterKnife;
+
/**
* Custom adapter to autocomplete categories.
* Created on 3/18/16.
@@ -25,6 +33,31 @@ public CategoryAutoCompleteAdapter(Context context, int resource, CategoryAutoCo
this.autoCompleter = autoCompleter;
}
+ @Override
+ public View getView(int position, View convertView, ViewGroup parent) {
+ ViewHolder viewHolder;
+
+ if (convertView == null) {
+ convertView = LayoutInflater.from(getContext()).inflate(R.layout.view_category_item, parent, false);
+ viewHolder = new ViewHolder(convertView);
+ convertView.setTag(viewHolder);
+ } else viewHolder = (ViewHolder) convertView.getTag();
+
+ final String category = resultList.get(position);
+
+ viewHolder.tvCategory.setText(category);
+ viewHolder.ivCancel.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ autoCompleter.removeFromAutoComplete(category);
+ resultList.remove(category);
+ notifyDataSetChanged();
+ }
+ });
+
+ return convertView;
+ }
+
@Override
public int getCount() {
return resultList.size();
@@ -63,4 +96,15 @@ protected void publishResults(CharSequence constraint, FilterResults results) {
}
};
}
+
+ public static class ViewHolder {
+ @Bind(R.id.tv_category)
+ TextView tvCategory;
+ @Bind(R.id.iv_cancel)
+ View ivCancel;
+
+ public ViewHolder(View view) {
+ ButterKnife.bind(this, view);
+ }
+ }
}
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 a3130f3..266866b 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,9 @@
import com.blogspot.e_kanivets.moneytracker.R;
+import java.util.HashSet;
+import java.util.Set;
+
/**
* Controller class to encapsulate Shared Preferences handling logic.
* Not deal with {@link com.blogspot.e_kanivets.moneytracker.repo.base.IRepo} instances as others.
@@ -22,6 +25,7 @@ public class PreferenceController {
private static final String KEY_LAST_TS = "key_last_ts";
private static final String KEY_PERIOD_TYPE = "key_period_type";
private static final String KEY_DROPBOX_ACCESS_TOKEN = "key_dropbox_access_token";
+ private static final String KEY_FILTERED_CATEGORIES = "key_filtered_categories";
private static final int RATE_PERIOD = 5;
@@ -34,7 +38,7 @@ public PreferenceController(@NonNull Context context) {
public void addLaunchCount() {
SharedPreferences preferences = getDefaultPrefs();
- SharedPreferences.Editor editor = preferences.edit();
+ SharedPreferences.Editor editor = getEditor();
editor.putInt(LAUNCH_COUNT, preferences.getInt(LAUNCH_COUNT, 0) + 1);
editor.apply();
}
@@ -50,16 +54,13 @@ public boolean checkRateDialog() {
}
public void appRated() {
- SharedPreferences preferences = getDefaultPrefs();
- SharedPreferences.Editor editor = preferences.edit();
+ SharedPreferences.Editor editor = getEditor();
editor.putBoolean(APP_RATED, true);
editor.apply();
}
public void writeFirstTs(long firstTs) {
- SharedPreferences preferences = getDefaultPrefs();
- SharedPreferences.Editor editor = preferences.edit();
-
+ SharedPreferences.Editor editor = getEditor();
editor.putLong(KEY_FIRST_TS, firstTs);
editor.apply();
}
@@ -73,21 +74,23 @@ public void writeLastTs(long lastTs) {
}
public void writePeriodType(String periodType) {
- SharedPreferences preferences = getDefaultPrefs();
- SharedPreferences.Editor editor = preferences.edit();
-
+ SharedPreferences.Editor editor = getEditor();
editor.putString(KEY_PERIOD_TYPE, periodType);
editor.apply();
}
public void writeDropboxAccessToken(String accessToken) {
- SharedPreferences preferences = getDefaultPrefs();
- SharedPreferences.Editor editor = preferences.edit();
-
+ SharedPreferences.Editor editor = getEditor();
editor.putString(KEY_DROPBOX_ACCESS_TOKEN, accessToken);
editor.apply();
}
+ public void writeFilteredCategories(@Nullable Set categorySet) {
+ SharedPreferences.Editor editor = getEditor();
+ editor.putStringSet(KEY_FILTERED_CATEGORIES, categorySet);
+ editor.apply();
+ }
+
public long readDefaultAccountId() {
String defaultAccountPref = context.getString(R.string.pref_default_account);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
@@ -129,7 +132,18 @@ public String readDropboxAccessToken() {
return getDefaultPrefs().getString(KEY_DROPBOX_ACCESS_TOKEN, null);
}
+ @NonNull
+ public Set readFilteredCategories() {
+ return getDefaultPrefs().getStringSet(KEY_FILTERED_CATEGORIES, new HashSet());
+ }
+
+ @NonNull
private SharedPreferences getDefaultPrefs() {
return context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE);
}
+
+ @NonNull
+ private SharedPreferences.Editor getEditor() {
+ return getDefaultPrefs().edit();
+ }
}
diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/data/CategoryController.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/data/CategoryController.java
index e95ed12..dd8bbca 100644
--- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/data/CategoryController.java
+++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/controller/data/CategoryController.java
@@ -3,12 +3,15 @@
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.Category;
import com.blogspot.e_kanivets.moneytracker.repo.base.IRepo;
+import java.util.ArrayList;
import java.util.List;
+import java.util.Set;
/**
* Controller class to encapsulate category handling logic.
@@ -20,13 +23,23 @@ public class CategoryController extends BaseController {
@SuppressWarnings("unused")
private static final String TAG = "CategoryController";
- public CategoryController(@NonNull IRepo categoryRepo) {
+ @NonNull
+ private final PreferenceController preferenceController;
+ @NonNull
+ private Set filteredCategories;
+
+ public CategoryController(@NonNull IRepo categoryRepo,
+ @NonNull PreferenceController preferenceController) {
super(categoryRepo);
+ this.preferenceController = preferenceController;
+ filteredCategories = preferenceController.readFilteredCategories();
}
public Category readOrCreate(@Nullable String categoryName) {
if (categoryName == null || categoryName.trim().isEmpty()) return null;
+ enableCategory(categoryName);
+
String condition = DbHelper.NAME_COLUMN + "=?";
String[] args = {categoryName};
List categoryList = repo.readWithCondition(condition, args);
@@ -34,4 +47,31 @@ public Category readOrCreate(@Nullable String categoryName) {
if (categoryList.size() >= 1) return categoryList.get(0);
else return repo.create(new Category(categoryName));
}
-}
\ No newline at end of file
+
+ @NonNull
+ public List readFiltered() {
+ List filteredList = new ArrayList<>();
+
+ for (Category category : readAll()) {
+ if (!filteredCategories.contains(category.getName())) filteredList.add(category);
+ }
+
+ return filteredList;
+ }
+
+ /**
+ * @param category to disable when request filtered list.
+ */
+ public void disableCategory(@Nullable String category) {
+ filteredCategories.add(category);
+ preferenceController.writeFilteredCategories(filteredCategories);
+ }
+
+ /**
+ * @param category to enable when request filtered list.
+ */
+ public void enableCategory(@Nullable String category) {
+ filteredCategories.remove(category);
+ preferenceController.writeFilteredCategories(filteredCategories);
+ }
+}
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 d54f1be..af86465 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
@@ -52,8 +52,9 @@ public AccountController providesAccountController(IRepo accountRepo,
@Provides
@NonNull
@Singleton
- public CategoryController providesCategoryController(IRepo categoryRepo) {
- return new CategoryController(categoryRepo);
+ public CategoryController providesCategoryController(IRepo categoryRepo,
+ PreferenceController preferenceController) {
+ return new CategoryController(categoryRepo, preferenceController);
}
@Provides
diff --git a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/CategoryAutoCompleter.java b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/CategoryAutoCompleter.java
index d1a7d87..86ea779 100644
--- a/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/CategoryAutoCompleter.java
+++ b/app/src/main/java/com/blogspot/e_kanivets/moneytracker/util/CategoryAutoCompleter.java
@@ -14,11 +14,13 @@
*/
public class CategoryAutoCompleter {
private List categoryList;
+ private CategoryController categoryController;
public CategoryAutoCompleter(CategoryController categoryController) {
+ this.categoryController = categoryController;
categoryList = new ArrayList<>();
- for (Category category : categoryController.readAll()) {
+ for (Category category : categoryController.readFiltered()) {
categoryList.add(category.getName());
}
}
@@ -32,4 +34,9 @@ public List completeByPart(String part) {
return resultList;
}
+
+ public void removeFromAutoComplete(String category) {
+ categoryList.remove(category);
+ categoryController.disableCategory(category);
+ }
}
diff --git a/app/src/main/res/drawable/ic_cancel.xml b/app/src/main/res/drawable/ic_cancel.xml
new file mode 100644
index 0000000..e247f84
--- /dev/null
+++ b/app/src/main/res/drawable/ic_cancel.xml
@@ -0,0 +1,13 @@
+
+
+
diff --git a/app/src/main/res/layout/view_category_item.xml b/app/src/main/res/layout/view_category_item.xml
index 88e399c..cb6c6b2 100644
--- a/app/src/main/res/layout/view_category_item.xml
+++ b/app/src/main/res/layout/view_category_item.xml
@@ -1,7 +1,28 @@
-
\ No newline at end of file
+ android:gravity="center_vertical"
+ android:orientation="horizontal"
+ android:paddingEnd="@dimen/half_margin"
+ android:paddingStart="@dimen/normal_margin"
+ tools:ignore="UseCompoundDrawables">
+
+
+
+
+
+
diff --git a/app/src/test/java/com/blogspot/e_kanivets/moneytracker/controller/data/CategoryControllerTest.java b/app/src/test/java/com/blogspot/e_kanivets/moneytracker/controller/data/CategoryControllerTest.java
index 057d1bd..c961f01 100644
--- a/app/src/test/java/com/blogspot/e_kanivets/moneytracker/controller/data/CategoryControllerTest.java
+++ b/app/src/test/java/com/blogspot/e_kanivets/moneytracker/controller/data/CategoryControllerTest.java
@@ -3,10 +3,13 @@
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.data.Category;
import com.blogspot.e_kanivets.moneytracker.repo.base.IRepo;
+import org.junit.Before;
import org.junit.Test;
+import org.mockito.Mockito;
import java.util.ArrayList;
import java.util.List;
@@ -20,10 +23,16 @@
* @author Evgenii Kanivets
*/
public class CategoryControllerTest {
+ private PreferenceController mockPrefs;
+
+ @Before
+ public void setUp() throws Exception {
+ mockPrefs = Mockito.mock(PreferenceController.class);
+ }
@Test
public void testReadOrCreate() throws Exception {
- CategoryController categoryController = new CategoryController(emptyRepo);
+ CategoryController categoryController = new CategoryController(emptyRepo, mockPrefs);
assertNull(categoryController.readOrCreate(null));
assertNull(categoryController.readOrCreate(""));
@@ -34,7 +43,7 @@ public void testReadOrCreate() throws Exception {
result = categoryController.readOrCreate("category 1");
assertEquals("category 1", result.getName());
- categoryController = new CategoryController(repo);
+ categoryController = new CategoryController(repo, mockPrefs);
result = categoryController.readOrCreate("category 1");
assertEquals("category 1", result.getName());