From ecd0acb82ba0dea49c3930fc3fe8cacfaef3868c Mon Sep 17 00:00:00 2001 From: Bogdan Melnychuk Date: Mon, 24 Oct 2016 14:39:20 +0200 Subject: [PATCH 01/25] Working on clean architecture implementation --- outlay/app/build.gradle | 7 +- outlay/app/src/main/java/com/outlay/App.java | 2 +- .../CategoriesDraggableGridAdapter.java | 2 +- .../outlay/adapter/CategoriesGridAdapter.java | 2 +- .../com/outlay/adapter/ExpenseAdapter.java | 2 +- .../com/outlay/adapter/ReportAdapter.java | 27 +-- .../listener/OnCategoryClickListener.java | 2 +- .../com/outlay/api/OutlayDatabaseApi.java | 41 ++-- .../java/com/outlay/di/module/AppModule.java | 24 +++ .../java/com/outlay/di/module/DaoModule.java | 23 +++ .../java/com/outlay/executor/JobExecutor.java | 58 ++++++ .../java/com/outlay/executor/UIThread.java | 23 +++ .../java/com/outlay/impl/CategoryAdapter.java | 31 ++++ .../outlay/impl/CategoryRepositoryImpl.java | 143 ++++++++++++++ .../outlay/impl/ExpenseRepositoryImpl.java | 86 +++++++++ .../preferences/PreferencesManager.java | 9 +- .../outlay/presenter/CategoriesPresenter.java | 37 ++-- .../presenter/CategoryDetailsPresenter.java | 39 ++-- .../presenter/MainFragmentPresenter.java | 98 ++++------ .../com/outlay/presenter/ReportPresenter.java | 85 +++++---- .../java/com/outlay/utils/FormatUtils.java | 6 + .../main/java/com/outlay/utils/IconUtils.java | 5 + .../outlay/view/activity/BaseActivity.java | 5 +- .../view/fragment/CategoriesFragment.java | 2 +- .../fragment/CategoryDetailsFragment.java | 8 +- .../fragment/ExpensesDetailsFragment.java | 2 +- .../view/fragment/ExpensesListFragment.java | 2 +- .../outlay/view/fragment/MainFragment.java | 31 ++-- .../outlay/view/fragment/ReportFragment.java | 4 +- outlay/build.gradle | 2 +- outlay/domain/.gitignore | 1 + outlay/domain/build.gradle | 20 ++ .../com/outlay/core/data/AppPreferences.java | 10 + .../core/executor/DefaultSubscriber.java | 29 +++ .../core/executor/PostExecutionThread.java | 10 + .../outlay/core/executor/ThreadExecutor.java | 9 + .../core/logger/DefaultEmptyLogger.java | 32 ++++ .../java/com/outlay/core/logger/Logger.java | 18 ++ .../com/outlay/core/logger/LoggerFactory.java | 19 ++ .../com/outlay/core}/utils/DateUtils.java | 10 +- .../interactor/CreateExpenseUseCase.java | 39 ++++ .../interactor/DeleteCategoryUseCase.java | 33 ++++ .../interactor/GetCategoriesUseCase.java | 35 ++++ .../domain/interactor/GetCategoryUseCase.java | 33 ++++ .../domain/interactor/GetDateSummary.java | 47 +++++ .../outlay/domain/interactor/InitUseCase.java | 47 +++++ .../domain/interactor/LoadReportUseCase.java | 41 ++++ .../interactor/UpdateCategoriesUseCase.java | 35 ++++ .../interactor/UpdateCategoryUseCase.java | 36 ++++ .../com/outlay/domain/interactor/UseCase.java | 51 +++++ .../com/outlay/domain/model/Category.java | 74 ++++++++ .../com/outlay/domain/model/DateSummary.java | 62 +++++++ .../com/outlay/domain/model/DaySummary.java | 62 +++++++ .../java/com/outlay/domain/model/Expense.java | 61 ++++++ .../java/com/outlay/domain/model/Period.java | 35 ++++ .../java/com/outlay/domain/model/Report.java | 175 ++++++++++++++++++ .../domain/repository/CategoryRepository.java | 25 +++ .../domain/repository/ExpenseRepository.java | 18 ++ .../gradle/wrapper/gradle-wrapper.properties | 4 +- outlay/settings.gradle | 2 +- 60 files changed, 1673 insertions(+), 208 deletions(-) create mode 100644 outlay/app/src/main/java/com/outlay/executor/JobExecutor.java create mode 100644 outlay/app/src/main/java/com/outlay/executor/UIThread.java create mode 100644 outlay/app/src/main/java/com/outlay/impl/CategoryAdapter.java create mode 100644 outlay/app/src/main/java/com/outlay/impl/CategoryRepositoryImpl.java create mode 100644 outlay/app/src/main/java/com/outlay/impl/ExpenseRepositoryImpl.java create mode 100644 outlay/domain/.gitignore create mode 100644 outlay/domain/build.gradle create mode 100644 outlay/domain/src/main/java/com/outlay/core/data/AppPreferences.java create mode 100644 outlay/domain/src/main/java/com/outlay/core/executor/DefaultSubscriber.java create mode 100644 outlay/domain/src/main/java/com/outlay/core/executor/PostExecutionThread.java create mode 100644 outlay/domain/src/main/java/com/outlay/core/executor/ThreadExecutor.java create mode 100644 outlay/domain/src/main/java/com/outlay/core/logger/DefaultEmptyLogger.java create mode 100644 outlay/domain/src/main/java/com/outlay/core/logger/Logger.java create mode 100644 outlay/domain/src/main/java/com/outlay/core/logger/LoggerFactory.java rename outlay/{app/src/main/java/com/outlay => domain/src/main/java/com/outlay/core}/utils/DateUtils.java (90%) create mode 100644 outlay/domain/src/main/java/com/outlay/domain/interactor/CreateExpenseUseCase.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/interactor/DeleteCategoryUseCase.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/interactor/GetCategoriesUseCase.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/interactor/GetCategoryUseCase.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/interactor/GetDateSummary.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/interactor/InitUseCase.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/interactor/LoadReportUseCase.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/interactor/UpdateCategoriesUseCase.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/interactor/UpdateCategoryUseCase.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/interactor/UseCase.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/model/Category.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/model/DateSummary.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/model/DaySummary.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/model/Expense.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/model/Period.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/model/Report.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/repository/CategoryRepository.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/repository/ExpenseRepository.java diff --git a/outlay/app/build.gradle b/outlay/app/build.gradle index 14b910d..5e6e4ab 100644 --- a/outlay/app/build.gradle +++ b/outlay/app/build.gradle @@ -62,11 +62,11 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) + compile project(':domain') //Google - apt "com.google.dagger:dagger-compiler:${libs.dagger}" - compile "com.google.dagger:dagger-compiler:${libs.dagger}" - provided "javax.annotation:jsr250-api:1.0" + compile 'com.google.dagger:dagger:2.7' + apt 'com.google.dagger:dagger-compiler:2.7' //Support compile "com.android.support:support-v13:${libs.supportVersion}" @@ -93,7 +93,6 @@ dependencies { compile 'com.github.PhilJay:MPAndroidChart:v2.2.0' compile 'com.rengwuxian.materialedittext:library:2.1.4' - compile 'joda-time:joda-time:2.9.2' compile('com.crashlytics.sdk.android:crashlytics:2.5.5@aar') { transitive = true; } diff --git a/outlay/app/src/main/java/com/outlay/App.java b/outlay/app/src/main/java/com/outlay/App.java index 5b69a8e..ce22ed9 100644 --- a/outlay/app/src/main/java/com/outlay/App.java +++ b/outlay/app/src/main/java/com/outlay/App.java @@ -20,7 +20,7 @@ public class App extends Application { @Override public void onCreate() { super.onCreate(); - Fabric.with(this, new Crashlytics()); +// Fabric.with(this, new Crashlytics()); initializeInjector(); } diff --git a/outlay/app/src/main/java/com/outlay/adapter/CategoriesDraggableGridAdapter.java b/outlay/app/src/main/java/com/outlay/adapter/CategoriesDraggableGridAdapter.java index 317f590..5728ffe 100644 --- a/outlay/app/src/main/java/com/outlay/adapter/CategoriesDraggableGridAdapter.java +++ b/outlay/app/src/main/java/com/outlay/adapter/CategoriesDraggableGridAdapter.java @@ -10,7 +10,7 @@ import com.github.johnkil.print.PrintView; import com.outlay.R; import com.outlay.adapter.listener.OnCategoryClickListener; -import com.outlay.dao.Category; +import com.outlay.domain.model.Category; import com.outlay.utils.IconUtils; import com.outlay.view.helper.itemtouch.ItemTouchHelperAdapter; import com.outlay.view.helper.itemtouch.ItemTouchHelperViewHolder; diff --git a/outlay/app/src/main/java/com/outlay/adapter/CategoriesGridAdapter.java b/outlay/app/src/main/java/com/outlay/adapter/CategoriesGridAdapter.java index 8d2f644..99dad4f 100644 --- a/outlay/app/src/main/java/com/outlay/adapter/CategoriesGridAdapter.java +++ b/outlay/app/src/main/java/com/outlay/adapter/CategoriesGridAdapter.java @@ -9,7 +9,7 @@ import com.github.johnkil.print.PrintView; import com.outlay.R; import com.outlay.adapter.listener.OnCategoryClickListener; -import com.outlay.dao.Category; +import com.outlay.domain.model.Category; import com.outlay.utils.IconUtils; import java.util.ArrayList; diff --git a/outlay/app/src/main/java/com/outlay/adapter/ExpenseAdapter.java b/outlay/app/src/main/java/com/outlay/adapter/ExpenseAdapter.java index 7555668..40b471b 100644 --- a/outlay/app/src/main/java/com/outlay/adapter/ExpenseAdapter.java +++ b/outlay/app/src/main/java/com/outlay/adapter/ExpenseAdapter.java @@ -9,7 +9,7 @@ import com.github.johnkil.print.PrintView; import com.outlay.R; import com.outlay.dao.Expense; -import com.outlay.utils.DateUtils; +import com.outlay.core.utils.DateUtils; import com.outlay.utils.FormatUtils; import com.outlay.utils.IconUtils; diff --git a/outlay/app/src/main/java/com/outlay/adapter/ReportAdapter.java b/outlay/app/src/main/java/com/outlay/adapter/ReportAdapter.java index 4c69c0c..f957ec6 100644 --- a/outlay/app/src/main/java/com/outlay/adapter/ReportAdapter.java +++ b/outlay/app/src/main/java/com/outlay/adapter/ReportAdapter.java @@ -16,11 +16,12 @@ import com.github.mikephil.charting.highlight.Highlight; import com.github.mikephil.charting.listener.OnChartValueSelectedListener; import com.outlay.R; -import com.outlay.model.Report; +import com.outlay.domain.model.Report; import com.outlay.utils.FormatUtils; import com.outlay.utils.IconUtils; import com.outlay.view.progress.ProgressLayout; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; @@ -83,12 +84,12 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { if (holder instanceof ReportViewHolder) { ReportViewHolder reportHolder = (ReportViewHolder) holder; Report currentReport = reports.get(position - 1); - reportHolder.amountText.setText(FormatUtils.formatAmount(currentReport.getAmount())); - reportHolder.titleText.setText(currentReport.getTitle()); - IconUtils.loadCategoryIcon(currentReport.getIcon(), reportHolder.icon); + reportHolder.amountText.setText(FormatUtils.formatAmount(currentReport.getTotalAmount())); + reportHolder.titleText.setText(currentReport.getCategory().getTitle()); + IconUtils.loadCategoryIcon(currentReport.getCategory().getIcon(), reportHolder.icon); reportHolder.progressLayout.setMaxProgress((int) (maxProgress * 10)); - reportHolder.progressLayout.setCurrentProgress((int) (currentReport.getAmount() * 10)); - reportHolder.icon.setIconColor(currentReport.getColor()); + reportHolder.progressLayout.setCurrentProgress(currentReport.getTotalAmount().multiply(new BigDecimal(10)).intValue()); + reportHolder.icon.setIconColor(currentReport.getCategory().getColor()); reportHolder.reportContainer.setOnClickListener(v -> { if (onItemClickListener != null) { onItemClickListener.onItemClicked(currentReport); @@ -168,10 +169,10 @@ private void updateChartData(List reports, PieChart chart) { double sum = 0; for (int i = 0; i < reports.size(); i++) { Report r = reports.get(i); - sum += r.getAmount(); - entries.add(new Entry((int) (r.getAmount() * 1000), i)); - labels.add(r.getTitle()); - colors.add(r.getColor()); + sum += r.getTotalAmount().doubleValue(); + entries.add(new Entry((int) (r.getTotalAmount().doubleValue() * 1000), i)); + labels.add(r.getCategory().getTitle()); + colors.add(r.getCategory().getColor()); } PieDataSet dataSet = new PieDataSet(entries, "Outlay"); @@ -180,7 +181,7 @@ private void updateChartData(List reports, PieChart chart) { dataSet.setColors(colors); PieData data = new PieData(labels, dataSet); - data.setValueFormatter((value, entry, dataSetIndex, viewPortHandler) -> FormatUtils.formatAmount((double)value / 1000)); + data.setValueFormatter((value, entry, dataSetIndex, viewPortHandler) -> FormatUtils.formatAmount((double) value / 1000)); data.setValueTextSize(11f); data.setValueTextColor(Color.WHITE); chart.setData(data); @@ -192,8 +193,8 @@ private void updateChartData(List reports, PieChart chart) { private double getMaxProgress() { double max = -1; for (Report r : reports) { - if (max < r.getAmount()) { - max = r.getAmount(); + if (max < r.getTotalAmount().doubleValue()) { + max = r.getTotalAmount().doubleValue(); } } return max; diff --git a/outlay/app/src/main/java/com/outlay/adapter/listener/OnCategoryClickListener.java b/outlay/app/src/main/java/com/outlay/adapter/listener/OnCategoryClickListener.java index 83324d7..7cc82b7 100644 --- a/outlay/app/src/main/java/com/outlay/adapter/listener/OnCategoryClickListener.java +++ b/outlay/app/src/main/java/com/outlay/adapter/listener/OnCategoryClickListener.java @@ -1,7 +1,7 @@ package com.outlay.adapter.listener; -import com.outlay.dao.Category; +import com.outlay.domain.model.Category; /** * Created by Bogdan Melnychuk on 1/27/16. diff --git a/outlay/app/src/main/java/com/outlay/api/OutlayDatabaseApi.java b/outlay/app/src/main/java/com/outlay/api/OutlayDatabaseApi.java index bf86cb2..da78e8c 100644 --- a/outlay/app/src/main/java/com/outlay/api/OutlayDatabaseApi.java +++ b/outlay/app/src/main/java/com/outlay/api/OutlayDatabaseApi.java @@ -8,7 +8,7 @@ import com.outlay.dao.Expense; import com.outlay.dao.ExpenseDao; import com.outlay.model.Summary; -import com.outlay.utils.DateUtils; +import com.outlay.core.utils.DateUtils; import java.util.ArrayList; import java.util.Date; @@ -48,7 +48,7 @@ public void updateCategory(Category category) { } public long insertCategory(Category category) { - return categoryDao.insert(category); + return categoryDao.insertOrReplace(category); } public void deleteCategory(Category category) { @@ -68,8 +68,9 @@ public void deleteExpense(Expense e) { expenseDao.delete(e); } - public long insertExpense(Expense expense) { - return expenseDao.insert(expense); + public Expense insertExpense(Expense expense) { + expenseDao.insert(expense); + return expense; } public void deleteExpensesByCategory(Category category) { @@ -79,7 +80,7 @@ public void deleteExpensesByCategory(Category category) { public Observable> insertCategories(List categories) { return Observable.create(subscriber -> { - categoryDao.insertInTx(categories); + categoryDao.insertOrReplaceInTx(categories); subscriber.onNext(categories); subscriber.onCompleted(); }); @@ -99,20 +100,24 @@ public Observable> getExpenses(Date startDate, Date endDate) { public Observable> getExpenses(Date startDate, Date endDate, Long categoryId) { if (categoryId == null) { - return Observable.defer(() -> Observable.just( - expenseDao.queryBuilder().where( - ExpenseDao.Properties.ReportedAt.ge(startDate), - ExpenseDao.Properties.ReportedAt.le(endDate) - ).list()) - ); + return Observable.create(subscriber -> { + List expenses = expenseDao.queryBuilder().where( + ExpenseDao.Properties.ReportedAt.ge(startDate), + ExpenseDao.Properties.ReportedAt.le(endDate) + ).list(); + subscriber.onNext(expenses); + subscriber.onCompleted(); + }); } else { - return Observable.defer(() -> Observable.just( - expenseDao.queryBuilder().where( - ExpenseDao.Properties.ReportedAt.ge(startDate), - ExpenseDao.Properties.ReportedAt.le(endDate), - ExpenseDao.Properties.CategoryId.eq(categoryId) - ).list()) - ); + return Observable.create(subscriber -> { + List expenses = expenseDao.queryBuilder().where( + ExpenseDao.Properties.ReportedAt.ge(startDate), + ExpenseDao.Properties.ReportedAt.le(endDate), + ExpenseDao.Properties.CategoryId.eq(categoryId) + ).list(); + subscriber.onNext(expenses); + subscriber.onCompleted(); + }); } } diff --git a/outlay/app/src/main/java/com/outlay/di/module/AppModule.java b/outlay/app/src/main/java/com/outlay/di/module/AppModule.java index 0ecc767..858f4f2 100644 --- a/outlay/app/src/main/java/com/outlay/di/module/AppModule.java +++ b/outlay/app/src/main/java/com/outlay/di/module/AppModule.java @@ -1,8 +1,14 @@ package com.outlay.di.module; import android.app.Application; +import android.content.Context; import com.outlay.App; +import com.outlay.core.data.AppPreferences; +import com.outlay.core.executor.PostExecutionThread; +import com.outlay.core.executor.ThreadExecutor; +import com.outlay.executor.JobExecutor; +import com.outlay.executor.UIThread; import com.outlay.preferences.PreferencesManager; import javax.inject.Singleton; @@ -32,4 +38,22 @@ Application provideAppContext() { PreferencesManager providePreferencesManager() { return new PreferencesManager(mApplication); } + + @Provides + @Singleton + PostExecutionThread providePostExecutionThread(UIThread uiThread) { + return uiThread; + } + + @Provides + @Singleton + ThreadExecutor provideThreadExecutor(JobExecutor jobExecutor) { + return jobExecutor; + } + + @Provides + @Singleton + AppPreferences provideAppPreferences() { + return new PreferencesManager(mApplication); + } } diff --git a/outlay/app/src/main/java/com/outlay/di/module/DaoModule.java b/outlay/app/src/main/java/com/outlay/di/module/DaoModule.java index 169012b..1598751 100644 --- a/outlay/app/src/main/java/com/outlay/di/module/DaoModule.java +++ b/outlay/app/src/main/java/com/outlay/di/module/DaoModule.java @@ -3,10 +3,15 @@ import android.app.Application; import android.database.sqlite.SQLiteDatabase; +import com.outlay.api.OutlayDatabaseApi; import com.outlay.dao.CategoryDao; import com.outlay.dao.DaoMaster; import com.outlay.dao.DaoSession; import com.outlay.dao.ExpenseDao; +import com.outlay.domain.repository.CategoryRepository; +import com.outlay.domain.repository.ExpenseRepository; +import com.outlay.impl.CategoryRepositoryImpl; +import com.outlay.impl.ExpenseRepositoryImpl; import javax.inject.Singleton; @@ -55,4 +60,22 @@ public CategoryDao provideCategoryDao(DaoSession session) { public ExpenseDao provideExpenseDaoDao(DaoSession session) { return session.getExpenseDao(); } + + @Provides + @Singleton + public CategoryRepository provideCategoryRepository( + OutlayDatabaseApi outlayDatabaseApi, + Application application + ) { + return new CategoryRepositoryImpl(outlayDatabaseApi, application); + } + + @Provides + @Singleton + public ExpenseRepository provideExpenseRepository( + OutlayDatabaseApi outlayDatabaseApi, + Application application + ) { + return new ExpenseRepositoryImpl(outlayDatabaseApi, application); + } } diff --git a/outlay/app/src/main/java/com/outlay/executor/JobExecutor.java b/outlay/app/src/main/java/com/outlay/executor/JobExecutor.java new file mode 100644 index 0000000..f650570 --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/executor/JobExecutor.java @@ -0,0 +1,58 @@ +package com.outlay.executor; + + +import com.outlay.core.executor.ThreadExecutor; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import javax.inject.Inject; +import javax.inject.Singleton; + +@Singleton +public class JobExecutor implements ThreadExecutor { + + private static final int INITIAL_POOL_SIZE = 3; + private static final int MAX_POOL_SIZE = 5; + + // Sets the amount of time an idle thread waits before terminating + private static final int KEEP_ALIVE_TIME = 10; + + // Sets the Time Unit to seconds + private static final TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS; + + private final BlockingQueue workQueue; + + private final ThreadPoolExecutor threadPoolExecutor; + + private final ThreadFactory threadFactory; + + @Inject + public JobExecutor() { + this.workQueue = new LinkedBlockingQueue<>(); + this.threadFactory = new JobThreadFactory(); + this.threadPoolExecutor = new ThreadPoolExecutor(INITIAL_POOL_SIZE, MAX_POOL_SIZE, + KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT, this.workQueue, this.threadFactory); + } + + @Override + public void execute(Runnable runnable) { + if (runnable == null) { + throw new IllegalArgumentException("Runnable to execute cannot be null"); + } + this.threadPoolExecutor.execute(runnable); + } + + private static class JobThreadFactory implements ThreadFactory { + private static final String THREAD_NAME = "android_"; + private int counter = 0; + + @Override + public Thread newThread(Runnable runnable) { + return new Thread(runnable, THREAD_NAME + counter++); + } + } +} \ No newline at end of file diff --git a/outlay/app/src/main/java/com/outlay/executor/UIThread.java b/outlay/app/src/main/java/com/outlay/executor/UIThread.java new file mode 100644 index 0000000..a19e857 --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/executor/UIThread.java @@ -0,0 +1,23 @@ +package com.outlay.executor; + +import com.outlay.core.executor.PostExecutionThread; + +import javax.inject.Inject; + +import rx.Scheduler; +import rx.android.schedulers.AndroidSchedulers; + +/** + * Created by bmelnychuk on 5/6/16. + */ +public class UIThread implements PostExecutionThread { + + @Inject + public UIThread() { + } + + @Override + public Scheduler getScheduler() { + return AndroidSchedulers.mainThread(); + } +} \ No newline at end of file diff --git a/outlay/app/src/main/java/com/outlay/impl/CategoryAdapter.java b/outlay/app/src/main/java/com/outlay/impl/CategoryAdapter.java new file mode 100644 index 0000000..329dc03 --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/impl/CategoryAdapter.java @@ -0,0 +1,31 @@ +package com.outlay.impl; + +import com.outlay.domain.model.Category; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public class CategoryAdapter { + public static Category toCategory(com.outlay.dao.Category daoCategory) { + Category category = new Category(); + category.setColor(daoCategory.getColor()); + category.setIcon(daoCategory.getIcon()); + category.setOrder(daoCategory.getOrder()); + category.setId(daoCategory.getId()); + category.setTitle(daoCategory.getTitle()); + + return category; + } + + public static com.outlay.dao.Category fromCategory(Category category) { + com.outlay.dao.Category daoCategory = new com.outlay.dao.Category(); + daoCategory.setColor(category.getColor()); + daoCategory.setIcon(category.getIcon()); + daoCategory.setOrder(category.getOrder()); + daoCategory.setId(category.getId()); + daoCategory.setTitle(category.getTitle()); + + return daoCategory; + } +} diff --git a/outlay/app/src/main/java/com/outlay/impl/CategoryRepositoryImpl.java b/outlay/app/src/main/java/com/outlay/impl/CategoryRepositoryImpl.java new file mode 100644 index 0000000..d908fae --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/impl/CategoryRepositoryImpl.java @@ -0,0 +1,143 @@ +package com.outlay.impl; + +import android.content.Context; +import android.support.v4.content.ContextCompat; + +import com.outlay.R; +import com.outlay.api.OutlayDatabaseApi; +import com.outlay.domain.model.Category; +import com.outlay.domain.repository.CategoryRepository; + +import java.util.ArrayList; +import java.util.List; + +import javax.inject.Inject; + +import rx.Observable; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public class CategoryRepositoryImpl implements CategoryRepository { + private OutlayDatabaseApi outlayDatabaseApi; + private Context context; + + @Inject + public CategoryRepositoryImpl( + OutlayDatabaseApi outlayDatabaseApi, + Context context + ) { + this.outlayDatabaseApi = outlayDatabaseApi; + this.context = context; + } + + private static Category category(String title, String icon, int color, int order) { + Category c = new Category(); + c.setTitle(title); + c.setIcon(icon); + c.setColor(color); + c.setOrder(order); + return c; + } + + @Override + public Observable> getDefault() { + return Observable.create(subscriber -> { + List result = new ArrayList<>(); + result.add(category(context.getString(R.string.category_car), "ic_cars", ContextCompat.getColor(context, R.color.blue), 0)); + result.add(category(context.getString(R.string.category_house), "ic_house", ContextCompat.getColor(context, R.color.red), 1)); + result.add(category(context.getString(R.string.category_grocery), "ic_shopping", ContextCompat.getColor(context, R.color.green), 2)); + result.add(category(context.getString(R.string.category_games), "ic_controller", ContextCompat.getColor(context, R.color.purple), 3)); + result.add(category(context.getString(R.string.category_clothes), "ic_t_shirt", ContextCompat.getColor(context, R.color.teal), 4)); + result.add(category(context.getString(R.string.category_tickets), "ic_tag", ContextCompat.getColor(context, R.color.amber), 5)); + result.add(category(context.getString(R.string.category_sport), "ic_weightlifting", ContextCompat.getColor(context, R.color.brown), 6)); + result.add(category(context.getString(R.string.category_travel), "ic_flight", ContextCompat.getColor(context, R.color.cyan), 7)); + subscriber.onNext(result); + subscriber.onCompleted(); + }); + } + + @Override + public Observable> getAll() { + return outlayDatabaseApi.getCategories() + .map(categories -> { + List result = new ArrayList<>(); + for (com.outlay.dao.Category c : categories) { + Category category = new Category(); + category.setColor(c.getColor()); + category.setIcon(c.getIcon()); + category.setOrder(c.getOrder()); + category.setId(c.getId()); + category.setTitle(c.getTitle()); + result.add(category); + } + return result; + }); + } + + @Override + public Observable getById(Long id) { + return Observable.create(subscriber -> { + com.outlay.dao.Category c = outlayDatabaseApi.getCategoryById(id); + Category category = new Category(); + category.setColor(c.getColor()); + category.setIcon(c.getIcon()); + category.setOrder(c.getOrder()); + category.setId(c.getId()); + category.setTitle(c.getTitle()); + + subscriber.onNext(category); + subscriber.onCompleted(); + }); + } + + @Override + public Observable> saveAll(List categories) { + List toInsert = new ArrayList<>(); + for (Category c : categories) { + com.outlay.dao.Category category = new com.outlay.dao.Category(); + category.setColor(c.getColor()); + category.setIcon(c.getIcon()); + category.setOrder(c.getOrder()); + category.setId(c.getId()); + category.setTitle(c.getTitle()); + toInsert.add(category); + } + + return outlayDatabaseApi.insertCategories(toInsert) + .map(storedCategories -> { + List result = new ArrayList<>(); + for (com.outlay.dao.Category c : storedCategories) { + Category category = new Category(); + category.setColor(c.getColor()); + category.setIcon(c.getIcon()); + category.setOrder(c.getOrder()); + category.setId(c.getId()); + category.setTitle(c.getTitle()); + result.add(category); + } + return result; + }); + } + + @Override + public Observable save(Category category) { + return Observable.create(subscriber -> { + com.outlay.dao.Category daoCategory = CategoryAdapter.fromCategory(category); + outlayDatabaseApi.insertCategory(daoCategory); + subscriber.onNext(CategoryAdapter.toCategory(daoCategory)); + subscriber.onCompleted(); + }); + } + + @Override + public Observable remove(Category category) { + return Observable.create(subscriber -> { + com.outlay.dao.Category daoCategory = CategoryAdapter.fromCategory(category); + outlayDatabaseApi.deleteCategory(daoCategory); + subscriber.onNext(CategoryAdapter.toCategory(daoCategory)); + subscriber.onCompleted(); + }); + } +} diff --git a/outlay/app/src/main/java/com/outlay/impl/ExpenseRepositoryImpl.java b/outlay/app/src/main/java/com/outlay/impl/ExpenseRepositoryImpl.java new file mode 100644 index 0000000..548849f --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/impl/ExpenseRepositoryImpl.java @@ -0,0 +1,86 @@ +package com.outlay.impl; + +import android.content.Context; + +import com.outlay.api.OutlayDatabaseApi; +import com.outlay.domain.model.Category; +import com.outlay.domain.model.Expense; +import com.outlay.domain.model.Report; +import com.outlay.domain.repository.ExpenseRepository; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import javax.inject.Inject; + +import rx.Observable; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public class ExpenseRepositoryImpl implements ExpenseRepository { + private OutlayDatabaseApi outlayDatabaseApi; + private Context context; + + @Inject + public ExpenseRepositoryImpl( + OutlayDatabaseApi outlayDatabaseApi, + Context context + ) { + this.outlayDatabaseApi = outlayDatabaseApi; + this.context = context; + } + + @Override + public Observable saveExpense(Expense expense) { + com.outlay.dao.Expense expense1 = new com.outlay.dao.Expense(); + expense1.setId(expense.getId()); + expense1.setAmount(expense.getAmount().doubleValue()); + expense1.setCategoryId(expense.getCategory().getId()); + expense1.setNote(expense.getNote()); + expense1.setReportedAt(expense.getReportedAt()); + + + return Observable.create(subscriber -> { + com.outlay.dao.Expense dbExpense = outlayDatabaseApi.insertExpense(expense1); + + Expense result = new Expense(); + result.setId(dbExpense.getId()); + result.setAmount(new BigDecimal(dbExpense.getAmount().toString())); + result.setNote(dbExpense.getNote()); + result.setReportedAt(dbExpense.getReportedAt()); + + Category category = new Category(); + category.setId(dbExpense.getCategoryId()); + result.setCategory(category); + }); + } + + @Override + public Observable> getExpenses(Date startDate, Date endDate) { + return outlayDatabaseApi.getExpenses(startDate, endDate).map(databaseExpenses -> { + List result = new ArrayList<>(); + for (com.outlay.dao.Expense dbExpense : databaseExpenses) { + Expense exp = new Expense(); + exp.setId(dbExpense.getId()); + exp.setAmount(new BigDecimal(dbExpense.getAmount().toString())); + exp.setNote(dbExpense.getNote()); + exp.setReportedAt(dbExpense.getReportedAt()); + + Category category = new Category(); + category.setId(dbExpense.getCategoryId()); + category.setTitle(dbExpense.getCategory().getTitle()); + category.setColor(dbExpense.getCategory().getColor()); + category.setOrder(dbExpense.getCategory().getOrder()); + category.setIcon(dbExpense.getCategory().getIcon()); + exp.setCategory(category); + result.add(exp); + } + + return result; + }); + } +} diff --git a/outlay/app/src/main/java/com/outlay/preferences/PreferencesManager.java b/outlay/app/src/main/java/com/outlay/preferences/PreferencesManager.java index d1dda4b..bca7aa3 100644 --- a/outlay/app/src/main/java/com/outlay/preferences/PreferencesManager.java +++ b/outlay/app/src/main/java/com/outlay/preferences/PreferencesManager.java @@ -4,7 +4,9 @@ import android.content.SharedPreferences; import android.preference.PreferenceManager; -public class PreferencesManager { +import com.outlay.core.data.AppPreferences; + +public class PreferencesManager implements AppPreferences { private static final String PREF_FIRST_RUN = "_firstRun"; private Context context; @@ -57,4 +59,9 @@ public void putFirstRun(boolean value) { public boolean isFirstRun() { return getBoolean(PREF_FIRST_RUN, true); } + + @Override + public void setFirstRun(boolean firstRun) { + putFirstRun(firstRun); + } } diff --git a/outlay/app/src/main/java/com/outlay/presenter/CategoriesPresenter.java b/outlay/app/src/main/java/com/outlay/presenter/CategoriesPresenter.java index 3f2b66b..16a65a7 100644 --- a/outlay/app/src/main/java/com/outlay/presenter/CategoriesPresenter.java +++ b/outlay/app/src/main/java/com/outlay/presenter/CategoriesPresenter.java @@ -1,26 +1,30 @@ package com.outlay.presenter; -import com.outlay.api.OutlayDatabaseApi; -import com.outlay.dao.Category; +import com.outlay.core.executor.DefaultSubscriber; +import com.outlay.domain.interactor.GetCategoriesUseCase; +import com.outlay.domain.interactor.UpdateCategoriesUseCase; +import com.outlay.domain.model.Category; import com.outlay.view.fragment.CategoriesFragment; import java.util.List; import javax.inject.Inject; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; - /** * Created by Bogdan Melnychuk on 1/21/16. */ public class CategoriesPresenter { - private OutlayDatabaseApi api; private CategoriesFragment view; + private GetCategoriesUseCase getCategoriesUseCase; + private UpdateCategoriesUseCase updateCategoriesUseCase; @Inject - public CategoriesPresenter(OutlayDatabaseApi outlayDatabaseApi) { - this.api = outlayDatabaseApi; + public CategoriesPresenter( + GetCategoriesUseCase getCategoriesUseCase, + UpdateCategoriesUseCase updateCategoriesUseCase + ) { + this.getCategoriesUseCase = getCategoriesUseCase; + this.updateCategoriesUseCase = updateCategoriesUseCase; } public void attachView(CategoriesFragment fragment) { @@ -28,17 +32,18 @@ public void attachView(CategoriesFragment fragment) { } public void loadCategories() { - api.getCategories() - .subscribeOn(Schedulers.newThread()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(response -> view.displayCategories(response)); + getCategoriesUseCase.execute(new DefaultSubscriber>() { + @Override + public void onNext(List categories) { + view.displayCategories(categories); + } + }); } public void updateCategories(List categories) { - api.updateCategories(categories) - .subscribeOn(Schedulers.newThread()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(); + updateCategoriesUseCase.execute(categories, new DefaultSubscriber() { + + }); } } diff --git a/outlay/app/src/main/java/com/outlay/presenter/CategoryDetailsPresenter.java b/outlay/app/src/main/java/com/outlay/presenter/CategoryDetailsPresenter.java index bc0a260..f56e853 100644 --- a/outlay/app/src/main/java/com/outlay/presenter/CategoryDetailsPresenter.java +++ b/outlay/app/src/main/java/com/outlay/presenter/CategoryDetailsPresenter.java @@ -1,7 +1,10 @@ package com.outlay.presenter; -import com.outlay.api.OutlayDatabaseApi; -import com.outlay.dao.Category; +import com.outlay.core.executor.DefaultSubscriber; +import com.outlay.domain.interactor.DeleteCategoryUseCase; +import com.outlay.domain.interactor.GetCategoryUseCase; +import com.outlay.domain.interactor.UpdateCategoryUseCase; +import com.outlay.domain.model.Category; import com.outlay.view.fragment.CategoryDetailsFragment; import javax.inject.Inject; @@ -10,12 +13,20 @@ * Created by Bogdan Melnychuk on 1/21/16. */ public class CategoryDetailsPresenter { - private OutlayDatabaseApi api; private CategoryDetailsFragment view; + private UpdateCategoryUseCase updateCategoryUseCase; + private DeleteCategoryUseCase deleteCategoryUseCase; + private GetCategoryUseCase getCategoryUseCase; @Inject - public CategoryDetailsPresenter(OutlayDatabaseApi outlayDatabaseApi) { - this.api = outlayDatabaseApi; + public CategoryDetailsPresenter( + UpdateCategoryUseCase updateCategoryUseCase, + DeleteCategoryUseCase deleteCategoryUseCase, + GetCategoryUseCase getCategoryUseCase + ) { + this.updateCategoryUseCase = updateCategoryUseCase; + this.deleteCategoryUseCase = deleteCategoryUseCase; + this.getCategoryUseCase = getCategoryUseCase; } public void attachView(CategoryDetailsFragment fragment) { @@ -23,21 +34,21 @@ public void attachView(CategoryDetailsFragment fragment) { } public void loadCategory(Long id) { - view.displayCategory(api.getCategoryById(id)); + getCategoryUseCase.execute(id, new DefaultSubscriber(){ + @Override + public void onNext(Category category) { + view.displayCategory(category); + } + }); + } public void updateCategory(Category category) { - if (category.getId() == null) { - category.setOrder(Integer.MAX_VALUE); - api.insertCategory(category); - } else { - api.updateCategory(category); - } + updateCategoryUseCase.execute(category, new DefaultSubscriber()); } public void deleteCategory(Category category) { - api.deleteExpensesByCategory(category); - api.deleteCategory(category); + deleteCategoryUseCase.execute(category, new DefaultSubscriber()); } } diff --git a/outlay/app/src/main/java/com/outlay/presenter/MainFragmentPresenter.java b/outlay/app/src/main/java/com/outlay/presenter/MainFragmentPresenter.java index 278fab9..6ba4de9 100644 --- a/outlay/app/src/main/java/com/outlay/presenter/MainFragmentPresenter.java +++ b/outlay/app/src/main/java/com/outlay/presenter/MainFragmentPresenter.java @@ -1,40 +1,36 @@ package com.outlay.presenter; -import android.content.Context; -import android.support.v4.content.ContextCompat; - -import com.outlay.R; -import com.outlay.api.OutlayDatabaseApi; -import com.outlay.dao.Category; -import com.outlay.dao.Expense; -import com.outlay.model.Icon; +import com.outlay.core.executor.DefaultSubscriber; +import com.outlay.domain.interactor.CreateExpenseUseCase; +import com.outlay.domain.interactor.GetDateSummary; +import com.outlay.domain.interactor.InitUseCase; +import com.outlay.domain.model.DateSummary; import com.outlay.preferences.PreferencesManager; import com.outlay.view.fragment.MainFragment; -import org.joda.time.DateTime; -import org.joda.time.LocalTime; - -import java.util.ArrayList; import java.util.Date; import java.util.List; import javax.inject.Inject; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; - /** * Created by Bogdan Melnychuk on 1/25/16. */ public class MainFragmentPresenter { - private OutlayDatabaseApi api; private MainFragment view; - private PreferencesManager preferencesManager; + private InitUseCase initUseCase; + private CreateExpenseUseCase createExpenseUseCase; + private GetDateSummary getDateSummaryUseCase; @Inject - public MainFragmentPresenter(OutlayDatabaseApi outlayDatabaseApi, PreferencesManager preferencesManager) { - this.preferencesManager = preferencesManager; - this.api = outlayDatabaseApi; + public MainFragmentPresenter( + InitUseCase initUseCase, + CreateExpenseUseCase createExpenseUseCase, + GetDateSummary getDateSummaryUseCase + ) { + this.initUseCase = initUseCase; + this.createExpenseUseCase = createExpenseUseCase; + this.getDateSummaryUseCase = getDateSummaryUseCase; } public void attachView(MainFragment fragment) { @@ -42,59 +38,27 @@ public void attachView(MainFragment fragment) { } public void loadCategories() { - if (preferencesManager.isFirstRun()) { - List defaultCategories = getDefault(view.getActivity()); - api.insertCategories(defaultCategories) - .subscribeOn(Schedulers.newThread()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(response -> { - preferencesManager.putFirstRun(false); - view.displayCategories(response); - }); - } else { - api.getCategories() - .subscribeOn(Schedulers.newThread()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(response -> view.displayCategories(response)); - } - - + initUseCase.execute(new DefaultSubscriber>() { + @Override + public void onNext(List categories) { + view.displayCategories(categories); + } + }); } public void loadSummary(Date date) { - api.getSummary(date) - .subscribeOn(Schedulers.newThread()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(response -> view.displaySummary(response)); + getDateSummaryUseCase.execute(date, new DefaultSubscriber() { + @Override + public void onNext(DateSummary dateSummary) { + view.displaySummary(dateSummary); + } + }); } - public void insertExpense(Expense expense) { - DateTime dateTime = new DateTime(expense.getReportedAt().getTime()); - dateTime = dateTime.withTime(LocalTime.now()); - expense.setReportedAt(dateTime.toDate()); - api.insertExpense(expense); - } - - public static List getDefault(Context context) { - List result = new ArrayList<>(); - result.add(category(context.getString(R.string.category_car), "ic_cars", ContextCompat.getColor(context, R.color.blue), 0)); - result.add(category(context.getString(R.string.category_house), "ic_house", ContextCompat.getColor(context, R.color.red), 1)); - result.add(category(context.getString(R.string.category_grocery), "ic_shopping", ContextCompat.getColor(context, R.color.green), 2)); - result.add(category(context.getString(R.string.category_games), "ic_controller", ContextCompat.getColor(context, R.color.purple), 3)); - result.add(category(context.getString(R.string.category_clothes), "ic_t_shirt", ContextCompat.getColor(context, R.color.teal), 4)); - result.add(category(context.getString(R.string.category_tickets), "ic_tag", ContextCompat.getColor(context, R.color.amber), 5)); - result.add(category(context.getString(R.string.category_sport), "ic_weightlifting", ContextCompat.getColor(context, R.color.brown), 6)); - result.add(category(context.getString(R.string.category_travel), "ic_flight", ContextCompat.getColor(context, R.color.cyan), 7)); - return result; - } + public void insertExpense(com.outlay.domain.model.Expense expense) { + createExpenseUseCase.execute(expense, new DefaultSubscriber() { - private static Category category(String title, String icon, int color, int order) { - Category c = new Category(); - c.setTitle(title); - c.setIcon(icon); - c.setColor(color); - c.setOrder(order); - return c; + }); } } diff --git a/outlay/app/src/main/java/com/outlay/presenter/ReportPresenter.java b/outlay/app/src/main/java/com/outlay/presenter/ReportPresenter.java index 9ef7c9d..017d7c2 100644 --- a/outlay/app/src/main/java/com/outlay/presenter/ReportPresenter.java +++ b/outlay/app/src/main/java/com/outlay/presenter/ReportPresenter.java @@ -1,32 +1,32 @@ package com.outlay.presenter; import com.outlay.api.OutlayDatabaseApi; -import com.outlay.dao.Expense; -import com.outlay.model.Report; -import com.outlay.utils.DateUtils; +import com.outlay.core.executor.DefaultSubscriber; +import com.outlay.core.utils.DateUtils; +import com.outlay.domain.interactor.LoadReportUseCase; +import com.outlay.domain.model.Period; +import com.outlay.domain.model.Report; import com.outlay.view.Page; import com.outlay.view.fragment.ReportFragment; import java.util.ArrayList; import java.util.Date; -import java.util.Map; -import java.util.TreeMap; import javax.inject.Inject; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; - /** * Created by Bogdan Melnychuk on 1/21/16. */ public class ReportPresenter { - private OutlayDatabaseApi api; private ReportFragment view; + private LoadReportUseCase loadReportUseCase; @Inject - public ReportPresenter(OutlayDatabaseApi outlayDatabaseApi) { - this.api = outlayDatabaseApi; + public ReportPresenter( + OutlayDatabaseApi outlayDatabaseApi, + LoadReportUseCase loadReportUseCase + ) { + this.loadReportUseCase = loadReportUseCase; } public void attachView(ReportFragment fragment) { @@ -52,34 +52,43 @@ public void loadReport(Date date, int period) { break; } - api.getExpenses(startDate, endDate) - .subscribeOn(Schedulers.newThread()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(expenses -> { - //TODO Don't like this map here, this logic should be on database side, though I am too lazy now - Map reportMap = new TreeMap<>((lhs, rhs) -> { - if (lhs == rhs) { - return 0; - } - if (lhs == null) { - return -1; - } - if (rhs == null) { - return 1; - } - return lhs.compareTo(rhs); - }); - for (Expense e : expenses) { - Report r = reportMap.get(e.getCategory().getTitle()); - if (r == null) { - r = new Report(); - } - r.addExpense(e); - reportMap.put(e.getCategory().getTitle(), r); - } - view.displayReports(new ArrayList<>(reportMap.values())); - }); + loadReportUseCase.execute(new Period(startDate, endDate), new DefaultSubscriber() { + @Override + public void onNext(Report report) { + super.onNext(report); + view.displayReports(new ArrayList<>(report.groupByCategory().values())); + } + }); + +// api.getExpenses(startDate, endDate) +// .subscribeOn(Schedulers.newThread()) +// .observeOn(AndroidSchedulers.mainThread()) +// .subscribe(expenses -> { +// //TODO Don't like this map here, this logic should be on database side, though I am too lazy now +// Map reportMap = new TreeMap<>((lhs, rhs) -> { +// if (lhs == rhs) { +// return 0; +// } +// if (lhs == null) { +// return -1; +// } +// if (rhs == null) { +// return 1; +// } +// return lhs.compareTo(rhs); +// }); +// for (Expense e : expenses) { +// Report r = reportMap.get(e.getCategory().getTitle()); +// if (r == null) { +// r = new Report(); +// } +// r.addExpense(e); +// reportMap.put(e.getCategory().getTitle(), r); +// } +// view.displayReports(new ArrayList<>(reportMap.values())); +// +// }); } public void goToExpensesList(Date date, int selectedPeriod) { diff --git a/outlay/app/src/main/java/com/outlay/utils/FormatUtils.java b/outlay/app/src/main/java/com/outlay/utils/FormatUtils.java index b6bd1d6..3a47f8d 100644 --- a/outlay/app/src/main/java/com/outlay/utils/FormatUtils.java +++ b/outlay/app/src/main/java/com/outlay/utils/FormatUtils.java @@ -1,5 +1,7 @@ package com.outlay.utils; +import java.math.BigDecimal; + /** * Created by Bogdan Melnychuk on 2/10/16. */ @@ -7,4 +9,8 @@ public final class FormatUtils { public static String formatAmount(Double amount) { return String.format("%.2f", amount); } + + public static String formatAmount(BigDecimal amount) { + return String.format("%.2f", amount.doubleValue()); + } } diff --git a/outlay/app/src/main/java/com/outlay/utils/IconUtils.java b/outlay/app/src/main/java/com/outlay/utils/IconUtils.java index 8923d2c..0a22d40 100644 --- a/outlay/app/src/main/java/com/outlay/utils/IconUtils.java +++ b/outlay/app/src/main/java/com/outlay/utils/IconUtils.java @@ -12,6 +12,11 @@ public static void loadCategoryIcon(Category category, PrintView printView) { printView.setIconColor(category.getColor()); } + public static void loadCategoryIcon(com.outlay.domain.model.Category category, PrintView printView) { + loadCategoryIcon(category.getIcon(), printView); + printView.setIconColor(category.getColor()); + } + public static void loadCategoryIcon(String icon, PrintView printView) { printView.setIconFont("fonts/font-outlay.ttf"); printView.setIconCodeRes(ResourceUtils.getIntegerResource(printView.getContext(), icon)); diff --git a/outlay/app/src/main/java/com/outlay/view/activity/BaseActivity.java b/outlay/app/src/main/java/com/outlay/view/activity/BaseActivity.java index a982371..c93dde0 100644 --- a/outlay/app/src/main/java/com/outlay/view/activity/BaseActivity.java +++ b/outlay/app/src/main/java/com/outlay/view/activity/BaseActivity.java @@ -18,7 +18,8 @@ import com.mikepenz.materialdrawer.model.PrimaryDrawerItem; import com.outlay.Constants; import com.outlay.R; -import com.outlay.dao.Category; +import com.outlay.domain.model.Category; +import com.outlay.domain.model.DateSummary; import com.outlay.model.Summary; import com.outlay.utils.IconUtils; import com.outlay.view.alert.Alert; @@ -113,7 +114,7 @@ public void onBackPressed() { } } - public void updateDrawerData(Summary summary) { + public void updateDrawerData(DateSummary summary) { if (headerView != null) { TextView dateAmount = (TextView) headerView.findViewById(R.id.dateAmount); TextView weekAmount = (TextView) headerView.findViewById(R.id.weekAmount); diff --git a/outlay/app/src/main/java/com/outlay/view/fragment/CategoriesFragment.java b/outlay/app/src/main/java/com/outlay/view/fragment/CategoriesFragment.java index 93c2714..fda0093 100644 --- a/outlay/app/src/main/java/com/outlay/view/fragment/CategoriesFragment.java +++ b/outlay/app/src/main/java/com/outlay/view/fragment/CategoriesFragment.java @@ -14,7 +14,7 @@ import com.outlay.App; import com.outlay.R; import com.outlay.adapter.CategoriesDraggableGridAdapter; -import com.outlay.dao.Category; +import com.outlay.domain.model.Category; import com.outlay.presenter.CategoriesPresenter; import com.outlay.utils.ResourceUtils; import com.outlay.view.Page; diff --git a/outlay/app/src/main/java/com/outlay/view/fragment/CategoryDetailsFragment.java b/outlay/app/src/main/java/com/outlay/view/fragment/CategoryDetailsFragment.java index 714fba8..fe21acd 100644 --- a/outlay/app/src/main/java/com/outlay/view/fragment/CategoryDetailsFragment.java +++ b/outlay/app/src/main/java/com/outlay/view/fragment/CategoryDetailsFragment.java @@ -18,7 +18,7 @@ import com.outlay.App; import com.outlay.R; import com.outlay.adapter.IconsGridAdapter; -import com.outlay.dao.Category; +import com.outlay.domain.model.Category; import com.outlay.helper.TextWatcherAdapter; import com.outlay.model.Icon; import com.outlay.presenter.CategoryDetailsPresenter; @@ -137,9 +137,9 @@ public void onTextChanged(CharSequence s, int start, int before, int count) { @Override public void onDestroy() { - if(category.getId() != null) { - category.refresh(); - } +// if(category.getId() != null) { +// category.refresh(); +// } super.onDestroy(); } diff --git a/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesDetailsFragment.java b/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesDetailsFragment.java index bd9beda..f07c5a7 100644 --- a/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesDetailsFragment.java +++ b/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesDetailsFragment.java @@ -20,7 +20,7 @@ import com.outlay.dao.Expense; import com.outlay.helper.TextWatcherAdapter; import com.outlay.presenter.ExpensesDetailsPresenter; -import com.outlay.utils.DateUtils; +import com.outlay.core.utils.DateUtils; import com.outlay.utils.FormatUtils; import com.outlay.utils.IconUtils; import com.outlay.utils.ResourceUtils; diff --git a/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesListFragment.java b/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesListFragment.java index 8abc804..1a7fd4a 100644 --- a/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesListFragment.java +++ b/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesListFragment.java @@ -18,7 +18,7 @@ import com.outlay.dao.Category; import com.outlay.dao.Expense; import com.outlay.presenter.ExpensesListPresenter; -import com.outlay.utils.DateUtils; +import com.outlay.core.utils.DateUtils; import com.outlay.utils.IconUtils; import com.outlay.utils.ResourceUtils; import com.outlay.view.Page; diff --git a/outlay/app/src/main/java/com/outlay/view/fragment/MainFragment.java b/outlay/app/src/main/java/com/outlay/view/fragment/MainFragment.java index 88476f6..7777f1c 100644 --- a/outlay/app/src/main/java/com/outlay/view/fragment/MainFragment.java +++ b/outlay/app/src/main/java/com/outlay/view/fragment/MainFragment.java @@ -22,22 +22,21 @@ import com.outlay.App; import com.outlay.R; import com.outlay.adapter.CategoriesGridAdapter; -import com.outlay.dao.Category; -import com.outlay.dao.Expense; +import com.outlay.domain.model.DateSummary; import com.outlay.helper.TextWatcherAdapter; import com.outlay.model.Summary; import com.outlay.presenter.MainFragmentPresenter; -import com.outlay.utils.DateUtils; +import com.outlay.core.utils.DateUtils; import com.outlay.utils.DeviceUtils; import com.outlay.utils.ResourceUtils; import com.outlay.view.Page; import com.outlay.view.activity.BaseActivity; -import com.outlay.view.alert.Alert; import com.outlay.view.dialog.DatePickerFragment; import com.outlay.view.numpad.NumpadEditable; import com.outlay.view.numpad.NumpadView; import com.outlay.view.numpad.SimpleNumpadValidator; +import java.math.BigDecimal; import java.util.Calendar; import java.util.Date; import java.util.List; @@ -147,9 +146,9 @@ public void onTextChanged(CharSequence s, int start, int before, int count) { adapter = new CategoriesGridAdapter(new CategoriesGridAdapter.Style(categoriesGridHeight / 2)); adapter.setOnCategoryClickListener(c -> { if (validator.valid(amountText.getText().toString())) { - Expense e = new Expense(); + com.outlay.domain.model.Expense e = new com.outlay.domain.model.Expense(); e.setCategory(c); - e.setAmount(Double.valueOf(amountText.getText().toString())); + e.setAmount(new BigDecimal(amountText.getText().toString())); e.setReportedAt(selectedDate); presenter.insertExpense(e); presenter.loadSummary(new Date()); @@ -157,13 +156,13 @@ public void onTextChanged(CharSequence s, int start, int before, int count) { String message = getString(R.string.info_expense_created); message = String.format(message, e.getAmount(), e.getCategory().getTitle()); - Alert.info(getRootView(), message, - v -> { - e.delete(); - presenter.loadSummary(new Date()); - amountText.setText(String.valueOf(e.getAmount())); - } - ); +// Alert.info(getRootView(), message, +// v -> { +// e.delete(); +// presenter.loadSummary(new Date()); +// amountText.setText(String.valueOf(e.getAmount())); +// } +// ); } else { validator.onInvalidInput(amountText.getText().toString()); } @@ -245,11 +244,11 @@ public void inputError() { amountText.startAnimation(shakeAnimation); } - public void displaySummary(Summary summary) { - ((BaseActivity) getActivity()).updateDrawerData(summary); + public void displaySummary(DateSummary dateSummary) { + ((BaseActivity) getActivity()).updateDrawerData(dateSummary); } - public void displayCategories(List categoryList) { + public void displayCategories(List categoryList) { adapter.setItems(categoryList); } } diff --git a/outlay/app/src/main/java/com/outlay/view/fragment/ReportFragment.java b/outlay/app/src/main/java/com/outlay/view/fragment/ReportFragment.java index f0332b2..a4c5292 100644 --- a/outlay/app/src/main/java/com/outlay/view/fragment/ReportFragment.java +++ b/outlay/app/src/main/java/com/outlay/view/fragment/ReportFragment.java @@ -16,10 +16,10 @@ import com.outlay.App; import com.outlay.R; import com.outlay.adapter.ReportAdapter; +import com.outlay.domain.model.Report; import com.outlay.helper.OnTabSelectedListenerAdapter; -import com.outlay.model.Report; import com.outlay.presenter.ReportPresenter; -import com.outlay.utils.DateUtils; +import com.outlay.core.utils.DateUtils; import com.outlay.utils.ResourceUtils; import com.outlay.view.dialog.DatePickerFragment; diff --git a/outlay/build.gradle b/outlay/build.gradle index aa8aa5f..5c76376 100644 --- a/outlay/build.gradle +++ b/outlay/build.gradle @@ -30,7 +30,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:1.5.0' + classpath 'com.android.tools.build:gradle:2.2.1' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' classpath 'me.tatarka:gradle-retrolambda:3.1.0' classpath 'com.android.databinding:dataBinder:1.0-rc4' diff --git a/outlay/domain/.gitignore b/outlay/domain/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/outlay/domain/.gitignore @@ -0,0 +1 @@ +/build diff --git a/outlay/domain/build.gradle b/outlay/domain/build.gradle new file mode 100644 index 0000000..6d3545f --- /dev/null +++ b/outlay/domain/build.gradle @@ -0,0 +1,20 @@ +apply plugin: 'java' +apply plugin: 'me.tatarka.retrolambda' + +sourceCompatibility = 1.8 +targetCompatibility = 1.8 + +configurations { + provided +} + +def cfg = rootProject.ext.configuration +def libs = rootProject.ext.libraries + +dependencies { + compile fileTree(dir: 'libs', include: ['*.jar']) + compile "io.reactivex:rxjava:${libs.rxjava}" + compile "com.google.code.gson:gson:2.6.2" + compile "com.google.dagger:dagger:2.7" + compile 'joda-time:joda-time:2.9.2' +} \ No newline at end of file diff --git a/outlay/domain/src/main/java/com/outlay/core/data/AppPreferences.java b/outlay/domain/src/main/java/com/outlay/core/data/AppPreferences.java new file mode 100644 index 0000000..8b37e7a --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/core/data/AppPreferences.java @@ -0,0 +1,10 @@ +package com.outlay.core.data; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public interface AppPreferences { + boolean isFirstRun(); + void setFirstRun(boolean firstRun); +} diff --git a/outlay/domain/src/main/java/com/outlay/core/executor/DefaultSubscriber.java b/outlay/domain/src/main/java/com/outlay/core/executor/DefaultSubscriber.java new file mode 100644 index 0000000..869a973 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/core/executor/DefaultSubscriber.java @@ -0,0 +1,29 @@ +package com.outlay.core.executor; + + +import com.outlay.core.logger.Logger; +import com.outlay.core.logger.LoggerFactory; + +import rx.Subscriber; + +/** + * Created by Bogdan Melnychuk on 2/19/16. + */ +public class DefaultSubscriber extends Subscriber { + private static Logger log = LoggerFactory.getLogger(); + + @Override + public void onCompleted() { + + } + + @Override + public void onError(Throwable e) { + log.error("Rx uncaught error.", e); + } + + @Override + public void onNext(T t) { + + } +} diff --git a/outlay/domain/src/main/java/com/outlay/core/executor/PostExecutionThread.java b/outlay/domain/src/main/java/com/outlay/core/executor/PostExecutionThread.java new file mode 100644 index 0000000..925cd89 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/core/executor/PostExecutionThread.java @@ -0,0 +1,10 @@ +package com.outlay.core.executor; + +import rx.Scheduler; + +/** + * Created by bmelnychuk on 5/6/16. + */ +public interface PostExecutionThread { + Scheduler getScheduler(); +} diff --git a/outlay/domain/src/main/java/com/outlay/core/executor/ThreadExecutor.java b/outlay/domain/src/main/java/com/outlay/core/executor/ThreadExecutor.java new file mode 100644 index 0000000..b0cf3ff --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/core/executor/ThreadExecutor.java @@ -0,0 +1,9 @@ +package com.outlay.core.executor; + +import java.util.concurrent.Executor; + +/** + * Created by bmelnychuk on 5/6/16. + */ +public interface ThreadExecutor extends Executor { +} diff --git a/outlay/domain/src/main/java/com/outlay/core/logger/DefaultEmptyLogger.java b/outlay/domain/src/main/java/com/outlay/core/logger/DefaultEmptyLogger.java new file mode 100644 index 0000000..2adca42 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/core/logger/DefaultEmptyLogger.java @@ -0,0 +1,32 @@ +package com.outlay.core.logger; + +/** + * Created by Bogdan Melnychuk on 4/23/16. + */ +public class DefaultEmptyLogger implements Logger { + private static final String TAG = "API"; + + @Override + public void info(String message) { + } + + @Override + public void warn(String message) { + } + + @Override + public void warn(String message, Throwable e) { + } + + @Override + public void debug(String message) { + } + + @Override + public void error(String message) { + } + + @Override + public void error(String message, Throwable e) { + } +} diff --git a/outlay/domain/src/main/java/com/outlay/core/logger/Logger.java b/outlay/domain/src/main/java/com/outlay/core/logger/Logger.java new file mode 100644 index 0000000..28bebeb --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/core/logger/Logger.java @@ -0,0 +1,18 @@ +package com.outlay.core.logger; + +/** + * Created by Bogdan Melnychuk on 4/23/16. + */ +public interface Logger { + void info(String message); + + void warn(String message); + + void warn(String message, Throwable e); + + void debug(String message); + + void error(String message); + + void error(String message, Throwable e); +} diff --git a/outlay/domain/src/main/java/com/outlay/core/logger/LoggerFactory.java b/outlay/domain/src/main/java/com/outlay/core/logger/LoggerFactory.java new file mode 100644 index 0000000..e94f3d8 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/core/logger/LoggerFactory.java @@ -0,0 +1,19 @@ +package com.outlay.core.logger; + +/** + * Created by Bogdan Melnychuk on 4/23/16. + */ +public class LoggerFactory { + private static Logger logger; + + public static Logger getLogger() { + if (logger == null) { + logger = new DefaultEmptyLogger(); + } + return logger; + } + + public static void registerLogger(Logger logger) { + LoggerFactory.logger = logger; + } +} diff --git a/outlay/app/src/main/java/com/outlay/utils/DateUtils.java b/outlay/domain/src/main/java/com/outlay/core/utils/DateUtils.java similarity index 90% rename from outlay/app/src/main/java/com/outlay/utils/DateUtils.java rename to outlay/domain/src/main/java/com/outlay/core/utils/DateUtils.java index ff1692f..36aa85a 100644 --- a/outlay/app/src/main/java/com/outlay/utils/DateUtils.java +++ b/outlay/domain/src/main/java/com/outlay/core/utils/DateUtils.java @@ -1,4 +1,4 @@ -package com.outlay.utils; +package com.outlay.core.utils; import org.joda.time.DateTime; import org.joda.time.DateTimeConstants; @@ -89,4 +89,12 @@ public static Date getDayEnd(Date date) { LocalTime time = new LocalTime().withHourOfDay(23).withMinuteOfHour(59).withSecondOfMinute(59); return LocalDate.fromDateFields(date).toDateTime(time).toDate(); } + + public static Date getMin(Date date1, Date date2) { + return new DateTime(date1).isAfter(new DateTime(date2)) ? date2 : date1; + } + + public static Date getMax(Date date1, Date date2) { + return new DateTime(date1).isAfter(new DateTime(date2)) ? date1 : date2; + } } diff --git a/outlay/domain/src/main/java/com/outlay/domain/interactor/CreateExpenseUseCase.java b/outlay/domain/src/main/java/com/outlay/domain/interactor/CreateExpenseUseCase.java new file mode 100644 index 0000000..2a9e746 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/domain/interactor/CreateExpenseUseCase.java @@ -0,0 +1,39 @@ +package com.outlay.domain.interactor; + +import com.outlay.core.executor.PostExecutionThread; +import com.outlay.core.executor.ThreadExecutor; +import com.outlay.domain.model.Expense; +import com.outlay.domain.repository.ExpenseRepository; + +import org.joda.time.DateTime; +import org.joda.time.LocalTime; + +import javax.inject.Inject; + +import rx.Observable; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public class CreateExpenseUseCase extends UseCase { + private ExpenseRepository expenseRepository; + + @Inject + public CreateExpenseUseCase( + ThreadExecutor threadExecutor, + PostExecutionThread postExecutionThread, + ExpenseRepository expenseRepository + ) { + super(threadExecutor, postExecutionThread); + this.expenseRepository = expenseRepository; + } + + @Override + protected Observable buildUseCaseObservable(Expense expense) { + DateTime dateTime = new DateTime(expense.getReportedAt().getTime()); + dateTime = dateTime.withTime(LocalTime.now()); + expense.setReportedAt(dateTime.toDate()); + return expenseRepository.saveExpense(expense); + } +} diff --git a/outlay/domain/src/main/java/com/outlay/domain/interactor/DeleteCategoryUseCase.java b/outlay/domain/src/main/java/com/outlay/domain/interactor/DeleteCategoryUseCase.java new file mode 100644 index 0000000..73ca0e8 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/domain/interactor/DeleteCategoryUseCase.java @@ -0,0 +1,33 @@ +package com.outlay.domain.interactor; + +import com.outlay.core.executor.PostExecutionThread; +import com.outlay.core.executor.ThreadExecutor; +import com.outlay.domain.model.Category; +import com.outlay.domain.repository.CategoryRepository; + +import javax.inject.Inject; + +import rx.Observable; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public class DeleteCategoryUseCase extends UseCase { + private CategoryRepository categoryRepository; + + @Inject + public DeleteCategoryUseCase( + ThreadExecutor threadExecutor, + PostExecutionThread postExecutionThread, + CategoryRepository categoryRepository + ) { + super(threadExecutor, postExecutionThread); + this.categoryRepository = categoryRepository; + } + + @Override + protected Observable buildUseCaseObservable(Category category) { + return categoryRepository.remove(category); + } +} diff --git a/outlay/domain/src/main/java/com/outlay/domain/interactor/GetCategoriesUseCase.java b/outlay/domain/src/main/java/com/outlay/domain/interactor/GetCategoriesUseCase.java new file mode 100644 index 0000000..88fca59 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/domain/interactor/GetCategoriesUseCase.java @@ -0,0 +1,35 @@ +package com.outlay.domain.interactor; + +import com.outlay.core.executor.PostExecutionThread; +import com.outlay.core.executor.ThreadExecutor; +import com.outlay.domain.model.Category; +import com.outlay.domain.repository.CategoryRepository; + +import java.util.List; + +import javax.inject.Inject; + +import rx.Observable; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public class GetCategoriesUseCase extends UseCase> { + private CategoryRepository categoryRepository; + + @Inject + public GetCategoriesUseCase( + ThreadExecutor threadExecutor, + PostExecutionThread postExecutionThread, + CategoryRepository categoryRepository + ) { + super(threadExecutor, postExecutionThread); + this.categoryRepository = categoryRepository; + } + + @Override + protected Observable> buildUseCaseObservable(Void aVoid) { + return categoryRepository.getAll(); + } +} diff --git a/outlay/domain/src/main/java/com/outlay/domain/interactor/GetCategoryUseCase.java b/outlay/domain/src/main/java/com/outlay/domain/interactor/GetCategoryUseCase.java new file mode 100644 index 0000000..6f337d8 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/domain/interactor/GetCategoryUseCase.java @@ -0,0 +1,33 @@ +package com.outlay.domain.interactor; + +import com.outlay.core.executor.PostExecutionThread; +import com.outlay.core.executor.ThreadExecutor; +import com.outlay.domain.model.Category; +import com.outlay.domain.repository.CategoryRepository; + +import javax.inject.Inject; + +import rx.Observable; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public class GetCategoryUseCase extends UseCase { + private CategoryRepository categoryRepository; + + @Inject + public GetCategoryUseCase( + ThreadExecutor threadExecutor, + PostExecutionThread postExecutionThread, + CategoryRepository categoryRepository + ) { + super(threadExecutor, postExecutionThread); + this.categoryRepository = categoryRepository; + } + + @Override + protected Observable buildUseCaseObservable(Long categoryId) { + return categoryRepository.getById(categoryId); + } +} diff --git a/outlay/domain/src/main/java/com/outlay/domain/interactor/GetDateSummary.java b/outlay/domain/src/main/java/com/outlay/domain/interactor/GetDateSummary.java new file mode 100644 index 0000000..a2d0981 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/domain/interactor/GetDateSummary.java @@ -0,0 +1,47 @@ +package com.outlay.domain.interactor; + +import com.outlay.core.executor.PostExecutionThread; +import com.outlay.core.executor.ThreadExecutor; +import com.outlay.core.utils.DateUtils; +import com.outlay.domain.model.DateSummary; +import com.outlay.domain.model.Report; +import com.outlay.domain.repository.ExpenseRepository; + +import java.util.Date; + +import javax.inject.Inject; + +import rx.Observable; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public class GetDateSummary extends UseCase { + private ExpenseRepository expenseRepository; + + @Inject + public GetDateSummary( + ThreadExecutor threadExecutor, + PostExecutionThread postExecutionThread, + ExpenseRepository expenseRepository + ) { + super(threadExecutor, postExecutionThread); + this.expenseRepository = expenseRepository; + } + + @Override + protected Observable buildUseCaseObservable(final Date date) { + Date startOfMonth = DateUtils.getMonthStart(date); + Date endOfMonth = DateUtils.getMonthEnd(date); + + return expenseRepository.getExpenses(startOfMonth, endOfMonth) + .map(expenses -> { + Report report = new Report(); + report.setStartDate(startOfMonth); + report.setEndDate(endOfMonth); + report.setExpenses(expenses); + return report.getDateSummary(date); + }); + } +} diff --git a/outlay/domain/src/main/java/com/outlay/domain/interactor/InitUseCase.java b/outlay/domain/src/main/java/com/outlay/domain/interactor/InitUseCase.java new file mode 100644 index 0000000..661ca09 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/domain/interactor/InitUseCase.java @@ -0,0 +1,47 @@ +package com.outlay.domain.interactor; + +import com.outlay.core.data.AppPreferences; +import com.outlay.core.executor.PostExecutionThread; +import com.outlay.core.executor.ThreadExecutor; +import com.outlay.domain.model.Category; +import com.outlay.domain.repository.CategoryRepository; + +import java.util.List; + +import javax.inject.Inject; + +import rx.Observable; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public class InitUseCase extends UseCase> { + private CategoryRepository categoryRepository; + private AppPreferences appPreferences; + + @Inject + public InitUseCase( + ThreadExecutor threadExecutor, + PostExecutionThread postExecutionThread, + CategoryRepository categoryRepository, + AppPreferences appPreferences + ) { + super(threadExecutor, postExecutionThread); + this.categoryRepository = categoryRepository; + this.appPreferences = appPreferences; + } + + @Override + protected Observable> buildUseCaseObservable(Void aVoid) { + if (appPreferences.isFirstRun()) { + return categoryRepository.getDefault() + .switchMap(categories -> { + appPreferences.setFirstRun(false); + return categoryRepository.saveAll(categories); + }); + } else { + return categoryRepository.getAll(); + } + } +} diff --git a/outlay/domain/src/main/java/com/outlay/domain/interactor/LoadReportUseCase.java b/outlay/domain/src/main/java/com/outlay/domain/interactor/LoadReportUseCase.java new file mode 100644 index 0000000..8484907 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/domain/interactor/LoadReportUseCase.java @@ -0,0 +1,41 @@ +package com.outlay.domain.interactor; + +import com.outlay.core.executor.PostExecutionThread; +import com.outlay.core.executor.ThreadExecutor; +import com.outlay.domain.model.Period; +import com.outlay.domain.model.Report; +import com.outlay.domain.repository.ExpenseRepository; + +import javax.inject.Inject; + +import rx.Observable; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public class LoadReportUseCase extends UseCase { + private ExpenseRepository expenseRepository; + + @Inject + public LoadReportUseCase( + ThreadExecutor threadExecutor, + PostExecutionThread postExecutionThread, + ExpenseRepository expenseRepository + ) { + super(threadExecutor, postExecutionThread); + this.expenseRepository = expenseRepository; + } + + @Override + protected Observable buildUseCaseObservable(Period period) { + return expenseRepository.getExpenses(period.getStartDate(), period.getEndDate()) + .map(expenses -> { + Report report = new Report(); + report.setEndDate(period.getEndDate()); + report.setStartDate(period.getStartDate()); + report.setExpenses(expenses); + return report; + }); + } +} diff --git a/outlay/domain/src/main/java/com/outlay/domain/interactor/UpdateCategoriesUseCase.java b/outlay/domain/src/main/java/com/outlay/domain/interactor/UpdateCategoriesUseCase.java new file mode 100644 index 0000000..9116a92 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/domain/interactor/UpdateCategoriesUseCase.java @@ -0,0 +1,35 @@ +package com.outlay.domain.interactor; + +import com.outlay.core.executor.PostExecutionThread; +import com.outlay.core.executor.ThreadExecutor; +import com.outlay.domain.model.Category; +import com.outlay.domain.repository.CategoryRepository; + +import java.util.List; + +import javax.inject.Inject; + +import rx.Observable; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public class UpdateCategoriesUseCase extends UseCase, List> { + private CategoryRepository categoryRepository; + + @Inject + public UpdateCategoriesUseCase( + ThreadExecutor threadExecutor, + PostExecutionThread postExecutionThread, + CategoryRepository categoryRepository + ) { + super(threadExecutor, postExecutionThread); + this.categoryRepository = categoryRepository; + } + + @Override + protected Observable> buildUseCaseObservable(List categories) { + return categoryRepository.saveAll(categories); + } +} \ No newline at end of file diff --git a/outlay/domain/src/main/java/com/outlay/domain/interactor/UpdateCategoryUseCase.java b/outlay/domain/src/main/java/com/outlay/domain/interactor/UpdateCategoryUseCase.java new file mode 100644 index 0000000..1e2e463 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/domain/interactor/UpdateCategoryUseCase.java @@ -0,0 +1,36 @@ +package com.outlay.domain.interactor; + +import com.outlay.core.executor.PostExecutionThread; +import com.outlay.core.executor.ThreadExecutor; +import com.outlay.domain.model.Category; +import com.outlay.domain.repository.CategoryRepository; + +import javax.inject.Inject; + +import rx.Observable; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public class UpdateCategoryUseCase extends UseCase { + private CategoryRepository categoryRepository; + + @Inject + public UpdateCategoryUseCase( + ThreadExecutor threadExecutor, + PostExecutionThread postExecutionThread, + CategoryRepository categoryRepository + ) { + super(threadExecutor, postExecutionThread); + this.categoryRepository = categoryRepository; + } + + @Override + protected Observable buildUseCaseObservable(Category category) { + if (category.getId() == null) { + category.setOrder(Integer.MAX_VALUE); + } + return categoryRepository.save(category); + } +} diff --git a/outlay/domain/src/main/java/com/outlay/domain/interactor/UseCase.java b/outlay/domain/src/main/java/com/outlay/domain/interactor/UseCase.java new file mode 100644 index 0000000..4d5a2ec --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/domain/interactor/UseCase.java @@ -0,0 +1,51 @@ +package com.outlay.domain.interactor; + +import com.outlay.core.executor.PostExecutionThread; +import com.outlay.core.executor.ThreadExecutor; + +import rx.Observable; +import rx.Subscriber; +import rx.Subscription; +import rx.schedulers.Schedulers; +import rx.subscriptions.Subscriptions; + +/** + * Created by bmelnychuk on 5/10/16. + */ +public abstract class UseCase { + private final ThreadExecutor threadExecutor; + private final PostExecutionThread postExecutionThread; + + private Subscription subscription = Subscriptions.unsubscribed(); + + protected UseCase(ThreadExecutor threadExecutor, + PostExecutionThread postExecutionThread) { + this.threadExecutor = threadExecutor; + this.postExecutionThread = postExecutionThread; + } + + protected abstract Observable buildUseCaseObservable(INPUT input); + + public void execute(INPUT input, Subscriber useCaseSubscriber) { + this.unsubscribe(); + + this.subscription = this.buildUseCaseObservable(input) + .subscribeOn(Schedulers.from(threadExecutor)) + .observeOn(postExecutionThread.getScheduler()) + .subscribe(useCaseSubscriber); + } + + public void execute(Subscriber useCaseSubscriber) { + execute(null, useCaseSubscriber); + } + + public void unsubscribe() { + if (!subscription.isUnsubscribed()) { + subscription.unsubscribe(); + } + } + + public boolean isUnsubscribed() { + return this.subscription.isUnsubscribed(); + } +} diff --git a/outlay/domain/src/main/java/com/outlay/domain/model/Category.java b/outlay/domain/src/main/java/com/outlay/domain/model/Category.java new file mode 100644 index 0000000..e3a3bd1 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/domain/model/Category.java @@ -0,0 +1,74 @@ +package com.outlay.domain.model; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public class Category { + private Long id; + private String title; + private String icon; + private int order; + private int color; + + public Long getId() { + return id; + } + + public Category setId(Long id) { + this.id = id; + return this; + } + + public String getTitle() { + return title; + } + + public Category setTitle(String title) { + this.title = title; + return this; + } + + public String getIcon() { + return icon; + } + + public Category setIcon(String icon) { + this.icon = icon; + return this; + } + + public int getOrder() { + return order; + } + + public Category setOrder(int order) { + this.order = order; + return this; + } + + public int getColor() { + return color; + } + + public Category setColor(int color) { + this.color = color; + return this; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + Category category = (Category) o; + + return id != null ? id.equals(category.id) : category.id == null; + + } + + @Override + public int hashCode() { + return id != null ? id.hashCode() : 0; + } +} diff --git a/outlay/domain/src/main/java/com/outlay/domain/model/DateSummary.java b/outlay/domain/src/main/java/com/outlay/domain/model/DateSummary.java new file mode 100644 index 0000000..7df9832 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/domain/model/DateSummary.java @@ -0,0 +1,62 @@ +package com.outlay.domain.model; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public class DateSummary { + private Date date; + private BigDecimal dayAmount; + private BigDecimal weekAmount; + private BigDecimal monthAmount; + private List categories; + + public Date getDate() { + return date; + } + + public DateSummary setDate(Date date) { + this.date = date; + return this; + } + + public BigDecimal getDayAmount() { + return dayAmount; + } + + public DateSummary setDayAmount(BigDecimal dayAmount) { + this.dayAmount = dayAmount; + return this; + } + + public BigDecimal getWeekAmount() { + return weekAmount; + } + + public DateSummary setWeekAmount(BigDecimal weekAmount) { + this.weekAmount = weekAmount; + return this; + } + + public BigDecimal getMonthAmount() { + return monthAmount; + } + + public DateSummary setMonthAmount(BigDecimal monthAmount) { + this.monthAmount = monthAmount; + return this; + } + + public List getCategories() { + return categories; + } + + public DateSummary setCategories(List categories) { + this.categories = categories; + return this; + } +} diff --git a/outlay/domain/src/main/java/com/outlay/domain/model/DaySummary.java b/outlay/domain/src/main/java/com/outlay/domain/model/DaySummary.java new file mode 100644 index 0000000..deec443 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/domain/model/DaySummary.java @@ -0,0 +1,62 @@ +package com.outlay.domain.model; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public class DaySummary { + private Date date; + private BigDecimal dayAmount; + private BigDecimal weekAmount; + private BigDecimal monthAmount; + private List categories; + + public Date getDate() { + return date; + } + + public DaySummary setDate(Date date) { + this.date = date; + return this; + } + + public BigDecimal getDayAmount() { + return dayAmount; + } + + public DaySummary setDayAmount(BigDecimal dayAmount) { + this.dayAmount = dayAmount; + return this; + } + + public BigDecimal getWeekAmount() { + return weekAmount; + } + + public DaySummary setWeekAmount(BigDecimal weekAmount) { + this.weekAmount = weekAmount; + return this; + } + + public BigDecimal getMonthAmount() { + return monthAmount; + } + + public DaySummary setMonthAmount(BigDecimal monthAmount) { + this.monthAmount = monthAmount; + return this; + } + + public List getCategories() { + return categories; + } + + public DaySummary setCategories(List categories) { + this.categories = categories; + return this; + } +} diff --git a/outlay/domain/src/main/java/com/outlay/domain/model/Expense.java b/outlay/domain/src/main/java/com/outlay/domain/model/Expense.java new file mode 100644 index 0000000..06c7674 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/domain/model/Expense.java @@ -0,0 +1,61 @@ +package com.outlay.domain.model; + +import java.math.BigDecimal; +import java.util.Date; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public class Expense { + private Long id; + private String note; + private BigDecimal amount; + private Date reportedAt; + private Category category; + + public Long getId() { + return id; + } + + public Expense setId(Long id) { + this.id = id; + return this; + } + + public String getNote() { + return note; + } + + public Expense setNote(String note) { + this.note = note; + return this; + } + + public BigDecimal getAmount() { + return amount; + } + + public Expense setAmount(BigDecimal amount) { + this.amount = amount; + return this; + } + + public Date getReportedAt() { + return reportedAt; + } + + public Expense setReportedAt(Date reportedAt) { + this.reportedAt = reportedAt; + return this; + } + + public Category getCategory() { + return category; + } + + public Expense setCategory(Category category) { + this.category = category; + return this; + } +} diff --git a/outlay/domain/src/main/java/com/outlay/domain/model/Period.java b/outlay/domain/src/main/java/com/outlay/domain/model/Period.java new file mode 100644 index 0000000..e155e18 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/domain/model/Period.java @@ -0,0 +1,35 @@ +package com.outlay.domain.model; + +import java.util.Date; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public class Period { + private Date startDate; + private Date endDate; + + public Period(Date startDate, Date endDate) { + this.startDate = startDate; + this.endDate = endDate; + } + + public Date getStartDate() { + return startDate; + } + + public Period setStartDate(Date startDate) { + this.startDate = startDate; + return this; + } + + public Date getEndDate() { + return endDate; + } + + public Period setEndDate(Date endDate) { + this.endDate = endDate; + return this; + } +} diff --git a/outlay/domain/src/main/java/com/outlay/domain/model/Report.java b/outlay/domain/src/main/java/com/outlay/domain/model/Report.java new file mode 100644 index 0000000..75f42ad --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/domain/model/Report.java @@ -0,0 +1,175 @@ +package com.outlay.domain.model; + +import com.outlay.core.utils.DateUtils; + +import org.joda.time.DateTime; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public class Report { + private List expenses; + private Date startDate; + private Date endDate; + private Category category; + + public Category getCategory() { + return category; + } + + public Report setCategory(Category category) { + this.category = category; + return this; + } + + public List getExpenses() { + if (expenses == null) { + expenses = new ArrayList<>(); + } + return expenses; + } + + public Report setExpenses(List expenses) { + this.expenses = expenses; + return this; + } + + public Date getStartDate() { + return startDate; + } + + public Report setStartDate(Date startDate) { + this.startDate = startDate; + return this; + } + + public Date getEndDate() { + return endDate; + } + + public Report setEndDate(Date endDate) { + this.endDate = endDate; + return this; + } + + public BigDecimal getTotalAmount() { + BigDecimal result = BigDecimal.ZERO; + + for (Expense expense : expenses) { + result = result.add(expense.getAmount()); + } + + return result; + } + + public BigDecimal getTotalAmount(Date start, Date end) { + BigDecimal result = BigDecimal.ZERO; + + DateTime startDate = new DateTime(start); + DateTime endDate = new DateTime(end); + + for (Expense expense : expenses) { + DateTime expenseDate = new DateTime(expense.getReportedAt()); + if (expenseDate.isAfter(startDate) && expenseDate.isBefore(endDate)) { + result = result.add(expense.getAmount()); + } + } + + return result; + } + + public Map groupByCategory() { + Map result = new HashMap<>(); + + for (Expense expense : expenses) { + Category expenseCategory = expense.getCategory(); + Report reportForCategory = result.get(expenseCategory); + if (reportForCategory == null) { + reportForCategory = new Report(); + reportForCategory.setCategory(expenseCategory); + reportForCategory.setStartDate(expense.getReportedAt()); + reportForCategory.setEndDate(expense.getReportedAt()); + } else { + reportForCategory.setStartDate( + DateUtils.getMin(expense.getReportedAt(), reportForCategory.startDate) + ); + reportForCategory.setEndDate( + DateUtils.getMax(expense.getReportedAt(), reportForCategory.endDate) + ); + } + reportForCategory.getExpenses().add(expense); + + result.put(expenseCategory, reportForCategory); + } + + return result; + } + + + public DateSummary getDateSummary(Date date) { + Date startOfMonth = DateUtils.getMonthStart(date); + Date endOfMonth = DateUtils.getMonthEnd(date); + + Date startOfWeek = DateUtils.getWeekStart(date); + Date endOfWeek = DateUtils.getWeekEnd(date); + + Date startOfDay = DateUtils.getDayStart(date); + Date endOfDay = DateUtils.getDayEnd(date); + + BigDecimal monthAmount = getTotalAmount(startOfMonth, endOfMonth); + BigDecimal weeklyAmount = getTotalAmount(startOfWeek, endOfWeek); + BigDecimal dailyAmount = getTotalAmount(startOfDay, endOfDay); + + DateSummary result = new DateSummary(); + result.setDate(date); + result.setDayAmount(dailyAmount); + result.setWeekAmount(weeklyAmount); + result.setMonthAmount(monthAmount); + result.setCategories(getMostPayedCategories()); + return result; + } + + public List getMostPayedCategories() { + Map pricesPerCategory = new HashMap<>(); + + for (Expense expense : expenses) { + BigDecimal priceForCategory = pricesPerCategory.get(expense.getCategory()); + if (priceForCategory == null) { + priceForCategory = BigDecimal.ZERO; + } + priceForCategory = priceForCategory.add(expense.getAmount()); + pricesPerCategory.put(expense.getCategory(), priceForCategory); + } + + TreeMap treeMap = new TreeMap<>(new MapValueComparator(pricesPerCategory)); + treeMap.putAll(pricesPerCategory); + List result = new ArrayList<>(treeMap.keySet()); + + return result; + } + + private static final class MapValueComparator implements Comparator { + private Map source; + + public MapValueComparator(Map source) { + this.source = source; + } + + @Override + public int compare(K o1, K o2) { + V value1 = source.get(o1); + V value2 = source.get(o2); + return value2.compareTo(value1); + } + } +} diff --git a/outlay/domain/src/main/java/com/outlay/domain/repository/CategoryRepository.java b/outlay/domain/src/main/java/com/outlay/domain/repository/CategoryRepository.java new file mode 100644 index 0000000..9c96eb2 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/domain/repository/CategoryRepository.java @@ -0,0 +1,25 @@ +package com.outlay.domain.repository; + +import com.outlay.domain.model.Category; + +import java.util.List; + +import rx.Observable; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public interface CategoryRepository { + Observable> getDefault(); + + Observable> getAll(); + + Observable getById(Long id); + + Observable> saveAll(List categories); + + Observable save(Category category); + + Observable remove(Category category); +} diff --git a/outlay/domain/src/main/java/com/outlay/domain/repository/ExpenseRepository.java b/outlay/domain/src/main/java/com/outlay/domain/repository/ExpenseRepository.java new file mode 100644 index 0000000..d54b2b6 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/domain/repository/ExpenseRepository.java @@ -0,0 +1,18 @@ +package com.outlay.domain.repository; + +import com.outlay.domain.model.Expense; +import com.outlay.domain.model.Report; + +import java.util.Date; +import java.util.List; + +import rx.Observable; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public interface ExpenseRepository { + Observable saveExpense(Expense expense); + Observable> getExpenses(Date startDate, Date endDate); +} diff --git a/outlay/gradle/wrapper/gradle-wrapper.properties b/outlay/gradle/wrapper/gradle-wrapper.properties index f23df6e..54cbf5c 100644 --- a/outlay/gradle/wrapper/gradle-wrapper.properties +++ b/outlay/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Wed Oct 21 11:34:03 PDT 2015 +#Mon Oct 24 13:17:43 CEST 2016 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-2.8-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip diff --git a/outlay/settings.gradle b/outlay/settings.gradle index 2b0e21a..871bb2d 100644 --- a/outlay/settings.gradle +++ b/outlay/settings.gradle @@ -1 +1 @@ -include ':app', ':daogenerator' +include ':app', ':daogenerator', ':domain' From 6171407cca986a0c634e137e103bc13072f12561 Mon Sep 17 00:00:00 2001 From: Bogdan Melnychuk Date: Tue, 25 Oct 2016 11:34:33 +0200 Subject: [PATCH 02/25] Working on clean architecture implementation --- outlay/app/build.gradle | 4 +- .../com/outlay/api/OutlayDatabaseApi.java | 167 ------------------ .../java/com/outlay/di/module/AppModule.java | 9 +- .../java/com/outlay/di/module/DaoModule.java | 39 ++-- .../AndroidPreferencesManager.java} | 6 +- .../java/com/outlay/impl/CategoryAdapter.java | 31 ---- .../outlay/impl/CategoryRepositoryImpl.java | 76 +------- .../outlay/impl/ExpenseRepositoryImpl.java | 86 --------- .../src/main/java/com/outlay/model/Icon.java | 83 --------- .../main/java/com/outlay/model/Report.java | 60 ------- .../main/java/com/outlay/model/Summary.java | 57 ------ .../presenter/CategoriesPresenter.java | 18 +- .../presenter/CategoryDetailsPresenter.java | 30 ++-- .../mvp/presenter/EnterExpensePresenter.java | 77 ++++++++ .../presenter/ExpensesDetailsPresenter.java | 65 +++++++ .../mvp/presenter/ExpensesListPresenter.java | 35 ++++ .../outlay/mvp/presenter/MvpPresenter.java | 23 +++ .../outlay/mvp/presenter/ReportPresenter.java | 58 ++++++ .../com/outlay/mvp/view/CategoriesView.java | 14 ++ .../outlay/mvp/view/CategoryDetailsView.java | 12 ++ .../com/outlay/mvp/view/EnterExpenseView.java | 19 ++ .../outlay/mvp/view/ExpenseDetailsView.java | 15 ++ .../com/outlay/mvp/view/ExpensesView.java | 11 ++ .../java/com/outlay/mvp/view/MvpView.java | 8 + .../com/outlay/mvp/view/StatisticView.java | 13 ++ .../presenter/ExpensesDetailsPresenter.java | 56 ------ .../presenter/ExpensesListPresenter.java | 41 ----- .../presenter/MainFragmentPresenter.java | 64 ------- .../com/outlay/presenter/ReportPresenter.java | 119 ------------- .../java/com/outlay/utils/FormatUtils.java | 3 +- .../main/java/com/outlay/utils/IconUtils.java | 80 ++++++++- .../src/main/java/com/outlay/view/Page.java | 1 - .../outlay/view/activity/BaseActivity.java | 8 +- .../CategoriesDraggableGridAdapter.java | 4 +- .../adapter/CategoriesGridAdapter.java | 4 +- .../{ => view}/adapter/ExpenseAdapter.java | 4 +- .../{ => view}/adapter/IconsGridAdapter.java | 3 +- .../{ => view}/adapter/ReportAdapter.java | 4 +- .../listener/OnCategoryClickListener.java | 2 +- .../CategoryAutoCompleteAdapter.java | 2 +- .../view/fragment/CategoriesFragment.java | 10 +- .../fragment/CategoryDetailsFragment.java | 34 ++-- .../fragment/ExpensesDetailsFragment.java | 31 ++-- .../view/fragment/ExpensesListFragment.java | 41 +++-- .../outlay/view/fragment/MainFragment.java | 59 ++++--- .../outlay/view/fragment/ReportFragment.java | 45 ++++- .../helper/OnTabSelectedListenerAdapter.java | 2 +- .../{ => view}/helper/TextWatcherAdapter.java | 2 +- outlay/database/.gitignore | 1 + outlay/database/build.gradle | 44 +++++ outlay/database/proguard-rules.pro | 17 ++ outlay/database/src/main/AndroidManifest.xml | 5 + .../adapter/CategoryDatabaseMapper.java | 50 ++++++ .../adapter/ExpenseDatabaseMapper.java | 61 +++++++ .../com/outlay/database}/dao/Category.java | 5 +- .../com/outlay/database}/dao/CategoryDao.java | 4 +- .../com/outlay/database}/dao/DaoMaster.java | 5 +- .../com/outlay/database}/dao/DaoSession.java | 8 +- .../com/outlay/database}/dao/Expense.java | 4 +- .../com/outlay/database}/dao/ExpenseDao.java | 4 +- .../source/CategoryDatabaseSource.java | 107 +++++++++++ .../source/ExpenseDatabaseSource.java | 115 ++++++++++++ .../src/main/java/com/outlay/data/Test.java | 8 + .../repository/ExpenseRepositoryImpl.java | 59 +++++++ .../data/source/CategoryDataSource.java | 23 +++ .../outlay/data/source/ExpenseDataSource.java | 20 +++ .../interactor/DeleteExpenseUseCase.java | 33 ++++ .../domain/interactor/GetExpenseUseCase.java | 33 ++++ .../domain/interactor/LoadReportUseCase.java | 43 +++-- ...seUseCase.java => SaveExpenseUseCase.java} | 4 +- .../outlay/domain/model/ExpensePeriod.java | 50 ++++++ .../java/com/outlay/domain/model/Period.java | 35 ---- .../domain/repository/ExpenseRepository.java | 8 +- outlay/settings.gradle | 2 +- 74 files changed, 1321 insertions(+), 1062 deletions(-) delete mode 100644 outlay/app/src/main/java/com/outlay/api/OutlayDatabaseApi.java rename outlay/app/src/main/java/com/outlay/{preferences/PreferencesManager.java => impl/AndroidPreferencesManager.java} (89%) delete mode 100644 outlay/app/src/main/java/com/outlay/impl/CategoryAdapter.java delete mode 100644 outlay/app/src/main/java/com/outlay/impl/ExpenseRepositoryImpl.java delete mode 100644 outlay/app/src/main/java/com/outlay/model/Icon.java delete mode 100644 outlay/app/src/main/java/com/outlay/model/Report.java delete mode 100644 outlay/app/src/main/java/com/outlay/model/Summary.java rename outlay/app/src/main/java/com/outlay/{ => mvp}/presenter/CategoriesPresenter.java (76%) rename outlay/app/src/main/java/com/outlay/{ => mvp}/presenter/CategoryDetailsPresenter.java (71%) create mode 100644 outlay/app/src/main/java/com/outlay/mvp/presenter/EnterExpensePresenter.java create mode 100644 outlay/app/src/main/java/com/outlay/mvp/presenter/ExpensesDetailsPresenter.java create mode 100644 outlay/app/src/main/java/com/outlay/mvp/presenter/ExpensesListPresenter.java create mode 100644 outlay/app/src/main/java/com/outlay/mvp/presenter/MvpPresenter.java create mode 100644 outlay/app/src/main/java/com/outlay/mvp/presenter/ReportPresenter.java create mode 100644 outlay/app/src/main/java/com/outlay/mvp/view/CategoriesView.java create mode 100644 outlay/app/src/main/java/com/outlay/mvp/view/CategoryDetailsView.java create mode 100644 outlay/app/src/main/java/com/outlay/mvp/view/EnterExpenseView.java create mode 100644 outlay/app/src/main/java/com/outlay/mvp/view/ExpenseDetailsView.java create mode 100644 outlay/app/src/main/java/com/outlay/mvp/view/ExpensesView.java create mode 100644 outlay/app/src/main/java/com/outlay/mvp/view/MvpView.java create mode 100644 outlay/app/src/main/java/com/outlay/mvp/view/StatisticView.java delete mode 100644 outlay/app/src/main/java/com/outlay/presenter/ExpensesDetailsPresenter.java delete mode 100644 outlay/app/src/main/java/com/outlay/presenter/ExpensesListPresenter.java delete mode 100644 outlay/app/src/main/java/com/outlay/presenter/MainFragmentPresenter.java delete mode 100644 outlay/app/src/main/java/com/outlay/presenter/ReportPresenter.java rename outlay/app/src/main/java/com/outlay/{ => view}/adapter/CategoriesDraggableGridAdapter.java (97%) rename outlay/app/src/main/java/com/outlay/{ => view}/adapter/CategoriesGridAdapter.java (96%) rename outlay/app/src/main/java/com/outlay/{ => view}/adapter/ExpenseAdapter.java (97%) rename outlay/app/src/main/java/com/outlay/{ => view}/adapter/IconsGridAdapter.java (97%) rename outlay/app/src/main/java/com/outlay/{ => view}/adapter/ReportAdapter.java (98%) rename outlay/app/src/main/java/com/outlay/{ => view}/adapter/listener/OnCategoryClickListener.java (80%) rename outlay/app/src/main/java/com/outlay/{ => view}/helper/OnTabSelectedListenerAdapter.java (92%) rename outlay/app/src/main/java/com/outlay/{ => view}/helper/TextWatcherAdapter.java (93%) create mode 100644 outlay/database/.gitignore create mode 100644 outlay/database/build.gradle create mode 100644 outlay/database/proguard-rules.pro create mode 100644 outlay/database/src/main/AndroidManifest.xml create mode 100644 outlay/database/src/main/java/com/outlay/database/adapter/CategoryDatabaseMapper.java create mode 100644 outlay/database/src/main/java/com/outlay/database/adapter/ExpenseDatabaseMapper.java rename outlay/{app/src/main/java/com/outlay => database/src/main/java/com/outlay/database}/dao/Category.java (98%) rename outlay/{app/src/main/java/com/outlay => database/src/main/java/com/outlay/database}/dao/CategoryDao.java (98%) rename outlay/{app/src/main/java/com/outlay => database/src/main/java/com/outlay/database}/dao/DaoMaster.java (96%) rename outlay/{app/src/main/java/com/outlay => database/src/main/java/com/outlay/database}/dao/DaoSession.java (90%) rename outlay/{app/src/main/java/com/outlay => database/src/main/java/com/outlay/database}/dao/Expense.java (98%) rename outlay/{app/src/main/java/com/outlay => database/src/main/java/com/outlay/database}/dao/ExpenseDao.java (99%) create mode 100644 outlay/database/src/main/java/com/outlay/database/source/CategoryDatabaseSource.java create mode 100644 outlay/database/src/main/java/com/outlay/database/source/ExpenseDatabaseSource.java create mode 100644 outlay/domain/src/main/java/com/outlay/data/Test.java create mode 100644 outlay/domain/src/main/java/com/outlay/data/repository/ExpenseRepositoryImpl.java create mode 100644 outlay/domain/src/main/java/com/outlay/data/source/CategoryDataSource.java create mode 100644 outlay/domain/src/main/java/com/outlay/data/source/ExpenseDataSource.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/interactor/DeleteExpenseUseCase.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/interactor/GetExpenseUseCase.java rename outlay/domain/src/main/java/com/outlay/domain/interactor/{CreateExpenseUseCase.java => SaveExpenseUseCase.java} (91%) create mode 100644 outlay/domain/src/main/java/com/outlay/domain/model/ExpensePeriod.java delete mode 100644 outlay/domain/src/main/java/com/outlay/domain/model/Period.java diff --git a/outlay/app/build.gradle b/outlay/app/build.gradle index 5e6e4ab..dd8868a 100644 --- a/outlay/app/build.gradle +++ b/outlay/app/build.gradle @@ -63,6 +63,7 @@ android { dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile project(':domain') + compile project(':database') //Google compile 'com.google.dagger:dagger:2.7' @@ -79,9 +80,6 @@ dependencies { compile "io.reactivex:rxjava:${libs.rxjava}" compile "io.reactivex:rxandroid:${libs.rxandroid}" - //greenRobot - compile "de.greenrobot:greendao:${libs.greenDao}" - //UI compile('com.mikepenz:materialdrawer:4.6.4@aar') { transitive = true diff --git a/outlay/app/src/main/java/com/outlay/api/OutlayDatabaseApi.java b/outlay/app/src/main/java/com/outlay/api/OutlayDatabaseApi.java deleted file mode 100644 index da78e8c..0000000 --- a/outlay/app/src/main/java/com/outlay/api/OutlayDatabaseApi.java +++ /dev/null @@ -1,167 +0,0 @@ -package com.outlay.api; - -import android.database.Cursor; - -import com.outlay.dao.Category; -import com.outlay.dao.CategoryDao; -import com.outlay.dao.DaoSession; -import com.outlay.dao.Expense; -import com.outlay.dao.ExpenseDao; -import com.outlay.model.Summary; -import com.outlay.core.utils.DateUtils; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import javax.inject.Inject; - -import de.greenrobot.dao.query.DeleteQuery; -import rx.Observable; - -/** - * Created by Bogdan Melnychuk on 1/21/16. - */ -public class OutlayDatabaseApi { - private CategoryDao categoryDao; - private ExpenseDao expenseDao; - private DaoSession daoSession; - - @Inject - public OutlayDatabaseApi(CategoryDao categoryDao, ExpenseDao expenseDao, DaoSession session) { - this.categoryDao = categoryDao; - this.expenseDao = expenseDao; - this.daoSession = session; - } - - - public Observable> getCategories() { - return Observable.defer(() -> Observable.just(categoryDao.queryBuilder().orderAsc(CategoryDao.Properties.Order).list())); - } - - public Category getCategoryById(Long id) { - return categoryDao.load(id); - } - - public void updateCategory(Category category) { - categoryDao.update(category); - } - - public long insertCategory(Category category) { - return categoryDao.insertOrReplace(category); - } - - public void deleteCategory(Category category) { - categoryDao.delete(category); - } - - public Expense getExpenseById(Long id) { - return expenseDao.load(id); - } - - - public void updateExpense(Expense e) { - expenseDao.update(e); - } - - public void deleteExpense(Expense e) { - expenseDao.delete(e); - } - - public Expense insertExpense(Expense expense) { - expenseDao.insert(expense); - return expense; - } - - public void deleteExpensesByCategory(Category category) { - DeleteQuery deleteQuery = expenseDao.queryBuilder().where(ExpenseDao.Properties.CategoryId.ge(category.getId())).buildDelete(); - deleteQuery.executeDeleteWithoutDetachingEntities(); - } - - public Observable> insertCategories(List categories) { - return Observable.create(subscriber -> { - categoryDao.insertOrReplaceInTx(categories); - subscriber.onNext(categories); - subscriber.onCompleted(); - }); - } - - public Observable> updateCategories(List categories) { - return Observable.create(subscriber -> { - categoryDao.updateInTx(categories); - subscriber.onNext(categories); - subscriber.onCompleted(); - }); - } - - public Observable> getExpenses(Date startDate, Date endDate) { - return getExpenses(startDate, endDate, null); - } - - public Observable> getExpenses(Date startDate, Date endDate, Long categoryId) { - if (categoryId == null) { - return Observable.create(subscriber -> { - List expenses = expenseDao.queryBuilder().where( - ExpenseDao.Properties.ReportedAt.ge(startDate), - ExpenseDao.Properties.ReportedAt.le(endDate) - ).list(); - subscriber.onNext(expenses); - subscriber.onCompleted(); - }); - } else { - return Observable.create(subscriber -> { - List expenses = expenseDao.queryBuilder().where( - ExpenseDao.Properties.ReportedAt.ge(startDate), - ExpenseDao.Properties.ReportedAt.le(endDate), - ExpenseDao.Properties.CategoryId.eq(categoryId) - ).list(); - subscriber.onNext(expenses); - subscriber.onCompleted(); - }); - } - } - - public Observable getSummary(Date date) { - return Observable.create(subscriber -> { - Summary summary = new Summary(); - summary.setMonthAmount(getSumForPeriod(DateUtils.getMonthStart(date), DateUtils.getMonthEnd(date))); - summary.setWeekAmount(getSumForPeriod(DateUtils.getWeekStart(date), DateUtils.getWeekEnd(date))); - summary.setDayAmount(getSumForPeriod(DateUtils.getDayStart(date), DateUtils.getDayEnd(date))); - summary.setDate(date); - summary.setCategories(getMostPayedCategories(DateUtils.getDayStart(date), DateUtils.getDayEnd(date))); - subscriber.onNext(summary); - subscriber.onCompleted(); - }); - } - - private double getSumForPeriod(Date dateFrom, Date dateTo) { - Cursor cursor = daoSession.getDatabase().rawQuery( - "SELECT SUM(AMOUNT) FROM EXPENSE WHERE REPORTED_AT >= ? AND REPORTED_AT <= ?", - new String[]{String.valueOf(dateFrom.getTime()), String.valueOf(dateTo.getTime())}); - if (cursor.moveToFirst()) { - return cursor.getDouble(0); - } - return 0; - } - - private List getMostPayedCategories(Date dateFrom, Date dateTo) { - List result = new ArrayList<>(); - String query = "SELECT * FROM CATEGORY INNER JOIN EXPENSE ON CATEGORY._id = EXPENSE.CATEGORY_ID " + - "WHERE EXPENSE.REPORTED_AT >= ? AND EXPENSE.REPORTED_AT <= ? " + - "GROUP BY CATEGORY._id ORDER BY AMOUNT DESC LIMIT 4"; - - Cursor cursor = daoSession.getDatabase().rawQuery( - query, - new String[]{String.valueOf(dateFrom.getTime()), String.valueOf(dateTo.getTime())}); - - if (cursor != null && cursor.getCount() > 0) { - cursor.moveToFirst(); - do { - result.add(categoryDao.readEntity(cursor, 0)); - } while (cursor.moveToNext()); - } - - return result; - } - -} diff --git a/outlay/app/src/main/java/com/outlay/di/module/AppModule.java b/outlay/app/src/main/java/com/outlay/di/module/AppModule.java index 858f4f2..15dca8d 100644 --- a/outlay/app/src/main/java/com/outlay/di/module/AppModule.java +++ b/outlay/app/src/main/java/com/outlay/di/module/AppModule.java @@ -1,7 +1,6 @@ package com.outlay.di.module; import android.app.Application; -import android.content.Context; import com.outlay.App; import com.outlay.core.data.AppPreferences; @@ -9,7 +8,7 @@ import com.outlay.core.executor.ThreadExecutor; import com.outlay.executor.JobExecutor; import com.outlay.executor.UIThread; -import com.outlay.preferences.PreferencesManager; +import com.outlay.impl.AndroidPreferencesManager; import javax.inject.Singleton; @@ -35,8 +34,8 @@ Application provideAppContext() { @Provides @Singleton - PreferencesManager providePreferencesManager() { - return new PreferencesManager(mApplication); + AndroidPreferencesManager providePreferencesManager() { + return new AndroidPreferencesManager(mApplication); } @Provides @@ -54,6 +53,6 @@ ThreadExecutor provideThreadExecutor(JobExecutor jobExecutor) { @Provides @Singleton AppPreferences provideAppPreferences() { - return new PreferencesManager(mApplication); + return new AndroidPreferencesManager(mApplication); } } diff --git a/outlay/app/src/main/java/com/outlay/di/module/DaoModule.java b/outlay/app/src/main/java/com/outlay/di/module/DaoModule.java index 1598751..3983cdf 100644 --- a/outlay/app/src/main/java/com/outlay/di/module/DaoModule.java +++ b/outlay/app/src/main/java/com/outlay/di/module/DaoModule.java @@ -3,15 +3,18 @@ import android.app.Application; import android.database.sqlite.SQLiteDatabase; -import com.outlay.api.OutlayDatabaseApi; -import com.outlay.dao.CategoryDao; -import com.outlay.dao.DaoMaster; -import com.outlay.dao.DaoSession; -import com.outlay.dao.ExpenseDao; +import com.outlay.data.source.CategoryDataSource; +import com.outlay.data.source.ExpenseDataSource; +import com.outlay.database.dao.CategoryDao; +import com.outlay.database.dao.DaoMaster; +import com.outlay.database.dao.DaoSession; +import com.outlay.database.dao.ExpenseDao; +import com.outlay.database.source.CategoryDatabaseSource; +import com.outlay.database.source.ExpenseDatabaseSource; import com.outlay.domain.repository.CategoryRepository; import com.outlay.domain.repository.ExpenseRepository; import com.outlay.impl.CategoryRepositoryImpl; -import com.outlay.impl.ExpenseRepositoryImpl; +import com.outlay.data.repository.ExpenseRepositoryImpl; import javax.inject.Singleton; @@ -61,21 +64,35 @@ public ExpenseDao provideExpenseDaoDao(DaoSession session) { return session.getExpenseDao(); } + @Provides + @Singleton + public ExpenseDataSource provideExpenseDataSource(ExpenseDao expenseDao) { + return new ExpenseDatabaseSource(expenseDao); + } + + @Provides + @Singleton + public CategoryDataSource provideCategoryDataSource( + CategoryDao categoryDao, + ExpenseDao expenseDao + ) { + return new CategoryDatabaseSource(categoryDao, expenseDao); + } + @Provides @Singleton public CategoryRepository provideCategoryRepository( - OutlayDatabaseApi outlayDatabaseApi, + CategoryDataSource categoryDataSource, Application application ) { - return new CategoryRepositoryImpl(outlayDatabaseApi, application); + return new CategoryRepositoryImpl(categoryDataSource, application); } @Provides @Singleton public ExpenseRepository provideExpenseRepository( - OutlayDatabaseApi outlayDatabaseApi, - Application application + ExpenseDataSource expenseDataSource ) { - return new ExpenseRepositoryImpl(outlayDatabaseApi, application); + return new ExpenseRepositoryImpl(expenseDataSource); } } diff --git a/outlay/app/src/main/java/com/outlay/preferences/PreferencesManager.java b/outlay/app/src/main/java/com/outlay/impl/AndroidPreferencesManager.java similarity index 89% rename from outlay/app/src/main/java/com/outlay/preferences/PreferencesManager.java rename to outlay/app/src/main/java/com/outlay/impl/AndroidPreferencesManager.java index bca7aa3..ec8086a 100644 --- a/outlay/app/src/main/java/com/outlay/preferences/PreferencesManager.java +++ b/outlay/app/src/main/java/com/outlay/impl/AndroidPreferencesManager.java @@ -1,4 +1,4 @@ -package com.outlay.preferences; +package com.outlay.impl; import android.content.Context; import android.content.SharedPreferences; @@ -6,12 +6,12 @@ import com.outlay.core.data.AppPreferences; -public class PreferencesManager implements AppPreferences { +public class AndroidPreferencesManager implements AppPreferences { private static final String PREF_FIRST_RUN = "_firstRun"; private Context context; - public PreferencesManager(Context context) { + public AndroidPreferencesManager(Context context) { this.context = context; } diff --git a/outlay/app/src/main/java/com/outlay/impl/CategoryAdapter.java b/outlay/app/src/main/java/com/outlay/impl/CategoryAdapter.java deleted file mode 100644 index 329dc03..0000000 --- a/outlay/app/src/main/java/com/outlay/impl/CategoryAdapter.java +++ /dev/null @@ -1,31 +0,0 @@ -package com.outlay.impl; - -import com.outlay.domain.model.Category; - -/** - * Created by bmelnychuk on 10/24/16. - */ - -public class CategoryAdapter { - public static Category toCategory(com.outlay.dao.Category daoCategory) { - Category category = new Category(); - category.setColor(daoCategory.getColor()); - category.setIcon(daoCategory.getIcon()); - category.setOrder(daoCategory.getOrder()); - category.setId(daoCategory.getId()); - category.setTitle(daoCategory.getTitle()); - - return category; - } - - public static com.outlay.dao.Category fromCategory(Category category) { - com.outlay.dao.Category daoCategory = new com.outlay.dao.Category(); - daoCategory.setColor(category.getColor()); - daoCategory.setIcon(category.getIcon()); - daoCategory.setOrder(category.getOrder()); - daoCategory.setId(category.getId()); - daoCategory.setTitle(category.getTitle()); - - return daoCategory; - } -} diff --git a/outlay/app/src/main/java/com/outlay/impl/CategoryRepositoryImpl.java b/outlay/app/src/main/java/com/outlay/impl/CategoryRepositoryImpl.java index d908fae..ef3870c 100644 --- a/outlay/app/src/main/java/com/outlay/impl/CategoryRepositoryImpl.java +++ b/outlay/app/src/main/java/com/outlay/impl/CategoryRepositoryImpl.java @@ -4,7 +4,7 @@ import android.support.v4.content.ContextCompat; import com.outlay.R; -import com.outlay.api.OutlayDatabaseApi; +import com.outlay.data.source.CategoryDataSource; import com.outlay.domain.model.Category; import com.outlay.domain.repository.CategoryRepository; @@ -20,15 +20,15 @@ */ public class CategoryRepositoryImpl implements CategoryRepository { - private OutlayDatabaseApi outlayDatabaseApi; private Context context; + private CategoryDataSource databaseSource; @Inject public CategoryRepositoryImpl( - OutlayDatabaseApi outlayDatabaseApi, + CategoryDataSource databaseSource, Context context ) { - this.outlayDatabaseApi = outlayDatabaseApi; + this.databaseSource = databaseSource; this.context = context; } @@ -60,84 +60,26 @@ public Observable> getDefault() { @Override public Observable> getAll() { - return outlayDatabaseApi.getCategories() - .map(categories -> { - List result = new ArrayList<>(); - for (com.outlay.dao.Category c : categories) { - Category category = new Category(); - category.setColor(c.getColor()); - category.setIcon(c.getIcon()); - category.setOrder(c.getOrder()); - category.setId(c.getId()); - category.setTitle(c.getTitle()); - result.add(category); - } - return result; - }); + return databaseSource.getAll(); } @Override public Observable getById(Long id) { - return Observable.create(subscriber -> { - com.outlay.dao.Category c = outlayDatabaseApi.getCategoryById(id); - Category category = new Category(); - category.setColor(c.getColor()); - category.setIcon(c.getIcon()); - category.setOrder(c.getOrder()); - category.setId(c.getId()); - category.setTitle(c.getTitle()); - - subscriber.onNext(category); - subscriber.onCompleted(); - }); + return databaseSource.getById(id); } @Override public Observable> saveAll(List categories) { - List toInsert = new ArrayList<>(); - for (Category c : categories) { - com.outlay.dao.Category category = new com.outlay.dao.Category(); - category.setColor(c.getColor()); - category.setIcon(c.getIcon()); - category.setOrder(c.getOrder()); - category.setId(c.getId()); - category.setTitle(c.getTitle()); - toInsert.add(category); - } - - return outlayDatabaseApi.insertCategories(toInsert) - .map(storedCategories -> { - List result = new ArrayList<>(); - for (com.outlay.dao.Category c : storedCategories) { - Category category = new Category(); - category.setColor(c.getColor()); - category.setIcon(c.getIcon()); - category.setOrder(c.getOrder()); - category.setId(c.getId()); - category.setTitle(c.getTitle()); - result.add(category); - } - return result; - }); + return databaseSource.saveAll(categories); } @Override public Observable save(Category category) { - return Observable.create(subscriber -> { - com.outlay.dao.Category daoCategory = CategoryAdapter.fromCategory(category); - outlayDatabaseApi.insertCategory(daoCategory); - subscriber.onNext(CategoryAdapter.toCategory(daoCategory)); - subscriber.onCompleted(); - }); + return databaseSource.save(category); } @Override public Observable remove(Category category) { - return Observable.create(subscriber -> { - com.outlay.dao.Category daoCategory = CategoryAdapter.fromCategory(category); - outlayDatabaseApi.deleteCategory(daoCategory); - subscriber.onNext(CategoryAdapter.toCategory(daoCategory)); - subscriber.onCompleted(); - }); + return databaseSource.remove(category); } } diff --git a/outlay/app/src/main/java/com/outlay/impl/ExpenseRepositoryImpl.java b/outlay/app/src/main/java/com/outlay/impl/ExpenseRepositoryImpl.java deleted file mode 100644 index 548849f..0000000 --- a/outlay/app/src/main/java/com/outlay/impl/ExpenseRepositoryImpl.java +++ /dev/null @@ -1,86 +0,0 @@ -package com.outlay.impl; - -import android.content.Context; - -import com.outlay.api.OutlayDatabaseApi; -import com.outlay.domain.model.Category; -import com.outlay.domain.model.Expense; -import com.outlay.domain.model.Report; -import com.outlay.domain.repository.ExpenseRepository; - -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -import javax.inject.Inject; - -import rx.Observable; - -/** - * Created by bmelnychuk on 10/24/16. - */ - -public class ExpenseRepositoryImpl implements ExpenseRepository { - private OutlayDatabaseApi outlayDatabaseApi; - private Context context; - - @Inject - public ExpenseRepositoryImpl( - OutlayDatabaseApi outlayDatabaseApi, - Context context - ) { - this.outlayDatabaseApi = outlayDatabaseApi; - this.context = context; - } - - @Override - public Observable saveExpense(Expense expense) { - com.outlay.dao.Expense expense1 = new com.outlay.dao.Expense(); - expense1.setId(expense.getId()); - expense1.setAmount(expense.getAmount().doubleValue()); - expense1.setCategoryId(expense.getCategory().getId()); - expense1.setNote(expense.getNote()); - expense1.setReportedAt(expense.getReportedAt()); - - - return Observable.create(subscriber -> { - com.outlay.dao.Expense dbExpense = outlayDatabaseApi.insertExpense(expense1); - - Expense result = new Expense(); - result.setId(dbExpense.getId()); - result.setAmount(new BigDecimal(dbExpense.getAmount().toString())); - result.setNote(dbExpense.getNote()); - result.setReportedAt(dbExpense.getReportedAt()); - - Category category = new Category(); - category.setId(dbExpense.getCategoryId()); - result.setCategory(category); - }); - } - - @Override - public Observable> getExpenses(Date startDate, Date endDate) { - return outlayDatabaseApi.getExpenses(startDate, endDate).map(databaseExpenses -> { - List result = new ArrayList<>(); - for (com.outlay.dao.Expense dbExpense : databaseExpenses) { - Expense exp = new Expense(); - exp.setId(dbExpense.getId()); - exp.setAmount(new BigDecimal(dbExpense.getAmount().toString())); - exp.setNote(dbExpense.getNote()); - exp.setReportedAt(dbExpense.getReportedAt()); - - Category category = new Category(); - category.setId(dbExpense.getCategoryId()); - category.setTitle(dbExpense.getCategory().getTitle()); - category.setColor(dbExpense.getCategory().getColor()); - category.setOrder(dbExpense.getCategory().getOrder()); - category.setIcon(dbExpense.getCategory().getIcon()); - exp.setCategory(category); - result.add(exp); - } - - return result; - }); - } -} diff --git a/outlay/app/src/main/java/com/outlay/model/Icon.java b/outlay/app/src/main/java/com/outlay/model/Icon.java deleted file mode 100644 index 63c7fde..0000000 --- a/outlay/app/src/main/java/com/outlay/model/Icon.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.outlay.model; - -import java.util.Arrays; -import java.util.List; - -/** - * Created by Bogdan Melnychuk on 1/29/16. - */ -public final class Icon { - private static final String[] all = { - "ic_coins", - "ic_flight", - "ic_airplane", - "ic_bakery", - "ic_beach", - "ic_basketball", - "ic_front", - "ic_flamenco", - "ic_fast_food", - "ic_subscribe", - "ic_t_shirt", - "ic_md", - "ic_map", - "ic_medical", - "ic_flower", - "ic_fuel", - "ic_money", - "ic_tea", - "ic_tag", - "ic_mobilephone", - "ic_telephone", - "ic_multiple", - "ic_hairsalon", - "ic_books", - "ic_bowling", - "ic_hand", - "ic_note", - "ic_tool", - "ic_tools", - "ic_people", - "ic_hardbound", - "ic_box", - "ic_cars", - "ic_healthcare", - "ic_photo", - "ic_two", - "ic_weightlifting", - "ic_poor", - "ic_heart", - "ic_chart", - "ic_christmas", - "ic_insurance", - "ic_rent", - "ic_restaurant", - "ic_horse", - "ic_cigarette", - "ic_cleaning", - "ic_house", - "ic_run", - "ic_scissors", - "ic_italian_food", - "ic_justice", - "ic_climbing", - "ic_screen", - "ic_shopping", - "ic_legal", - "ic_controller", - "ic_dog", - "ic_lifeline", - "ic_sofa", - "ic_sportive", - "ic_locked", - "ic_donation", - "ic_draw", - "ic_luggage", - "ic_makeup", - "ic_ducks" - }; - - public static List getAll() { - return Arrays.asList(all); - } -} \ No newline at end of file diff --git a/outlay/app/src/main/java/com/outlay/model/Report.java b/outlay/app/src/main/java/com/outlay/model/Report.java deleted file mode 100644 index 0bf6412..0000000 --- a/outlay/app/src/main/java/com/outlay/model/Report.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.outlay.model; - -import com.outlay.dao.Category; -import com.outlay.dao.Expense; - -import java.util.ArrayList; -import java.util.List; - -/** - * Created by Bogdan Melnychuk on 1/27/16. - */ -public class Report { - private List expenses; - private double amount; - - public Report() { - expenses = new ArrayList<>(); - } - - public void addExpense(Expense e) { - expenses.add(e); - amount += e.getAmount(); - } - - public double getAmount() { - return amount; - } - - public String getIcon() { - String icon = null; - if (expenses != null && !expenses.isEmpty()) { - icon = expenses.get(0).getCategory().getIcon(); - } - return icon; - } - - public int getColor() { - int color = 0; - if (expenses != null && !expenses.isEmpty()) { - color = expenses.get(0).getCategory().getColor(); - } - return color; - } - - public String getTitle() { - String title = ""; - if (expenses != null && !expenses.isEmpty()) { - title = expenses.get(0).getCategory().getTitle(); - } - return title; - } - - public Category getCategory() { - Category category = null; - if (expenses != null && !expenses.isEmpty()) { - category = expenses.get(0).getCategory(); - } - return category; - } -} diff --git a/outlay/app/src/main/java/com/outlay/model/Summary.java b/outlay/app/src/main/java/com/outlay/model/Summary.java deleted file mode 100644 index 23b7488..0000000 --- a/outlay/app/src/main/java/com/outlay/model/Summary.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.outlay.model; - -import com.outlay.dao.Category; - -import java.util.Date; -import java.util.List; - -/** - * Created by Bogdan Melnychuk on 1/30/16. - */ -public class Summary { - private Date date; - private double dayAmount; - private double weekAmount; - private double monthAmount; - private List categories; - - public Date getDate() { - return date; - } - - public void setDate(Date date) { - this.date = date; - } - - public double getDayAmount() { - return dayAmount; - } - - public void setDayAmount(double dayAmount) { - this.dayAmount = dayAmount; - } - - public double getWeekAmount() { - return weekAmount; - } - - public void setWeekAmount(double weekAmount) { - this.weekAmount = weekAmount; - } - - public double getMonthAmount() { - return monthAmount; - } - - public void setMonthAmount(double monthAmount) { - this.monthAmount = monthAmount; - } - - public List getCategories() { - return categories; - } - - public void setCategories(List categories) { - this.categories = categories; - } -} diff --git a/outlay/app/src/main/java/com/outlay/presenter/CategoriesPresenter.java b/outlay/app/src/main/java/com/outlay/mvp/presenter/CategoriesPresenter.java similarity index 76% rename from outlay/app/src/main/java/com/outlay/presenter/CategoriesPresenter.java rename to outlay/app/src/main/java/com/outlay/mvp/presenter/CategoriesPresenter.java index 16a65a7..0b5945f 100644 --- a/outlay/app/src/main/java/com/outlay/presenter/CategoriesPresenter.java +++ b/outlay/app/src/main/java/com/outlay/mvp/presenter/CategoriesPresenter.java @@ -1,10 +1,10 @@ -package com.outlay.presenter; +package com.outlay.mvp.presenter; import com.outlay.core.executor.DefaultSubscriber; import com.outlay.domain.interactor.GetCategoriesUseCase; import com.outlay.domain.interactor.UpdateCategoriesUseCase; import com.outlay.domain.model.Category; -import com.outlay.view.fragment.CategoriesFragment; +import com.outlay.mvp.view.CategoriesView; import java.util.List; @@ -13,8 +13,7 @@ /** * Created by Bogdan Melnychuk on 1/21/16. */ -public class CategoriesPresenter { - private CategoriesFragment view; +public class CategoriesPresenter extends MvpPresenter { private GetCategoriesUseCase getCategoriesUseCase; private UpdateCategoriesUseCase updateCategoriesUseCase; @@ -27,23 +26,16 @@ public CategoriesPresenter( this.updateCategoriesUseCase = updateCategoriesUseCase; } - public void attachView(CategoriesFragment fragment) { - this.view = fragment; - } - public void loadCategories() { getCategoriesUseCase.execute(new DefaultSubscriber>() { @Override public void onNext(List categories) { - view.displayCategories(categories); + getView().showCategories(categories); } }); } - public void updateCategories(List categories) { - updateCategoriesUseCase.execute(categories, new DefaultSubscriber() { - - }); + updateCategoriesUseCase.execute(categories, new DefaultSubscriber()); } } diff --git a/outlay/app/src/main/java/com/outlay/presenter/CategoryDetailsPresenter.java b/outlay/app/src/main/java/com/outlay/mvp/presenter/CategoryDetailsPresenter.java similarity index 71% rename from outlay/app/src/main/java/com/outlay/presenter/CategoryDetailsPresenter.java rename to outlay/app/src/main/java/com/outlay/mvp/presenter/CategoryDetailsPresenter.java index f56e853..f299e14 100644 --- a/outlay/app/src/main/java/com/outlay/presenter/CategoryDetailsPresenter.java +++ b/outlay/app/src/main/java/com/outlay/mvp/presenter/CategoryDetailsPresenter.java @@ -1,19 +1,18 @@ -package com.outlay.presenter; +package com.outlay.mvp.presenter; import com.outlay.core.executor.DefaultSubscriber; import com.outlay.domain.interactor.DeleteCategoryUseCase; import com.outlay.domain.interactor.GetCategoryUseCase; import com.outlay.domain.interactor.UpdateCategoryUseCase; import com.outlay.domain.model.Category; -import com.outlay.view.fragment.CategoryDetailsFragment; +import com.outlay.mvp.view.CategoryDetailsView; import javax.inject.Inject; /** * Created by Bogdan Melnychuk on 1/21/16. */ -public class CategoryDetailsPresenter { - private CategoryDetailsFragment view; +public class CategoryDetailsPresenter extends MvpPresenter { private UpdateCategoryUseCase updateCategoryUseCase; private DeleteCategoryUseCase deleteCategoryUseCase; private GetCategoryUseCase getCategoryUseCase; @@ -29,26 +28,31 @@ public CategoryDetailsPresenter( this.getCategoryUseCase = getCategoryUseCase; } - public void attachView(CategoryDetailsFragment fragment) { - this.view = fragment; - } - public void loadCategory(Long id) { - getCategoryUseCase.execute(id, new DefaultSubscriber(){ + getCategoryUseCase.execute(id, new DefaultSubscriber() { @Override public void onNext(Category category) { - view.displayCategory(category); + getView().showCategory(category); } }); - } public void updateCategory(Category category) { - updateCategoryUseCase.execute(category, new DefaultSubscriber()); + updateCategoryUseCase.execute(category, new DefaultSubscriber() { + @Override + public void onNext(Category category) { + getView().finish(); + } + }); } public void deleteCategory(Category category) { - deleteCategoryUseCase.execute(category, new DefaultSubscriber()); + deleteCategoryUseCase.execute(category, new DefaultSubscriber() { + @Override + public void onNext(Category category) { + getView().finish(); + } + }); } } diff --git a/outlay/app/src/main/java/com/outlay/mvp/presenter/EnterExpensePresenter.java b/outlay/app/src/main/java/com/outlay/mvp/presenter/EnterExpensePresenter.java new file mode 100644 index 0000000..5c00702 --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/mvp/presenter/EnterExpensePresenter.java @@ -0,0 +1,77 @@ +package com.outlay.mvp.presenter; + +import com.outlay.core.executor.DefaultSubscriber; +import com.outlay.domain.interactor.DeleteExpenseUseCase; +import com.outlay.domain.interactor.GetDateSummary; +import com.outlay.domain.interactor.InitUseCase; +import com.outlay.domain.interactor.SaveExpenseUseCase; +import com.outlay.domain.model.DateSummary; +import com.outlay.domain.model.Expense; +import com.outlay.mvp.view.EnterExpenseView; + +import java.util.Date; +import java.util.List; + +import javax.inject.Inject; + +/** + * Created by Bogdan Melnychuk on 1/25/16. + */ +public class EnterExpensePresenter extends MvpPresenter { + private InitUseCase initUseCase; + private SaveExpenseUseCase createExpenseUseCase; + private GetDateSummary getDateSummaryUseCase; + private DeleteExpenseUseCase deleteExpenseUseCase; + + @Inject + public EnterExpensePresenter( + InitUseCase initUseCase, + SaveExpenseUseCase createExpenseUseCase, + GetDateSummary getDateSummaryUseCase, + DeleteExpenseUseCase deleteExpenseUseCase + ) { + this.initUseCase = initUseCase; + this.createExpenseUseCase = createExpenseUseCase; + this.getDateSummaryUseCase = getDateSummaryUseCase; + this.deleteExpenseUseCase = deleteExpenseUseCase; + } + + public void loadCategories() { + initUseCase.execute(new DefaultSubscriber>() { + @Override + public void onNext(List categories) { + getView().showCategories(categories); + } + }); + } + + public void loadSummary(Date date) { + getDateSummaryUseCase.execute(date, new DefaultSubscriber() { + @Override + public void onNext(DateSummary dateSummary) { + getView().showDateSummary(dateSummary); + } + }); + } + + + public void insertExpense(Expense expense) { + createExpenseUseCase.execute(expense, new DefaultSubscriber() { + @Override + public void onNext(Expense expense) { + loadSummary(new Date()); + getView().alertExpenseSuccess(expense); + + } + }); + } + + public void deleteExpense(Expense expense) { + deleteExpenseUseCase.execute(expense, new DefaultSubscriber() { + @Override + public void onNext(Expense expense) { + loadSummary(new Date()); + } + }); + } +} \ No newline at end of file diff --git a/outlay/app/src/main/java/com/outlay/mvp/presenter/ExpensesDetailsPresenter.java b/outlay/app/src/main/java/com/outlay/mvp/presenter/ExpensesDetailsPresenter.java new file mode 100644 index 0000000..d61cbdb --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/mvp/presenter/ExpensesDetailsPresenter.java @@ -0,0 +1,65 @@ +package com.outlay.mvp.presenter; + +import com.outlay.core.executor.DefaultSubscriber; +import com.outlay.domain.interactor.DeleteExpenseUseCase; +import com.outlay.domain.interactor.GetCategoriesUseCase; +import com.outlay.domain.interactor.GetExpenseUseCase; +import com.outlay.domain.interactor.SaveExpenseUseCase; +import com.outlay.domain.model.Category; +import com.outlay.domain.model.Expense; +import com.outlay.mvp.view.ExpenseDetailsView; + +import java.util.List; + +import javax.inject.Inject; + +/** + * Created by Bogdan Melnychuk on 1/21/16. + */ +public class ExpensesDetailsPresenter extends MvpPresenter { + private GetCategoriesUseCase getCategoriesUseCase; + private GetExpenseUseCase getExpenseUseCase; + private SaveExpenseUseCase saveExpenseUseCase; + private DeleteExpenseUseCase deleteExpenseUseCase; + + @Inject + public ExpensesDetailsPresenter( + GetCategoriesUseCase getCategoriesUseCase, + GetExpenseUseCase getExpenseUseCase, + SaveExpenseUseCase saveExpenseUseCase, + DeleteExpenseUseCase deleteExpenseUseCase + ) { + this.getCategoriesUseCase = getCategoriesUseCase; + this.getExpenseUseCase = getExpenseUseCase; + this.saveExpenseUseCase = saveExpenseUseCase; + this.deleteExpenseUseCase = deleteExpenseUseCase; + } + + public void loadExpense(Long expenseId) { + getExpenseUseCase.execute(expenseId, new DefaultSubscriber() { + @Override + public void onNext(Expense expense) { + getView().showExpense(expense); + } + }); + + } + + public void loadCategories() { + getCategoriesUseCase.execute(new DefaultSubscriber>() { + @Override + public void onNext(List categories) { + super.onNext(categories); + getView().showCategories(categories); + } + }); + } + + public void updateExpense(Expense expense) { + saveExpenseUseCase.execute(expense, new DefaultSubscriber()); + } + + public void deleteExpense(Expense expense) { + deleteExpenseUseCase.execute(expense, new DefaultSubscriber()); + } +} diff --git a/outlay/app/src/main/java/com/outlay/mvp/presenter/ExpensesListPresenter.java b/outlay/app/src/main/java/com/outlay/mvp/presenter/ExpensesListPresenter.java new file mode 100644 index 0000000..b849371 --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/mvp/presenter/ExpensesListPresenter.java @@ -0,0 +1,35 @@ +package com.outlay.mvp.presenter; + +import com.outlay.core.executor.DefaultSubscriber; +import com.outlay.domain.interactor.LoadReportUseCase; +import com.outlay.domain.model.ExpensePeriod; +import com.outlay.domain.model.Report; +import com.outlay.mvp.view.ExpensesView; + +import java.util.Date; + +import javax.inject.Inject; + +/** + * Created by Bogdan Melnychuk on 1/21/16. + */ +public class ExpensesListPresenter extends MvpPresenter { + private LoadReportUseCase loadReportUseCase; + + @Inject + public ExpensesListPresenter( + LoadReportUseCase loadReportUseCase + ) { + this.loadReportUseCase = loadReportUseCase; + } + + public void loadExpenses(Date dateFrom, Date dateTo, Long categoryId) { + ExpensePeriod period = new ExpensePeriod(dateFrom, dateTo, categoryId); + loadReportUseCase.execute(period, new DefaultSubscriber() { + @Override + public void onNext(Report report) { + getView().showReport(report); + } + }); + } +} diff --git a/outlay/app/src/main/java/com/outlay/mvp/presenter/MvpPresenter.java b/outlay/app/src/main/java/com/outlay/mvp/presenter/MvpPresenter.java new file mode 100644 index 0000000..e4df078 --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/mvp/presenter/MvpPresenter.java @@ -0,0 +1,23 @@ +package com.outlay.mvp.presenter; + +import com.outlay.mvp.view.MvpView; + +/** + * Created by bmelnychuk on 10/25/16. + */ + +public class MvpPresenter { + private T view; + + public void attachView(T view) { + this.view = view; + } + + protected T getView() { + return view; + } + + public boolean isViewAttached() { + return view != null; + } +} diff --git a/outlay/app/src/main/java/com/outlay/mvp/presenter/ReportPresenter.java b/outlay/app/src/main/java/com/outlay/mvp/presenter/ReportPresenter.java new file mode 100644 index 0000000..237c988 --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/mvp/presenter/ReportPresenter.java @@ -0,0 +1,58 @@ +package com.outlay.mvp.presenter; + +import com.outlay.core.executor.DefaultSubscriber; +import com.outlay.core.utils.DateUtils; +import com.outlay.domain.interactor.LoadReportUseCase; +import com.outlay.domain.model.ExpensePeriod; +import com.outlay.domain.model.Report; +import com.outlay.mvp.view.StatisticView; +import com.outlay.view.Page; +import com.outlay.view.fragment.ReportFragment; + +import java.util.ArrayList; +import java.util.Date; + +import javax.inject.Inject; + +/** + * Created by Bogdan Melnychuk on 1/21/16. + */ +public class ReportPresenter extends MvpPresenter { + private LoadReportUseCase loadReportUseCase; + + @Inject + public ReportPresenter( + LoadReportUseCase loadReportUseCase + ) { + this.loadReportUseCase = loadReportUseCase; + } + + public void loadReport(Date date, int period) { + Date startDate = date; + Date endDate = date; + + switch (period) { + case ReportFragment.PERIOD_DAY: + startDate = DateUtils.getDayStart(date); + endDate = DateUtils.getDayEnd(date); + break; + case ReportFragment.PERIOD_WEEK: + startDate = DateUtils.getWeekStart(date); + endDate = DateUtils.getWeekEnd(date); + break; + case ReportFragment.PERIOD_MONTH: + startDate = DateUtils.getMonthStart(date); + endDate = DateUtils.getMonthEnd(date); + break; + } + + + loadReportUseCase.execute(new ExpensePeriod(startDate, endDate), new DefaultSubscriber() { + @Override + public void onNext(Report report) { + super.onNext(report); + getView().showReports(new ArrayList<>(report.groupByCategory().values())); + } + }); + } +} \ No newline at end of file diff --git a/outlay/app/src/main/java/com/outlay/mvp/view/CategoriesView.java b/outlay/app/src/main/java/com/outlay/mvp/view/CategoriesView.java new file mode 100644 index 0000000..8febbcd --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/mvp/view/CategoriesView.java @@ -0,0 +1,14 @@ +package com.outlay.mvp.view; + +import com.outlay.domain.model.Category; + +import java.util.List; + +/** + * Created by bmelnychuk on 10/25/16. + */ + +public interface CategoriesView extends MvpView { + void showCategories(List categoryList); + +} diff --git a/outlay/app/src/main/java/com/outlay/mvp/view/CategoryDetailsView.java b/outlay/app/src/main/java/com/outlay/mvp/view/CategoryDetailsView.java new file mode 100644 index 0000000..2f0c67a --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/mvp/view/CategoryDetailsView.java @@ -0,0 +1,12 @@ +package com.outlay.mvp.view; + +import com.outlay.domain.model.Category; + +/** + * Created by bmelnychuk on 10/25/16. + */ + +public interface CategoryDetailsView extends MvpView { + void showCategory(Category category); + void finish(); +} diff --git a/outlay/app/src/main/java/com/outlay/mvp/view/EnterExpenseView.java b/outlay/app/src/main/java/com/outlay/mvp/view/EnterExpenseView.java new file mode 100644 index 0000000..46cfe01 --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/mvp/view/EnterExpenseView.java @@ -0,0 +1,19 @@ +package com.outlay.mvp.view; + +import com.outlay.domain.model.Category; +import com.outlay.domain.model.DateSummary; +import com.outlay.domain.model.Expense; + +import java.math.BigDecimal; +import java.util.List; + +/** + * Created by bmelnychuk on 10/25/16. + */ + +public interface EnterExpenseView extends MvpView { + void showCategories(List categoryList); + void showDateSummary(DateSummary dateSummary); + void setAmount(BigDecimal amount); + void alertExpenseSuccess(Expense expense); +} diff --git a/outlay/app/src/main/java/com/outlay/mvp/view/ExpenseDetailsView.java b/outlay/app/src/main/java/com/outlay/mvp/view/ExpenseDetailsView.java new file mode 100644 index 0000000..0d9b7fb --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/mvp/view/ExpenseDetailsView.java @@ -0,0 +1,15 @@ +package com.outlay.mvp.view; + +import com.outlay.domain.model.Category; +import com.outlay.domain.model.Expense; + +import java.util.List; + +/** + * Created by bmelnychuk on 10/25/16. + */ + +public interface ExpenseDetailsView extends MvpView { + void showExpense(Expense category); + void showCategories(List categoryList); +} diff --git a/outlay/app/src/main/java/com/outlay/mvp/view/ExpensesView.java b/outlay/app/src/main/java/com/outlay/mvp/view/ExpensesView.java new file mode 100644 index 0000000..04e911c --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/mvp/view/ExpensesView.java @@ -0,0 +1,11 @@ +package com.outlay.mvp.view; + +import com.outlay.domain.model.Report; + +/** + * Created by bmelnychuk on 10/25/16. + */ + +public interface ExpensesView extends MvpView { + void showReport(Report report); +} diff --git a/outlay/app/src/main/java/com/outlay/mvp/view/MvpView.java b/outlay/app/src/main/java/com/outlay/mvp/view/MvpView.java new file mode 100644 index 0000000..2760465 --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/mvp/view/MvpView.java @@ -0,0 +1,8 @@ +package com.outlay.mvp.view; + +/** + * Created by bmelnychuk on 10/25/16. + */ + +public interface MvpView { +} diff --git a/outlay/app/src/main/java/com/outlay/mvp/view/StatisticView.java b/outlay/app/src/main/java/com/outlay/mvp/view/StatisticView.java new file mode 100644 index 0000000..2565968 --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/mvp/view/StatisticView.java @@ -0,0 +1,13 @@ +package com.outlay.mvp.view; + +import com.outlay.domain.model.Report; + +import java.util.List; + +/** + * Created by bmelnychuk on 10/25/16. + */ + +public interface StatisticView extends MvpView { + void showReports(List report); +} diff --git a/outlay/app/src/main/java/com/outlay/presenter/ExpensesDetailsPresenter.java b/outlay/app/src/main/java/com/outlay/presenter/ExpensesDetailsPresenter.java deleted file mode 100644 index fecc4a8..0000000 --- a/outlay/app/src/main/java/com/outlay/presenter/ExpensesDetailsPresenter.java +++ /dev/null @@ -1,56 +0,0 @@ -package com.outlay.presenter; - -import com.outlay.api.OutlayDatabaseApi; -import com.outlay.dao.Expense; -import com.outlay.view.fragment.ExpensesDetailsFragment; - -import org.joda.time.DateTime; -import org.joda.time.LocalTime; - -import javax.inject.Inject; - -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; - -/** - * Created by Bogdan Melnychuk on 1/21/16. - */ -public class ExpensesDetailsPresenter { - private OutlayDatabaseApi api; - private ExpensesDetailsFragment view; - - @Inject - public ExpensesDetailsPresenter(OutlayDatabaseApi outlayDatabaseApi) { - this.api = outlayDatabaseApi; - } - - public void attachView(ExpensesDetailsFragment fragment) { - this.view = fragment; - } - - public void loadExpense(Long expenseId) { - view.displayExpense(api.getExpenseById(expenseId)); - } - - public void loadCategories() { - api.getCategories() - .subscribeOn(Schedulers.newThread()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(response -> view.setCategories(response)); - } - - public void updateExpense(Expense expense) { - DateTime dateTime = new DateTime(expense.getReportedAt().getTime()); - dateTime = dateTime.withTime(LocalTime.now()); - expense.setReportedAt(dateTime.toDate()); - if (expense.getId() == null) { - api.insertExpense(expense); - } else { - api.updateExpense(expense); - } - } - - public void deleteExpense(Expense expense) { - api.deleteExpense(expense); - } -} diff --git a/outlay/app/src/main/java/com/outlay/presenter/ExpensesListPresenter.java b/outlay/app/src/main/java/com/outlay/presenter/ExpensesListPresenter.java deleted file mode 100644 index 98c0ee7..0000000 --- a/outlay/app/src/main/java/com/outlay/presenter/ExpensesListPresenter.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.outlay.presenter; - -import com.outlay.api.OutlayDatabaseApi; -import com.outlay.dao.Category; -import com.outlay.view.fragment.ExpensesListFragment; - -import java.util.Date; - -import javax.inject.Inject; - -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; - -/** - * Created by Bogdan Melnychuk on 1/21/16. - */ -public class ExpensesListPresenter { - private OutlayDatabaseApi api; - private ExpensesListFragment view; - - @Inject - public ExpensesListPresenter(OutlayDatabaseApi outlayDatabaseApi) { - this.api = outlayDatabaseApi; - } - - public void attachView(ExpensesListFragment fragment) { - this.view = fragment; - } - - public void loadExpenses(Date dateFrom, Date dateTo, Long categoryId) { - api.getExpenses(dateFrom, dateTo, categoryId) - .subscribeOn(Schedulers.newThread()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(response -> view.displayExpenses(response)); - } - - public Category getCategoryById(Long id) { - return api.getCategoryById(id); - } - -} diff --git a/outlay/app/src/main/java/com/outlay/presenter/MainFragmentPresenter.java b/outlay/app/src/main/java/com/outlay/presenter/MainFragmentPresenter.java deleted file mode 100644 index 6ba4de9..0000000 --- a/outlay/app/src/main/java/com/outlay/presenter/MainFragmentPresenter.java +++ /dev/null @@ -1,64 +0,0 @@ -package com.outlay.presenter; - -import com.outlay.core.executor.DefaultSubscriber; -import com.outlay.domain.interactor.CreateExpenseUseCase; -import com.outlay.domain.interactor.GetDateSummary; -import com.outlay.domain.interactor.InitUseCase; -import com.outlay.domain.model.DateSummary; -import com.outlay.preferences.PreferencesManager; -import com.outlay.view.fragment.MainFragment; - -import java.util.Date; -import java.util.List; - -import javax.inject.Inject; - -/** - * Created by Bogdan Melnychuk on 1/25/16. - */ -public class MainFragmentPresenter { - private MainFragment view; - private InitUseCase initUseCase; - private CreateExpenseUseCase createExpenseUseCase; - private GetDateSummary getDateSummaryUseCase; - - @Inject - public MainFragmentPresenter( - InitUseCase initUseCase, - CreateExpenseUseCase createExpenseUseCase, - GetDateSummary getDateSummaryUseCase - ) { - this.initUseCase = initUseCase; - this.createExpenseUseCase = createExpenseUseCase; - this.getDateSummaryUseCase = getDateSummaryUseCase; - } - - public void attachView(MainFragment fragment) { - this.view = fragment; - } - - public void loadCategories() { - initUseCase.execute(new DefaultSubscriber>() { - @Override - public void onNext(List categories) { - view.displayCategories(categories); - } - }); - } - - public void loadSummary(Date date) { - getDateSummaryUseCase.execute(date, new DefaultSubscriber() { - @Override - public void onNext(DateSummary dateSummary) { - view.displaySummary(dateSummary); - } - }); - } - - - public void insertExpense(com.outlay.domain.model.Expense expense) { - createExpenseUseCase.execute(expense, new DefaultSubscriber() { - - }); - } -} diff --git a/outlay/app/src/main/java/com/outlay/presenter/ReportPresenter.java b/outlay/app/src/main/java/com/outlay/presenter/ReportPresenter.java deleted file mode 100644 index 017d7c2..0000000 --- a/outlay/app/src/main/java/com/outlay/presenter/ReportPresenter.java +++ /dev/null @@ -1,119 +0,0 @@ -package com.outlay.presenter; - -import com.outlay.api.OutlayDatabaseApi; -import com.outlay.core.executor.DefaultSubscriber; -import com.outlay.core.utils.DateUtils; -import com.outlay.domain.interactor.LoadReportUseCase; -import com.outlay.domain.model.Period; -import com.outlay.domain.model.Report; -import com.outlay.view.Page; -import com.outlay.view.fragment.ReportFragment; - -import java.util.ArrayList; -import java.util.Date; - -import javax.inject.Inject; - -/** - * Created by Bogdan Melnychuk on 1/21/16. - */ -public class ReportPresenter { - private ReportFragment view; - private LoadReportUseCase loadReportUseCase; - - @Inject - public ReportPresenter( - OutlayDatabaseApi outlayDatabaseApi, - LoadReportUseCase loadReportUseCase - ) { - this.loadReportUseCase = loadReportUseCase; - } - - public void attachView(ReportFragment fragment) { - this.view = fragment; - } - - public void loadReport(Date date, int period) { - Date startDate = date; - Date endDate = date; - - switch (period) { - case ReportFragment.PERIOD_DAY: - startDate = DateUtils.getDayStart(date); - endDate = DateUtils.getDayEnd(date); - break; - case ReportFragment.PERIOD_WEEK: - startDate = DateUtils.getWeekStart(date); - endDate = DateUtils.getWeekEnd(date); - break; - case ReportFragment.PERIOD_MONTH: - startDate = DateUtils.getMonthStart(date); - endDate = DateUtils.getMonthEnd(date); - break; - } - - - loadReportUseCase.execute(new Period(startDate, endDate), new DefaultSubscriber() { - @Override - public void onNext(Report report) { - super.onNext(report); - view.displayReports(new ArrayList<>(report.groupByCategory().values())); - } - }); - -// api.getExpenses(startDate, endDate) -// .subscribeOn(Schedulers.newThread()) -// .observeOn(AndroidSchedulers.mainThread()) -// .subscribe(expenses -> { -// //TODO Don't like this map here, this logic should be on database side, though I am too lazy now -// Map reportMap = new TreeMap<>((lhs, rhs) -> { -// if (lhs == rhs) { -// return 0; -// } -// if (lhs == null) { -// return -1; -// } -// if (rhs == null) { -// return 1; -// } -// return lhs.compareTo(rhs); -// }); -// for (Expense e : expenses) { -// Report r = reportMap.get(e.getCategory().getTitle()); -// if (r == null) { -// r = new Report(); -// } -// r.addExpense(e); -// reportMap.put(e.getCategory().getTitle(), r); -// } -// view.displayReports(new ArrayList<>(reportMap.values())); -// -// }); - } - - public void goToExpensesList(Date date, int selectedPeriod) { - this.goToExpensesList(date, selectedPeriod, null); - } - - public void goToExpensesList(Date date, int selectedPeriod, Long category) { - date = DateUtils.fillCurrentTime(date); - Date startDate = date; - Date endDate = date; - - switch (selectedPeriod) { - case ReportFragment.PERIOD_DAY: - startDate = DateUtils.getDayStart(date); - endDate = DateUtils.getDayEnd(date); - break; - case ReportFragment.PERIOD_WEEK: - startDate = DateUtils.getWeekStart(date); - endDate = DateUtils.getWeekEnd(date); - break; - case ReportFragment.PERIOD_MONTH: - startDate = DateUtils.getMonthStart(date); - endDate = DateUtils.getMonthEnd(date); - break; - } - Page.goToExpensesList(view.getActivity(), startDate, endDate, category); - } -} \ No newline at end of file diff --git a/outlay/app/src/main/java/com/outlay/utils/FormatUtils.java b/outlay/app/src/main/java/com/outlay/utils/FormatUtils.java index 3a47f8d..553195a 100644 --- a/outlay/app/src/main/java/com/outlay/utils/FormatUtils.java +++ b/outlay/app/src/main/java/com/outlay/utils/FormatUtils.java @@ -1,6 +1,7 @@ package com.outlay.utils; import java.math.BigDecimal; +import java.text.DecimalFormat; /** * Created by Bogdan Melnychuk on 2/10/16. @@ -11,6 +12,6 @@ public static String formatAmount(Double amount) { } public static String formatAmount(BigDecimal amount) { - return String.format("%.2f", amount.doubleValue()); + return new DecimalFormat("#0.00").format(amount); } } diff --git a/outlay/app/src/main/java/com/outlay/utils/IconUtils.java b/outlay/app/src/main/java/com/outlay/utils/IconUtils.java index 0a22d40..46ad394 100644 --- a/outlay/app/src/main/java/com/outlay/utils/IconUtils.java +++ b/outlay/app/src/main/java/com/outlay/utils/IconUtils.java @@ -1,15 +1,87 @@ package com.outlay.utils; import com.github.johnkil.print.PrintView; -import com.outlay.dao.Category; +import com.outlay.database.dao.Category; + +import java.util.Arrays; +import java.util.List; /** * Created by Bogdan Melnychuk on 2/2/16. */ public final class IconUtils { - public static void loadCategoryIcon(Category category, PrintView printView) { - loadCategoryIcon(category.getIcon(), printView); - printView.setIconColor(category.getColor()); + private static final String[] all = { + "ic_coins", + "ic_flight", + "ic_airplane", + "ic_bakery", + "ic_beach", + "ic_basketball", + "ic_front", + "ic_flamenco", + "ic_fast_food", + "ic_subscribe", + "ic_t_shirt", + "ic_md", + "ic_map", + "ic_medical", + "ic_flower", + "ic_fuel", + "ic_money", + "ic_tea", + "ic_tag", + "ic_mobilephone", + "ic_telephone", + "ic_multiple", + "ic_hairsalon", + "ic_books", + "ic_bowling", + "ic_hand", + "ic_note", + "ic_tool", + "ic_tools", + "ic_people", + "ic_hardbound", + "ic_box", + "ic_cars", + "ic_healthcare", + "ic_photo", + "ic_two", + "ic_weightlifting", + "ic_poor", + "ic_heart", + "ic_chart", + "ic_christmas", + "ic_insurance", + "ic_rent", + "ic_restaurant", + "ic_horse", + "ic_cigarette", + "ic_cleaning", + "ic_house", + "ic_run", + "ic_scissors", + "ic_italian_food", + "ic_justice", + "ic_climbing", + "ic_screen", + "ic_shopping", + "ic_legal", + "ic_controller", + "ic_dog", + "ic_lifeline", + "ic_sofa", + "ic_sportive", + "ic_locked", + "ic_donation", + "ic_draw", + "ic_luggage", + "ic_makeup", + "ic_ducks" + }; + + public static List getAll() { + return Arrays.asList(all); } public static void loadCategoryIcon(com.outlay.domain.model.Category category, PrintView printView) { diff --git a/outlay/app/src/main/java/com/outlay/view/Page.java b/outlay/app/src/main/java/com/outlay/view/Page.java index 974fb14..18ac0c6 100644 --- a/outlay/app/src/main/java/com/outlay/view/Page.java +++ b/outlay/app/src/main/java/com/outlay/view/Page.java @@ -5,7 +5,6 @@ import android.os.Bundle; import com.outlay.R; -import com.outlay.model.Report; import com.outlay.view.activity.SingleFragmentActivity; import com.outlay.view.fragment.CategoryDetailsFragment; import com.outlay.view.fragment.ExpensesDetailsFragment; diff --git a/outlay/app/src/main/java/com/outlay/view/activity/BaseActivity.java b/outlay/app/src/main/java/com/outlay/view/activity/BaseActivity.java index c93dde0..1f07180 100644 --- a/outlay/app/src/main/java/com/outlay/view/activity/BaseActivity.java +++ b/outlay/app/src/main/java/com/outlay/view/activity/BaseActivity.java @@ -20,7 +20,7 @@ import com.outlay.R; import com.outlay.domain.model.Category; import com.outlay.domain.model.DateSummary; -import com.outlay.model.Summary; +import com.outlay.utils.FormatUtils; import com.outlay.utils.IconUtils; import com.outlay.view.alert.Alert; import com.outlay.view.fragment.AboutFragment; @@ -120,9 +120,9 @@ public void updateDrawerData(DateSummary summary) { TextView weekAmount = (TextView) headerView.findViewById(R.id.weekAmount); TextView monthAmount = (TextView) headerView.findViewById(R.id.monthAmount); - dateAmount.setText(String.valueOf(summary.getDayAmount())); - weekAmount.setText(String.valueOf(summary.getWeekAmount())); - monthAmount.setText(String.valueOf(summary.getMonthAmount())); + dateAmount.setText(FormatUtils.formatAmount(summary.getDayAmount())); + weekAmount.setText(FormatUtils.formatAmount(summary.getWeekAmount())); + monthAmount.setText(FormatUtils.formatAmount(summary.getMonthAmount())); LinearLayout categoriesContainer = (LinearLayout) headerView.findViewById(R.id.mostSpentCategories); categoriesContainer.removeAllViews(); diff --git a/outlay/app/src/main/java/com/outlay/adapter/CategoriesDraggableGridAdapter.java b/outlay/app/src/main/java/com/outlay/view/adapter/CategoriesDraggableGridAdapter.java similarity index 97% rename from outlay/app/src/main/java/com/outlay/adapter/CategoriesDraggableGridAdapter.java rename to outlay/app/src/main/java/com/outlay/view/adapter/CategoriesDraggableGridAdapter.java index 5728ffe..46c6bad 100644 --- a/outlay/app/src/main/java/com/outlay/adapter/CategoriesDraggableGridAdapter.java +++ b/outlay/app/src/main/java/com/outlay/view/adapter/CategoriesDraggableGridAdapter.java @@ -1,4 +1,4 @@ -package com.outlay.adapter; +package com.outlay.view.adapter; import android.support.v4.content.ContextCompat; import android.support.v7.widget.RecyclerView; @@ -9,7 +9,7 @@ import com.github.johnkil.print.PrintView; import com.outlay.R; -import com.outlay.adapter.listener.OnCategoryClickListener; +import com.outlay.view.adapter.listener.OnCategoryClickListener; import com.outlay.domain.model.Category; import com.outlay.utils.IconUtils; import com.outlay.view.helper.itemtouch.ItemTouchHelperAdapter; diff --git a/outlay/app/src/main/java/com/outlay/adapter/CategoriesGridAdapter.java b/outlay/app/src/main/java/com/outlay/view/adapter/CategoriesGridAdapter.java similarity index 96% rename from outlay/app/src/main/java/com/outlay/adapter/CategoriesGridAdapter.java rename to outlay/app/src/main/java/com/outlay/view/adapter/CategoriesGridAdapter.java index 99dad4f..2bb2e34 100644 --- a/outlay/app/src/main/java/com/outlay/adapter/CategoriesGridAdapter.java +++ b/outlay/app/src/main/java/com/outlay/view/adapter/CategoriesGridAdapter.java @@ -1,4 +1,4 @@ -package com.outlay.adapter; +package com.outlay.view.adapter; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; @@ -8,7 +8,7 @@ import com.github.johnkil.print.PrintView; import com.outlay.R; -import com.outlay.adapter.listener.OnCategoryClickListener; +import com.outlay.view.adapter.listener.OnCategoryClickListener; import com.outlay.domain.model.Category; import com.outlay.utils.IconUtils; diff --git a/outlay/app/src/main/java/com/outlay/adapter/ExpenseAdapter.java b/outlay/app/src/main/java/com/outlay/view/adapter/ExpenseAdapter.java similarity index 97% rename from outlay/app/src/main/java/com/outlay/adapter/ExpenseAdapter.java rename to outlay/app/src/main/java/com/outlay/view/adapter/ExpenseAdapter.java index 40b471b..57c3260 100644 --- a/outlay/app/src/main/java/com/outlay/adapter/ExpenseAdapter.java +++ b/outlay/app/src/main/java/com/outlay/view/adapter/ExpenseAdapter.java @@ -1,4 +1,4 @@ -package com.outlay.adapter; +package com.outlay.view.adapter; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; @@ -8,8 +8,8 @@ import com.github.johnkil.print.PrintView; import com.outlay.R; -import com.outlay.dao.Expense; import com.outlay.core.utils.DateUtils; +import com.outlay.domain.model.Expense; import com.outlay.utils.FormatUtils; import com.outlay.utils.IconUtils; diff --git a/outlay/app/src/main/java/com/outlay/adapter/IconsGridAdapter.java b/outlay/app/src/main/java/com/outlay/view/adapter/IconsGridAdapter.java similarity index 97% rename from outlay/app/src/main/java/com/outlay/adapter/IconsGridAdapter.java rename to outlay/app/src/main/java/com/outlay/view/adapter/IconsGridAdapter.java index 7c6ce31..ca7a3f0 100644 --- a/outlay/app/src/main/java/com/outlay/adapter/IconsGridAdapter.java +++ b/outlay/app/src/main/java/com/outlay/view/adapter/IconsGridAdapter.java @@ -1,4 +1,4 @@ -package com.outlay.adapter; +package com.outlay.view.adapter; import android.content.Context; import android.support.v4.content.ContextCompat; @@ -10,7 +10,6 @@ import com.github.johnkil.print.PrintView; import com.outlay.R; import com.outlay.utils.IconUtils; -import com.outlay.utils.ResourceUtils; import java.util.List; diff --git a/outlay/app/src/main/java/com/outlay/adapter/ReportAdapter.java b/outlay/app/src/main/java/com/outlay/view/adapter/ReportAdapter.java similarity index 98% rename from outlay/app/src/main/java/com/outlay/adapter/ReportAdapter.java rename to outlay/app/src/main/java/com/outlay/view/adapter/ReportAdapter.java index f957ec6..c8375a3 100644 --- a/outlay/app/src/main/java/com/outlay/adapter/ReportAdapter.java +++ b/outlay/app/src/main/java/com/outlay/view/adapter/ReportAdapter.java @@ -1,4 +1,4 @@ -package com.outlay.adapter; +package com.outlay.view.adapter; import android.graphics.Color; import android.support.v7.widget.RecyclerView; @@ -99,7 +99,7 @@ public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { } else if (holder instanceof ChartViewHolder) { ChartViewHolder charViewHolder = (ChartViewHolder) holder; updateChartData(reports, charViewHolder.chart); - charViewHolder.chart.animateY(1000, Easing.EasingOption.EaseInOutQuad); + //charViewHolder.chart.animateY(1000, Easing.EasingOption.EaseInOutQuad); charViewHolder.chart.setOnChartValueSelectedListener(new OnChartValueSelectedListener() { @Override public void onValueSelected(Entry e, int dataSetIndex, Highlight h) { diff --git a/outlay/app/src/main/java/com/outlay/adapter/listener/OnCategoryClickListener.java b/outlay/app/src/main/java/com/outlay/view/adapter/listener/OnCategoryClickListener.java similarity index 80% rename from outlay/app/src/main/java/com/outlay/adapter/listener/OnCategoryClickListener.java rename to outlay/app/src/main/java/com/outlay/view/adapter/listener/OnCategoryClickListener.java index 7cc82b7..a42da62 100644 --- a/outlay/app/src/main/java/com/outlay/adapter/listener/OnCategoryClickListener.java +++ b/outlay/app/src/main/java/com/outlay/view/adapter/listener/OnCategoryClickListener.java @@ -1,4 +1,4 @@ -package com.outlay.adapter.listener; +package com.outlay.view.adapter.listener; import com.outlay.domain.model.Category; diff --git a/outlay/app/src/main/java/com/outlay/view/autocomplete/CategoryAutoCompleteAdapter.java b/outlay/app/src/main/java/com/outlay/view/autocomplete/CategoryAutoCompleteAdapter.java index f6ddec6..707a905 100644 --- a/outlay/app/src/main/java/com/outlay/view/autocomplete/CategoryAutoCompleteAdapter.java +++ b/outlay/app/src/main/java/com/outlay/view/autocomplete/CategoryAutoCompleteAdapter.java @@ -11,7 +11,7 @@ import com.github.johnkil.print.PrintView; import com.outlay.R; -import com.outlay.dao.Category; +import com.outlay.domain.model.Category; import com.outlay.utils.IconUtils; import java.util.ArrayList; diff --git a/outlay/app/src/main/java/com/outlay/view/fragment/CategoriesFragment.java b/outlay/app/src/main/java/com/outlay/view/fragment/CategoriesFragment.java index fda0093..f2b4566 100644 --- a/outlay/app/src/main/java/com/outlay/view/fragment/CategoriesFragment.java +++ b/outlay/app/src/main/java/com/outlay/view/fragment/CategoriesFragment.java @@ -13,9 +13,10 @@ import com.outlay.App; import com.outlay.R; -import com.outlay.adapter.CategoriesDraggableGridAdapter; +import com.outlay.view.adapter.CategoriesDraggableGridAdapter; import com.outlay.domain.model.Category; -import com.outlay.presenter.CategoriesPresenter; +import com.outlay.mvp.presenter.CategoriesPresenter; +import com.outlay.mvp.view.CategoriesView; import com.outlay.utils.ResourceUtils; import com.outlay.view.Page; import com.outlay.view.helper.itemtouch.OnDragListener; @@ -31,7 +32,7 @@ /** * Created by Bogdan Melnychuk on 1/20/16. */ -public class CategoriesFragment extends BaseFragment implements OnDragListener { +public class CategoriesFragment extends BaseFragment implements OnDragListener, CategoriesView { @Bind(R.id.toolbar) Toolbar toolbar; @@ -106,7 +107,8 @@ public void onEndDrag(RecyclerView.ViewHolder viewHolder) { presenter.updateCategories(items); } - public void displayCategories(List categoryList) { + @Override + public void showCategories(List categoryList) { adapter.setItems(categoryList); } } diff --git a/outlay/app/src/main/java/com/outlay/view/fragment/CategoryDetailsFragment.java b/outlay/app/src/main/java/com/outlay/view/fragment/CategoryDetailsFragment.java index fe21acd..a69d312 100644 --- a/outlay/app/src/main/java/com/outlay/view/fragment/CategoryDetailsFragment.java +++ b/outlay/app/src/main/java/com/outlay/view/fragment/CategoryDetailsFragment.java @@ -17,11 +17,12 @@ import com.outlay.App; import com.outlay.R; -import com.outlay.adapter.IconsGridAdapter; +import com.outlay.view.adapter.IconsGridAdapter; import com.outlay.domain.model.Category; -import com.outlay.helper.TextWatcherAdapter; -import com.outlay.model.Icon; -import com.outlay.presenter.CategoryDetailsPresenter; +import com.outlay.view.helper.TextWatcherAdapter; +import com.outlay.mvp.presenter.CategoryDetailsPresenter; +import com.outlay.mvp.view.CategoryDetailsView; +import com.outlay.utils.IconUtils; import com.outlay.utils.ResourceUtils; import com.outlay.view.alert.Alert; @@ -36,7 +37,7 @@ /** * Created by Bogdan Melnychuk on 1/20/16. */ -public class CategoryDetailsFragment extends BaseFragment { +public class CategoryDetailsFragment extends BaseFragment implements CategoryDetailsView { public static final String ARG_CATEGORY_PARAM = "_argCategoryId"; @Bind(R.id.toolbar) @@ -97,13 +98,10 @@ public boolean onOptionsItemSelected(MenuItem item) { Category category = getCategory(); if (validateCategory(category)) { presenter.updateCategory(getCategory()); - getActivity().onBackPressed(); } break; case R.id.action_delete: presenter.deleteCategory(getCategory()); - this.category.setId(null); - getActivity().onBackPressed(); break; } return super.onOptionsItemSelected(item); @@ -114,7 +112,7 @@ public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); GridLayoutManager gridLayoutManager = new GridLayoutManager(getActivity(), 4); iconsGrid.setLayoutManager(gridLayoutManager); - adapter = new IconsGridAdapter(Icon.getAll()); + adapter = new IconsGridAdapter(IconUtils.getAll()); iconsGrid.setAdapter(adapter); colorPicker.setOnColorChangedListener(i -> adapter.setActiveColor(i)); @@ -124,7 +122,7 @@ public void onViewCreated(View view, Bundle savedInstanceState) { presenter.loadCategory(categoryId); } else { getActivity().setTitle(getString(R.string.caption_edit_category)); - displayCategory(new Category()); + showCategory(new Category()); } categoryName.addTextChangedListener(new TextWatcherAdapter() { @@ -136,14 +134,7 @@ public void onTextChanged(CharSequence s, int start, int before, int count) { } @Override - public void onDestroy() { -// if(category.getId() != null) { -// category.refresh(); -// } - super.onDestroy(); - } - - public void displayCategory(Category category) { + public void showCategory(Category category) { this.category = category; if (category.getId() == null) { @@ -158,6 +149,11 @@ public void displayCategory(Category category) { } } + @Override + public void finish() { + getActivity().onBackPressed(); + } + public Category getCategory() { category.setTitle(categoryName.getText().toString()); category.setColor(colorPicker.getColor()); @@ -172,7 +168,7 @@ private boolean validateCategory(Category category) { categoryInputLayout.setError(getString(R.string.error_category_name_empty)); categoryName.requestFocus(); result = false; - } else if(TextUtils.isEmpty(category.getIcon())) { + } else if (TextUtils.isEmpty(category.getIcon())) { Alert.error(getRootView(), getString(R.string.error_category_icon)); result = false; } diff --git a/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesDetailsFragment.java b/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesDetailsFragment.java index f07c5a7..7a3742c 100644 --- a/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesDetailsFragment.java +++ b/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesDetailsFragment.java @@ -16,11 +16,12 @@ import com.github.johnkil.print.PrintView; import com.outlay.App; import com.outlay.R; -import com.outlay.dao.Category; -import com.outlay.dao.Expense; -import com.outlay.helper.TextWatcherAdapter; -import com.outlay.presenter.ExpensesDetailsPresenter; import com.outlay.core.utils.DateUtils; +import com.outlay.domain.model.Category; +import com.outlay.domain.model.Expense; +import com.outlay.view.helper.TextWatcherAdapter; +import com.outlay.mvp.presenter.ExpensesDetailsPresenter; +import com.outlay.mvp.view.ExpenseDetailsView; import com.outlay.utils.FormatUtils; import com.outlay.utils.IconUtils; import com.outlay.utils.ResourceUtils; @@ -28,6 +29,7 @@ import com.outlay.view.dialog.DatePickerFragment; import com.rengwuxian.materialedittext.MaterialAutoCompleteTextView; +import java.math.BigDecimal; import java.util.Calendar; import java.util.Date; import java.util.List; @@ -40,7 +42,7 @@ /** * Created by Bogdan Melnychuk on 1/20/16. */ -public class ExpensesDetailsFragment extends BaseFragment { +public class ExpensesDetailsFragment extends BaseFragment implements ExpenseDetailsView { public static final String ARG_EXPENSE_ID = "_argExpenseId"; public static final String ARG_DATE = "_argDate"; @@ -114,7 +116,6 @@ public boolean onOptionsItemSelected(MenuItem item) { break; case R.id.action_delete: presenter.deleteExpense(getExpense()); - expense.setId(null); getActivity().onBackPressed(); break; } @@ -145,7 +146,7 @@ public void onViewCreated(View view, Bundle savedInstanceState) { presenter.loadExpense(expenseId); } else { getActivity().setTitle(getString(R.string.caption_new_expense)); - displayExpense(new Expense()); + showExpense(new Expense()); } amount.addTextChangedListener(new TextWatcherAdapter() { @Override @@ -167,11 +168,13 @@ private void showDatePickerDialog() { datePickerFragment.show(getChildFragmentManager(), "datePicker"); } - public void setCategories(List categories) { + @Override + public void showCategories(List categories) { autoCompleteAdapter.setItems(categories); } - public void displayExpense(Expense e) { + @Override + public void showExpense(Expense e) { this.expense = e; if (e.getId() != null) { selectedCategory = e.getCategory(); @@ -191,7 +194,7 @@ public Expense getExpense() { expense.setCategory(selectedCategory); } if (!TextUtils.isEmpty(amount.getText().toString())) { - expense.setAmount(Double.valueOf(amount.getText().toString())); + expense.setAmount(new BigDecimal(amount.getText().toString())); } expense.setNote(note.getText().toString()); return expense; @@ -220,12 +223,4 @@ private boolean validateInput() { return result; } - - @Override - public void onDestroy() { - if (expense.getId() != null) { - expense.refresh(); - } - super.onDestroy(); - } } diff --git a/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesListFragment.java b/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesListFragment.java index 1a7fd4a..a6e7e31 100644 --- a/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesListFragment.java +++ b/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesListFragment.java @@ -14,11 +14,13 @@ import com.github.johnkil.print.PrintView; import com.outlay.App; import com.outlay.R; -import com.outlay.adapter.ExpenseAdapter; -import com.outlay.dao.Category; -import com.outlay.dao.Expense; -import com.outlay.presenter.ExpensesListPresenter; +import com.outlay.view.adapter.ExpenseAdapter; import com.outlay.core.utils.DateUtils; +import com.outlay.domain.model.Category; +import com.outlay.domain.model.Expense; +import com.outlay.domain.model.Report; +import com.outlay.mvp.presenter.ExpensesListPresenter; +import com.outlay.mvp.view.ExpensesView; import com.outlay.utils.IconUtils; import com.outlay.utils.ResourceUtils; import com.outlay.view.Page; @@ -34,7 +36,7 @@ /** * Created by Bogdan Melnychuk on 1/20/16. */ -public class ExpensesListFragment extends BaseFragment { +public class ExpensesListFragment extends BaseFragment implements ExpensesView { public static final String ARG_CATEGORY_ID = "_argCategoryId"; public static final String ARG_DATE_FROM = "_argDateFrom"; public static final String ARG_DATE_TO = "_argDateTo"; @@ -107,15 +109,6 @@ public void onViewCreated(View view, Bundle savedInstanceState) { recyclerView.setAdapter(adapter); fab.setImageDrawable(ResourceUtils.getMaterialToolbarIcon(getActivity(), R.string.ic_material_add)); fab.setOnClickListener(v -> Page.goToExpenseDetails(getActivity(), null)); - - if(categoryId != null) { - Category category = presenter.getCategoryById(categoryId); - IconUtils.loadCategoryIcon(category, filterCategoryIcon); - filterCategoryName.setText(category.getTitle()); - } else { - filterCategoryName.setVisibility(View.GONE); - filterCategoryIcon.setVisibility(View.GONE); - } filterDateLabel.setText(getDateLabel()); } @@ -125,7 +118,7 @@ public void onResume() { presenter.loadExpenses(dateFrom, dateTo, categoryId); } - public void displayExpenses(List expenses) { + private void displayExpenses(List expenses) { if (expenses.isEmpty()) { noResults.setVisibility(View.VISIBLE); } else { @@ -134,11 +127,27 @@ public void displayExpenses(List expenses) { } } + private void displayCategory(Category category) { + if (category != null) { + IconUtils.loadCategoryIcon(category, filterCategoryIcon); + filterCategoryName.setText(category.getTitle()); + } else { + filterCategoryName.setVisibility(View.GONE); + filterCategoryIcon.setVisibility(View.GONE); + } + } + + @Override + public void showReport(Report report) { + displayExpenses(report.getExpenses()); + displayCategory(report.getCategory()); + } + private String getDateLabel() { String dateFromStr = DateUtils.toShortString(dateFrom); String dateToStr = DateUtils.toShortString(dateTo); String result = dateFromStr; - if(!dateFromStr.equals(dateToStr)) { + if (!dateFromStr.equals(dateToStr)) { result += " - " + dateToStr; } return result; diff --git a/outlay/app/src/main/java/com/outlay/view/fragment/MainFragment.java b/outlay/app/src/main/java/com/outlay/view/fragment/MainFragment.java index 7777f1c..0d2f232 100644 --- a/outlay/app/src/main/java/com/outlay/view/fragment/MainFragment.java +++ b/outlay/app/src/main/java/com/outlay/view/fragment/MainFragment.java @@ -21,17 +21,20 @@ import com.outlay.App; import com.outlay.R; -import com.outlay.adapter.CategoriesGridAdapter; -import com.outlay.domain.model.DateSummary; -import com.outlay.helper.TextWatcherAdapter; -import com.outlay.model.Summary; -import com.outlay.presenter.MainFragmentPresenter; import com.outlay.core.utils.DateUtils; +import com.outlay.domain.model.DateSummary; +import com.outlay.domain.model.Expense; +import com.outlay.mvp.presenter.EnterExpensePresenter; +import com.outlay.mvp.view.EnterExpenseView; import com.outlay.utils.DeviceUtils; +import com.outlay.utils.FormatUtils; import com.outlay.utils.ResourceUtils; import com.outlay.view.Page; import com.outlay.view.activity.BaseActivity; +import com.outlay.view.adapter.CategoriesGridAdapter; +import com.outlay.view.alert.Alert; import com.outlay.view.dialog.DatePickerFragment; +import com.outlay.view.helper.TextWatcherAdapter; import com.outlay.view.numpad.NumpadEditable; import com.outlay.view.numpad.NumpadView; import com.outlay.view.numpad.SimpleNumpadValidator; @@ -46,7 +49,7 @@ import butterknife.Bind; import butterknife.ButterKnife; -public class MainFragment extends BaseFragment implements AppBarLayout.OnOffsetChangedListener { +public class MainFragment extends BaseFragment implements AppBarLayout.OnOffsetChangedListener, EnterExpenseView { private static final float PERCENTAGE_TO_HIDE_TITLE_DETAILS = 0.2f; private static final int ALPHA_ANIMATIONS_DURATION = 200; @@ -79,7 +82,7 @@ public class MainFragment extends BaseFragment implements AppBarLayout.OnOffsetC CoordinatorLayout coordinatorLayout; @Inject - MainFragmentPresenter presenter; + EnterExpensePresenter presenter; private CategoriesGridAdapter adapter; private boolean mIsTheTitleContainerVisible = false; @@ -151,18 +154,7 @@ public void onTextChanged(CharSequence s, int start, int before, int count) { e.setAmount(new BigDecimal(amountText.getText().toString())); e.setReportedAt(selectedDate); presenter.insertExpense(e); - presenter.loadSummary(new Date()); - amountText.setText(""); - - String message = getString(R.string.info_expense_created); - message = String.format(message, e.getAmount(), e.getCategory().getTitle()); -// Alert.info(getRootView(), message, -// v -> { -// e.delete(); -// presenter.loadSummary(new Date()); -// amountText.setText(String.valueOf(e.getAmount())); -// } -// ); + cleanAmountInput(); } else { validator.onInvalidInput(amountText.getText().toString()); } @@ -185,7 +177,7 @@ public void onTextChanged(CharSequence s, int start, int before, int count) { @Override public void onResume() { super.onResume(); - amountText.setText(""); + cleanAmountInput(); presenter.loadCategories(); presenter.loadSummary(new Date()); } @@ -244,11 +236,34 @@ public void inputError() { amountText.startAnimation(shakeAnimation); } - public void displaySummary(DateSummary dateSummary) { + @Override + public void showDateSummary(DateSummary dateSummary) { ((BaseActivity) getActivity()).updateDrawerData(dateSummary); } - public void displayCategories(List categoryList) { + @Override + public void setAmount(BigDecimal amount) { + amountText.setText(FormatUtils.formatAmount(amount)); + } + + @Override + public void alertExpenseSuccess(Expense e) { + String message = getString(R.string.info_expense_created); + message = String.format(message, e.getAmount(), e.getCategory().getTitle()); + Alert.info(getRootView(), message, + v -> { + presenter.deleteExpense(e); + amountText.setText(FormatUtils.formatAmount(e.getAmount())); + } + ); + } + + @Override + public void showCategories(List categoryList) { adapter.setItems(categoryList); } + + private void cleanAmountInput() { + amountText.setText(""); + } } diff --git a/outlay/app/src/main/java/com/outlay/view/fragment/ReportFragment.java b/outlay/app/src/main/java/com/outlay/view/fragment/ReportFragment.java index a4c5292..3e9bcdd 100644 --- a/outlay/app/src/main/java/com/outlay/view/fragment/ReportFragment.java +++ b/outlay/app/src/main/java/com/outlay/view/fragment/ReportFragment.java @@ -15,12 +15,14 @@ import com.outlay.App; import com.outlay.R; -import com.outlay.adapter.ReportAdapter; -import com.outlay.domain.model.Report; -import com.outlay.helper.OnTabSelectedListenerAdapter; -import com.outlay.presenter.ReportPresenter; +import com.outlay.view.adapter.ReportAdapter; import com.outlay.core.utils.DateUtils; +import com.outlay.domain.model.Report; +import com.outlay.view.helper.OnTabSelectedListenerAdapter; +import com.outlay.mvp.presenter.ReportPresenter; +import com.outlay.mvp.view.StatisticView; import com.outlay.utils.ResourceUtils; +import com.outlay.view.Page; import com.outlay.view.dialog.DatePickerFragment; import java.util.Calendar; @@ -35,7 +37,7 @@ /** * Created by Bogdan Melnychuk on 1/20/16. */ -public class ReportFragment extends BaseFragment { +public class ReportFragment extends BaseFragment implements StatisticView { public static final String ARG_DATE = "_argDate"; public static final int PERIOD_DAY = 0; @@ -108,7 +110,7 @@ public boolean onOptionsItemSelected(MenuItem item) { datePickerFragment.show(getChildFragmentManager(), "datePicker"); break; case R.id.action_list: - presenter.goToExpensesList(selectedDate, selectedPeriod); + goToExpensesList(selectedDate, selectedPeriod); } return super.onOptionsItemSelected(item); } @@ -136,10 +138,11 @@ public void onTabSelected(TabLayout.Tab tab) { recyclerView.setAdapter(adapter); presenter.loadReport(selectedDate, selectedPeriod); - adapter.setOnItemClickListener(report -> presenter.goToExpensesList(selectedDate, selectedPeriod, report.getCategory().getId())); + adapter.setOnItemClickListener(report -> goToExpensesList(selectedDate, selectedPeriod, report.getCategory().getId())); } - public void displayReports(List reportList) { + @Override + public void showReports(List reportList) { if (reportList.isEmpty()) { noResults.setVisibility(View.VISIBLE); } else { @@ -165,4 +168,30 @@ private void updateTitle() { break; } } + + public void goToExpensesList(Date date, int selectedPeriod) { + this.goToExpensesList(date, selectedPeriod, null); + } + + public void goToExpensesList(Date date, int selectedPeriod, Long category) { + date = DateUtils.fillCurrentTime(date); + Date startDate = date; + Date endDate = date; + + switch (selectedPeriod) { + case ReportFragment.PERIOD_DAY: + startDate = DateUtils.getDayStart(date); + endDate = DateUtils.getDayEnd(date); + break; + case ReportFragment.PERIOD_WEEK: + startDate = DateUtils.getWeekStart(date); + endDate = DateUtils.getWeekEnd(date); + break; + case ReportFragment.PERIOD_MONTH: + startDate = DateUtils.getMonthStart(date); + endDate = DateUtils.getMonthEnd(date); + break; + } + Page.goToExpensesList(getActivity(), startDate, endDate, category); + } } \ No newline at end of file diff --git a/outlay/app/src/main/java/com/outlay/helper/OnTabSelectedListenerAdapter.java b/outlay/app/src/main/java/com/outlay/view/helper/OnTabSelectedListenerAdapter.java similarity index 92% rename from outlay/app/src/main/java/com/outlay/helper/OnTabSelectedListenerAdapter.java rename to outlay/app/src/main/java/com/outlay/view/helper/OnTabSelectedListenerAdapter.java index acd8828..8938b7a 100644 --- a/outlay/app/src/main/java/com/outlay/helper/OnTabSelectedListenerAdapter.java +++ b/outlay/app/src/main/java/com/outlay/view/helper/OnTabSelectedListenerAdapter.java @@ -1,4 +1,4 @@ -package com.outlay.helper; +package com.outlay.view.helper; import android.support.design.widget.TabLayout; diff --git a/outlay/app/src/main/java/com/outlay/helper/TextWatcherAdapter.java b/outlay/app/src/main/java/com/outlay/view/helper/TextWatcherAdapter.java similarity index 93% rename from outlay/app/src/main/java/com/outlay/helper/TextWatcherAdapter.java rename to outlay/app/src/main/java/com/outlay/view/helper/TextWatcherAdapter.java index 59ebb2f..f78b28b 100644 --- a/outlay/app/src/main/java/com/outlay/helper/TextWatcherAdapter.java +++ b/outlay/app/src/main/java/com/outlay/view/helper/TextWatcherAdapter.java @@ -1,4 +1,4 @@ -package com.outlay.helper; +package com.outlay.view.helper; import android.text.Editable; import android.text.TextWatcher; diff --git a/outlay/database/.gitignore b/outlay/database/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/outlay/database/.gitignore @@ -0,0 +1 @@ +/build diff --git a/outlay/database/build.gradle b/outlay/database/build.gradle new file mode 100644 index 0000000..09eea29 --- /dev/null +++ b/outlay/database/build.gradle @@ -0,0 +1,44 @@ +apply plugin: 'com.android.library' +apply plugin: 'me.tatarka.retrolambda' + +def cfg = rootProject.ext.configuration +def libs = rootProject.ext.libraries + +android { + compileSdkVersion cfg.compileVersion + buildToolsVersion cfg.buildToolsVersion + + defaultConfig { + minSdkVersion cfg.minSdk + targetSdkVersion cfg.targetSdk + versionCode cfg.version_code + versionName cfg.version_name + + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + compile project(':domain') + compile fileTree(dir: 'libs', include: ['*.jar']) + androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { + exclude group: 'com.android.support', module: 'support-annotations' + }) + compile "com.android.support:appcompat-v7:${libs.supportVersion}" + testCompile 'junit:junit:4.12' + + //greenRobot + compile "de.greenrobot:greendao:${libs.greenDao}" +} diff --git a/outlay/database/proguard-rules.pro b/outlay/database/proguard-rules.pro new file mode 100644 index 0000000..39be0a8 --- /dev/null +++ b/outlay/database/proguard-rules.pro @@ -0,0 +1,17 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /Users/bmelnychuk/Library/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/outlay/database/src/main/AndroidManifest.xml b/outlay/database/src/main/AndroidManifest.xml new file mode 100644 index 0000000..b5c4284 --- /dev/null +++ b/outlay/database/src/main/AndroidManifest.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/outlay/database/src/main/java/com/outlay/database/adapter/CategoryDatabaseMapper.java b/outlay/database/src/main/java/com/outlay/database/adapter/CategoryDatabaseMapper.java new file mode 100644 index 0000000..72e24ec --- /dev/null +++ b/outlay/database/src/main/java/com/outlay/database/adapter/CategoryDatabaseMapper.java @@ -0,0 +1,50 @@ +package com.outlay.database.adapter; + +import com.outlay.domain.model.Category; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by bmelnychuk on 10/25/16. + */ + +public class CategoryDatabaseMapper { + public Category toCategory(com.outlay.database.dao.Category daoCategory) { + Category category = new Category(); + category.setColor(daoCategory.getColor()); + category.setIcon(daoCategory.getIcon()); + category.setOrder(daoCategory.getOrder()); + category.setId(daoCategory.getId()); + category.setTitle(daoCategory.getTitle()); + + return category; + } + + public com.outlay.database.dao.Category fromCategory(Category category) { + com.outlay.database.dao.Category daoCategory = new com.outlay.database.dao.Category(); + daoCategory.setColor(category.getColor()); + daoCategory.setIcon(category.getIcon()); + daoCategory.setOrder(category.getOrder()); + daoCategory.setId(category.getId()); + daoCategory.setTitle(category.getTitle()); + + return daoCategory; + } + + public List toCategories(List daoCategoryList) { + List result = new ArrayList<>(daoCategoryList.size()); + for (com.outlay.database.dao.Category c : daoCategoryList) { + result.add(toCategory(c)); + } + return result; + } + + public List fromCategories(List categoryList) { + List result = new ArrayList<>(categoryList.size()); + for (Category c : categoryList) { + result.add(fromCategory(c)); + } + return result; + } +} diff --git a/outlay/database/src/main/java/com/outlay/database/adapter/ExpenseDatabaseMapper.java b/outlay/database/src/main/java/com/outlay/database/adapter/ExpenseDatabaseMapper.java new file mode 100644 index 0000000..62e059f --- /dev/null +++ b/outlay/database/src/main/java/com/outlay/database/adapter/ExpenseDatabaseMapper.java @@ -0,0 +1,61 @@ +package com.outlay.database.adapter; + +import com.outlay.domain.model.Category; +import com.outlay.domain.model.Expense; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +/** + * Created by bmelnychuk on 10/25/16. + */ + +public class ExpenseDatabaseMapper { + private CategoryDatabaseMapper categoryDatabaseMapper; + + + public ExpenseDatabaseMapper(CategoryDatabaseMapper categoryDatabaseMapper) { + this.categoryDatabaseMapper = categoryDatabaseMapper; + } + + public Expense toExpense(com.outlay.database.dao.Expense dbExpense) { + Expense result = new Expense(); + result.setId(dbExpense.getId()); + result.setAmount(new BigDecimal(dbExpense.getAmount().toString())); + result.setNote(dbExpense.getNote()); + result.setReportedAt(dbExpense.getReportedAt()); + + Category category = categoryDatabaseMapper.toCategory(dbExpense.getCategory()); + result.setCategory(category); + + return result; + } + + public com.outlay.database.dao.Expense fromExpense(Expense expense) { + com.outlay.database.dao.Expense dbExpense = new com.outlay.database.dao.Expense(); + dbExpense.setId(expense.getId()); + dbExpense.setAmount(expense.getAmount().doubleValue()); + dbExpense.setCategoryId(expense.getCategory().getId()); + dbExpense.setNote(expense.getNote()); + dbExpense.setReportedAt(expense.getReportedAt()); + + return dbExpense; + } + + public List toExpenses(List daoExpenses) { + List result = new ArrayList<>(daoExpenses.size()); + for (com.outlay.database.dao.Expense c : daoExpenses) { + result.add(toExpense(c)); + } + return result; + } + + public List fromExpenses(List expenses) { + List result = new ArrayList<>(expenses.size()); + for (Expense c : expenses) { + result.add(fromExpense(c)); + } + return result; + } +} diff --git a/outlay/app/src/main/java/com/outlay/dao/Category.java b/outlay/database/src/main/java/com/outlay/database/dao/Category.java similarity index 98% rename from outlay/app/src/main/java/com/outlay/dao/Category.java rename to outlay/database/src/main/java/com/outlay/database/dao/Category.java index f2c6035..b64dc91 100644 --- a/outlay/app/src/main/java/com/outlay/dao/Category.java +++ b/outlay/database/src/main/java/com/outlay/database/dao/Category.java @@ -1,7 +1,8 @@ -package com.outlay.dao; +package com.outlay.database.dao; import java.util.List; -import com.outlay.dao.DaoSession; + +import de.greenrobot.dao.AbstractDao; import de.greenrobot.dao.DaoException; // THIS CODE IS GENERATED BY greenDAO, EDIT ONLY INSIDE THE "KEEP"-SECTIONS diff --git a/outlay/app/src/main/java/com/outlay/dao/CategoryDao.java b/outlay/database/src/main/java/com/outlay/database/dao/CategoryDao.java similarity index 98% rename from outlay/app/src/main/java/com/outlay/dao/CategoryDao.java rename to outlay/database/src/main/java/com/outlay/database/dao/CategoryDao.java index 6eb176d..4a9fa1c 100644 --- a/outlay/app/src/main/java/com/outlay/dao/CategoryDao.java +++ b/outlay/database/src/main/java/com/outlay/database/dao/CategoryDao.java @@ -1,4 +1,4 @@ -package com.outlay.dao; +package com.outlay.database.dao; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; @@ -8,8 +8,6 @@ import de.greenrobot.dao.Property; import de.greenrobot.dao.internal.DaoConfig; -import com.outlay.dao.Category; - // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. /** * DAO for table "CATEGORY". diff --git a/outlay/app/src/main/java/com/outlay/dao/DaoMaster.java b/outlay/database/src/main/java/com/outlay/database/dao/DaoMaster.java similarity index 96% rename from outlay/app/src/main/java/com/outlay/dao/DaoMaster.java rename to outlay/database/src/main/java/com/outlay/database/dao/DaoMaster.java index 081e5da..bc8e279 100644 --- a/outlay/app/src/main/java/com/outlay/dao/DaoMaster.java +++ b/outlay/database/src/main/java/com/outlay/database/dao/DaoMaster.java @@ -1,4 +1,4 @@ -package com.outlay.dao; +package com.outlay.database.dao; import android.content.Context; import android.database.sqlite.SQLiteDatabase; @@ -8,9 +8,6 @@ import de.greenrobot.dao.AbstractDaoMaster; import de.greenrobot.dao.identityscope.IdentityScopeType; -import com.outlay.dao.CategoryDao; -import com.outlay.dao.ExpenseDao; - // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. /** * Master of DAO (schema version 1): knows all DAOs. diff --git a/outlay/app/src/main/java/com/outlay/dao/DaoSession.java b/outlay/database/src/main/java/com/outlay/database/dao/DaoSession.java similarity index 90% rename from outlay/app/src/main/java/com/outlay/dao/DaoSession.java rename to outlay/database/src/main/java/com/outlay/database/dao/DaoSession.java index 41afb6a..3a2fa0b 100644 --- a/outlay/app/src/main/java/com/outlay/dao/DaoSession.java +++ b/outlay/database/src/main/java/com/outlay/database/dao/DaoSession.java @@ -1,4 +1,4 @@ -package com.outlay.dao; +package com.outlay.database.dao; import android.database.sqlite.SQLiteDatabase; @@ -9,12 +9,6 @@ import de.greenrobot.dao.identityscope.IdentityScopeType; import de.greenrobot.dao.internal.DaoConfig; -import com.outlay.dao.Category; -import com.outlay.dao.Expense; - -import com.outlay.dao.CategoryDao; -import com.outlay.dao.ExpenseDao; - // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. /** diff --git a/outlay/app/src/main/java/com/outlay/dao/Expense.java b/outlay/database/src/main/java/com/outlay/database/dao/Expense.java similarity index 98% rename from outlay/app/src/main/java/com/outlay/dao/Expense.java rename to outlay/database/src/main/java/com/outlay/database/dao/Expense.java index 939975e..59d55f1 100644 --- a/outlay/app/src/main/java/com/outlay/dao/Expense.java +++ b/outlay/database/src/main/java/com/outlay/database/dao/Expense.java @@ -1,6 +1,6 @@ -package com.outlay.dao; +package com.outlay.database.dao; -import com.outlay.dao.DaoSession; +import de.greenrobot.dao.AbstractDao; import de.greenrobot.dao.DaoException; // THIS CODE IS GENERATED BY greenDAO, EDIT ONLY INSIDE THE "KEEP"-SECTIONS diff --git a/outlay/app/src/main/java/com/outlay/dao/ExpenseDao.java b/outlay/database/src/main/java/com/outlay/database/dao/ExpenseDao.java similarity index 99% rename from outlay/app/src/main/java/com/outlay/dao/ExpenseDao.java rename to outlay/database/src/main/java/com/outlay/database/dao/ExpenseDao.java index 6642c3e..e11bcca 100644 --- a/outlay/app/src/main/java/com/outlay/dao/ExpenseDao.java +++ b/outlay/database/src/main/java/com/outlay/database/dao/ExpenseDao.java @@ -1,4 +1,4 @@ -package com.outlay.dao; +package com.outlay.database.dao; import java.util.List; import java.util.ArrayList; @@ -13,8 +13,6 @@ import de.greenrobot.dao.query.Query; import de.greenrobot.dao.query.QueryBuilder; -import com.outlay.dao.Expense; - // THIS CODE IS GENERATED BY greenDAO, DO NOT EDIT. /** * DAO for table "EXPENSE". diff --git a/outlay/database/src/main/java/com/outlay/database/source/CategoryDatabaseSource.java b/outlay/database/src/main/java/com/outlay/database/source/CategoryDatabaseSource.java new file mode 100644 index 0000000..19cf1b3 --- /dev/null +++ b/outlay/database/src/main/java/com/outlay/database/source/CategoryDatabaseSource.java @@ -0,0 +1,107 @@ +package com.outlay.database.source; + +import com.outlay.data.source.CategoryDataSource; +import com.outlay.database.adapter.CategoryDatabaseMapper; +import com.outlay.database.dao.CategoryDao; +import com.outlay.database.dao.ExpenseDao; +import com.outlay.domain.model.Category; +import com.outlay.domain.model.Expense; + +import java.util.List; + +import de.greenrobot.dao.query.DeleteQuery; +import rx.Observable; + +/** + * Created by bmelnychuk on 10/25/16. + */ + +public class CategoryDatabaseSource implements CategoryDataSource { + private CategoryDao categoryDao; + private ExpenseDao expenseDao; + private CategoryDatabaseMapper categoryMapper; + + public CategoryDatabaseSource( + CategoryDao categoryDao, + ExpenseDao expenseDao + ) { + this.categoryDao = categoryDao; + this.expenseDao = expenseDao; + this.categoryMapper = new CategoryDatabaseMapper(); + } + + @Override + public Observable> getAll() { + return Observable.create(subscriber -> { + try { + List categories = categoryDao.queryBuilder().orderAsc(CategoryDao.Properties.Order).list(); + List result = categoryMapper.toCategories(categories); + subscriber.onNext(result); + subscriber.onCompleted(); + } catch (Exception e) { + subscriber.onError(e); + } + }); + } + + @Override + public Observable getById(Long id) { + return Observable.create(subscriber -> { + try { + com.outlay.database.dao.Category c = categoryDao.load(id); + Category category = categoryMapper.toCategory(c); + subscriber.onNext(category); + subscriber.onCompleted(); + } catch (Exception e) { + subscriber.onError(e); + } + }); + } + + @Override + public Observable> saveAll(List categories) { + return Observable.create(subscriber -> { + try { + List dbCategories = categoryMapper.fromCategories(categories); + categoryDao.insertOrReplaceInTx(dbCategories); + subscriber.onNext(categoryMapper.toCategories(dbCategories)); + subscriber.onCompleted(); + } catch (Exception e) { + subscriber.onError(e); + } + }); + } + + @Override + public Observable save(Category category) { + return Observable.create(subscriber -> { + try { + com.outlay.database.dao.Category daoCategory = categoryMapper.fromCategory(category); + categoryDao.insertOrReplace(daoCategory); + subscriber.onNext(categoryMapper.toCategory(daoCategory)); + subscriber.onCompleted(); + } catch (Exception e) { + subscriber.onError(e); + } + }); + } + + @Override + public Observable remove(Category category) { + return Observable.create(subscriber -> { + try { + DeleteQuery deleteQuery = expenseDao.queryBuilder() + .where(ExpenseDao.Properties.CategoryId.eq(category.getId())) + .buildDelete(); + deleteQuery.executeDeleteWithoutDetachingEntities(); + + categoryDao.deleteByKey(category.getId()); + subscriber.onNext(category); + subscriber.onCompleted(); + } catch (Exception e) { + subscriber.onError(e); + } + }); + } + +} diff --git a/outlay/database/src/main/java/com/outlay/database/source/ExpenseDatabaseSource.java b/outlay/database/src/main/java/com/outlay/database/source/ExpenseDatabaseSource.java new file mode 100644 index 0000000..0a16414 --- /dev/null +++ b/outlay/database/src/main/java/com/outlay/database/source/ExpenseDatabaseSource.java @@ -0,0 +1,115 @@ +package com.outlay.database.source; + +import com.outlay.data.source.ExpenseDataSource; +import com.outlay.database.adapter.CategoryDatabaseMapper; +import com.outlay.database.adapter.ExpenseDatabaseMapper; +import com.outlay.database.dao.ExpenseDao; +import com.outlay.domain.model.Expense; + +import java.util.Date; +import java.util.List; + +import rx.Observable; + +/** + * Created by bmelnychuk on 10/25/16. + */ + +public class ExpenseDatabaseSource implements ExpenseDataSource { + private ExpenseDao expenseDao; + private ExpenseDatabaseMapper mapper; + private CategoryDatabaseMapper categoryDatabaseMapper; + + public ExpenseDatabaseSource(ExpenseDao expenseDao) { + this.expenseDao = expenseDao; + this.categoryDatabaseMapper = new CategoryDatabaseMapper(); + this.mapper = new ExpenseDatabaseMapper(categoryDatabaseMapper); + } + + @Override + public Observable saveExpense(Expense expense) { + return Observable.create(subscriber -> { + try { + com.outlay.database.dao.Expense dbExpense = mapper.fromExpense(expense); + expenseDao.insertOrReplace(dbExpense); + + subscriber.onNext(mapper.toExpense(dbExpense)); + subscriber.onCompleted(); + } catch (Exception e) { + subscriber.onError(e); + } + }); + } + + @Override + public Observable> getExpenses(Date startDate, Date endDate, Long categoryId) { + return Observable.create(subscriber -> { + try { + List dbExpenses = loadExpenses( + startDate, + endDate, + categoryId + ); + List expenses = mapper.toExpenses(dbExpenses); + subscriber.onNext(expenses); + subscriber.onCompleted(); + } catch (Exception e) { + subscriber.onError(e); + } + }); + } + + @Override + public Observable getById(Long expenseId) { + return Observable.create(subscriber -> { + try { + com.outlay.database.dao.Expense e = expenseDao.load(expenseId); + Expense category = mapper.toExpense(e); + subscriber.onNext(category); + subscriber.onCompleted(); + } catch (Exception e) { + subscriber.onError(e); + } + }); + } + + @Override + public Observable remove(Expense expense) { + return Observable.create(subscriber -> { + try { + com.outlay.database.dao.Expense daoExpense = mapper.fromExpense(expense); + expenseDao.delete(daoExpense); + subscriber.onNext(expense); + subscriber.onCompleted(); + } catch (Exception e) { + subscriber.onError(e); + } + }); + } + + @Override + public Observable> getExpenses(Date startDate, Date endDate) { + return getExpenses(startDate, endDate, null); + } + + private List loadExpenses( + Date startDate, + Date endDate, + Long categoryId + ) { + if (categoryId == null) { + List expenses = expenseDao.queryBuilder().where( + ExpenseDao.Properties.ReportedAt.ge(startDate), + ExpenseDao.Properties.ReportedAt.le(endDate) + ).list(); + return expenses; + } else { + List expenses = expenseDao.queryBuilder().where( + ExpenseDao.Properties.ReportedAt.ge(startDate), + ExpenseDao.Properties.ReportedAt.le(endDate), + ExpenseDao.Properties.CategoryId.eq(categoryId) + ).list(); + return expenses; + } + } +} diff --git a/outlay/domain/src/main/java/com/outlay/data/Test.java b/outlay/domain/src/main/java/com/outlay/data/Test.java new file mode 100644 index 0000000..f3c3830 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/data/Test.java @@ -0,0 +1,8 @@ +package com.outlay.data; + +/** + * Created by bmelnychuk on 10/25/16. + */ + +public class Test { +} diff --git a/outlay/domain/src/main/java/com/outlay/data/repository/ExpenseRepositoryImpl.java b/outlay/domain/src/main/java/com/outlay/data/repository/ExpenseRepositoryImpl.java new file mode 100644 index 0000000..7b5a118 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/data/repository/ExpenseRepositoryImpl.java @@ -0,0 +1,59 @@ +package com.outlay.data.repository; + +import com.outlay.data.source.ExpenseDataSource; +import com.outlay.domain.model.Expense; +import com.outlay.domain.repository.ExpenseRepository; + +import java.util.Date; +import java.util.List; + +import javax.inject.Inject; + +import rx.Observable; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public class ExpenseRepositoryImpl implements ExpenseRepository { + private ExpenseDataSource expenseDataSource; + + @Inject + public ExpenseRepositoryImpl( + ExpenseDataSource expenseDataSource + ) { + this.expenseDataSource = expenseDataSource; + } + + @Override + public Observable saveExpense(Expense expense) { + return expenseDataSource.saveExpense(expense); + } + + @Override + public Observable remove(Expense expense) { + return expenseDataSource.remove(expense); + } + + @Override + public Observable getById(Long expenseId) { + return expenseDataSource.getById(expenseId); + } + + @Override + public Observable> getExpenses( + Date startDate, + Date endDate + ) { + return getExpenses(startDate, endDate, null); + } + + @Override + public Observable> getExpenses( + Date startDate, + Date endDate, + Long categoryId + ) { + return expenseDataSource.getExpenses(startDate, endDate, categoryId); + } +} diff --git a/outlay/domain/src/main/java/com/outlay/data/source/CategoryDataSource.java b/outlay/domain/src/main/java/com/outlay/data/source/CategoryDataSource.java new file mode 100644 index 0000000..c3dfe05 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/data/source/CategoryDataSource.java @@ -0,0 +1,23 @@ +package com.outlay.data.source; + +import com.outlay.domain.model.Category; + +import java.util.List; + +import rx.Observable; + +/** + * Created by bmelnychuk on 10/25/16. + */ + +public interface CategoryDataSource { + Observable> getAll(); + + Observable getById(Long id); + + Observable> saveAll(List categories); + + Observable save(Category category); + + Observable remove(Category category); +} diff --git a/outlay/domain/src/main/java/com/outlay/data/source/ExpenseDataSource.java b/outlay/domain/src/main/java/com/outlay/data/source/ExpenseDataSource.java new file mode 100644 index 0000000..1e9d3bd --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/data/source/ExpenseDataSource.java @@ -0,0 +1,20 @@ +package com.outlay.data.source; + +import com.outlay.domain.model.Expense; + +import java.util.Date; +import java.util.List; + +import rx.Observable; + +/** + * Created by bmelnychuk on 10/25/16. + */ + +public interface ExpenseDataSource { + Observable saveExpense(Expense expense); + Observable> getExpenses(Date startDate, Date endDate); + Observable> getExpenses(Date startDate, Date endDate, Long categoryId); + Observable getById(Long expenseId); + Observable remove(Expense expense); +} diff --git a/outlay/domain/src/main/java/com/outlay/domain/interactor/DeleteExpenseUseCase.java b/outlay/domain/src/main/java/com/outlay/domain/interactor/DeleteExpenseUseCase.java new file mode 100644 index 0000000..8211789 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/domain/interactor/DeleteExpenseUseCase.java @@ -0,0 +1,33 @@ +package com.outlay.domain.interactor; + +import com.outlay.core.executor.PostExecutionThread; +import com.outlay.core.executor.ThreadExecutor; +import com.outlay.domain.model.Expense; +import com.outlay.domain.repository.ExpenseRepository; + +import javax.inject.Inject; + +import rx.Observable; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public class DeleteExpenseUseCase extends UseCase { + private ExpenseRepository expenseRepository; + + @Inject + public DeleteExpenseUseCase( + ThreadExecutor threadExecutor, + PostExecutionThread postExecutionThread, + ExpenseRepository expenseRepository + ) { + super(threadExecutor, postExecutionThread); + this.expenseRepository = expenseRepository; + } + + @Override + protected Observable buildUseCaseObservable(Expense expense) { + return expenseRepository.remove(expense); + } +} diff --git a/outlay/domain/src/main/java/com/outlay/domain/interactor/GetExpenseUseCase.java b/outlay/domain/src/main/java/com/outlay/domain/interactor/GetExpenseUseCase.java new file mode 100644 index 0000000..1c3df83 --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/domain/interactor/GetExpenseUseCase.java @@ -0,0 +1,33 @@ +package com.outlay.domain.interactor; + +import com.outlay.core.executor.PostExecutionThread; +import com.outlay.core.executor.ThreadExecutor; +import com.outlay.domain.model.Expense; +import com.outlay.domain.repository.ExpenseRepository; + +import javax.inject.Inject; + +import rx.Observable; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public class GetExpenseUseCase extends UseCase { + private ExpenseRepository expenseRepository; + + @Inject + public GetExpenseUseCase( + ThreadExecutor threadExecutor, + PostExecutionThread postExecutionThread, + ExpenseRepository expenseRepository + ) { + super(threadExecutor, postExecutionThread); + this.expenseRepository = expenseRepository; + } + + @Override + protected Observable buildUseCaseObservable(Long categoryId) { + return expenseRepository.getById(categoryId); + } +} diff --git a/outlay/domain/src/main/java/com/outlay/domain/interactor/LoadReportUseCase.java b/outlay/domain/src/main/java/com/outlay/domain/interactor/LoadReportUseCase.java index 8484907..9e9a553 100644 --- a/outlay/domain/src/main/java/com/outlay/domain/interactor/LoadReportUseCase.java +++ b/outlay/domain/src/main/java/com/outlay/domain/interactor/LoadReportUseCase.java @@ -2,8 +2,9 @@ import com.outlay.core.executor.PostExecutionThread; import com.outlay.core.executor.ThreadExecutor; -import com.outlay.domain.model.Period; +import com.outlay.domain.model.ExpensePeriod; import com.outlay.domain.model.Report; +import com.outlay.domain.repository.CategoryRepository; import com.outlay.domain.repository.ExpenseRepository; import javax.inject.Inject; @@ -14,28 +15,46 @@ * Created by bmelnychuk on 10/24/16. */ -public class LoadReportUseCase extends UseCase { +public class LoadReportUseCase extends UseCase { private ExpenseRepository expenseRepository; + private CategoryRepository categoryRepository; @Inject public LoadReportUseCase( ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread, - ExpenseRepository expenseRepository + ExpenseRepository expenseRepository, + CategoryRepository categoryRepository ) { super(threadExecutor, postExecutionThread); this.expenseRepository = expenseRepository; + this.categoryRepository = categoryRepository; } @Override - protected Observable buildUseCaseObservable(Period period) { - return expenseRepository.getExpenses(period.getStartDate(), period.getEndDate()) - .map(expenses -> { - Report report = new Report(); - report.setEndDate(period.getEndDate()); - report.setStartDate(period.getStartDate()); - report.setExpenses(expenses); - return report; - }); + protected Observable buildUseCaseObservable(ExpensePeriod period) { + if (period.getCategoryId() != null) { + return Observable.zip( + categoryRepository.getById(period.getCategoryId()), + expenseRepository.getExpenses(period.getStartDate(), period.getEndDate(), period.getCategoryId()), + (category, expenses) -> { + Report report = new Report(); + report.setEndDate(period.getEndDate()); + report.setStartDate(period.getStartDate()); + report.setExpenses(expenses); + report.setCategory(category); + return report; + } + ); + } else { + return expenseRepository.getExpenses(period.getStartDate(), period.getEndDate(), period.getCategoryId()) + .map(expenses -> { + Report report = new Report(); + report.setEndDate(period.getEndDate()); + report.setStartDate(period.getStartDate()); + report.setExpenses(expenses); + return report; + }); + } } } diff --git a/outlay/domain/src/main/java/com/outlay/domain/interactor/CreateExpenseUseCase.java b/outlay/domain/src/main/java/com/outlay/domain/interactor/SaveExpenseUseCase.java similarity index 91% rename from outlay/domain/src/main/java/com/outlay/domain/interactor/CreateExpenseUseCase.java rename to outlay/domain/src/main/java/com/outlay/domain/interactor/SaveExpenseUseCase.java index 2a9e746..555e756 100644 --- a/outlay/domain/src/main/java/com/outlay/domain/interactor/CreateExpenseUseCase.java +++ b/outlay/domain/src/main/java/com/outlay/domain/interactor/SaveExpenseUseCase.java @@ -16,11 +16,11 @@ * Created by bmelnychuk on 10/24/16. */ -public class CreateExpenseUseCase extends UseCase { +public class SaveExpenseUseCase extends UseCase { private ExpenseRepository expenseRepository; @Inject - public CreateExpenseUseCase( + public SaveExpenseUseCase( ThreadExecutor threadExecutor, PostExecutionThread postExecutionThread, ExpenseRepository expenseRepository diff --git a/outlay/domain/src/main/java/com/outlay/domain/model/ExpensePeriod.java b/outlay/domain/src/main/java/com/outlay/domain/model/ExpensePeriod.java new file mode 100644 index 0000000..d55eb1c --- /dev/null +++ b/outlay/domain/src/main/java/com/outlay/domain/model/ExpensePeriod.java @@ -0,0 +1,50 @@ +package com.outlay.domain.model; + +import java.util.Date; + +/** + * Created by bmelnychuk on 10/24/16. + */ + +public class ExpensePeriod { + private Date startDate; + private Date endDate; + private Long categoryId; + + public ExpensePeriod(Date startDate, Date endDate) { + this(startDate, endDate, null); + } + + public ExpensePeriod(Date startDate, Date endDate, Long categoryId) { + this.startDate = startDate; + this.endDate = endDate; + this.categoryId = categoryId; + } + + public Date getStartDate() { + return startDate; + } + + public ExpensePeriod setStartDate(Date startDate) { + this.startDate = startDate; + return this; + } + + public Date getEndDate() { + return endDate; + } + + public ExpensePeriod setEndDate(Date endDate) { + this.endDate = endDate; + return this; + } + + public Long getCategoryId() { + return categoryId; + } + + public ExpensePeriod setCategoryId(Long categoryId) { + this.categoryId = categoryId; + return this; + } +} diff --git a/outlay/domain/src/main/java/com/outlay/domain/model/Period.java b/outlay/domain/src/main/java/com/outlay/domain/model/Period.java deleted file mode 100644 index e155e18..0000000 --- a/outlay/domain/src/main/java/com/outlay/domain/model/Period.java +++ /dev/null @@ -1,35 +0,0 @@ -package com.outlay.domain.model; - -import java.util.Date; - -/** - * Created by bmelnychuk on 10/24/16. - */ - -public class Period { - private Date startDate; - private Date endDate; - - public Period(Date startDate, Date endDate) { - this.startDate = startDate; - this.endDate = endDate; - } - - public Date getStartDate() { - return startDate; - } - - public Period setStartDate(Date startDate) { - this.startDate = startDate; - return this; - } - - public Date getEndDate() { - return endDate; - } - - public Period setEndDate(Date endDate) { - this.endDate = endDate; - return this; - } -} diff --git a/outlay/domain/src/main/java/com/outlay/domain/repository/ExpenseRepository.java b/outlay/domain/src/main/java/com/outlay/domain/repository/ExpenseRepository.java index d54b2b6..090758e 100644 --- a/outlay/domain/src/main/java/com/outlay/domain/repository/ExpenseRepository.java +++ b/outlay/domain/src/main/java/com/outlay/domain/repository/ExpenseRepository.java @@ -1,7 +1,6 @@ package com.outlay.domain.repository; import com.outlay.domain.model.Expense; -import com.outlay.domain.model.Report; import java.util.Date; import java.util.List; @@ -14,5 +13,12 @@ public interface ExpenseRepository { Observable saveExpense(Expense expense); + + Observable remove(Expense expense); + + Observable getById(Long expenseId); + Observable> getExpenses(Date startDate, Date endDate); + + Observable> getExpenses(Date startDate, Date endDate, Long categoryId); } diff --git a/outlay/settings.gradle b/outlay/settings.gradle index 871bb2d..45aed57 100644 --- a/outlay/settings.gradle +++ b/outlay/settings.gradle @@ -1 +1 @@ -include ':app', ':daogenerator', ':domain' +include ':app', ':daogenerator', ':domain', ':database' From aded145b39d7dbaea1b02dabb56f4a790bc749b9 Mon Sep 17 00:00:00 2001 From: Bogdan Melnychuk Date: Tue, 25 Oct 2016 16:20:34 +0200 Subject: [PATCH 03/25] Integrating firebase --- outlay/.gitignore | 1 + outlay/app/build.gradle | 6 + outlay/app/src/main/AndroidManifest.xml | 12 +- outlay/app/src/main/java/com/outlay/App.java | 36 +++- .../di/{ => component}/AppComponent.java | 24 +-- .../outlay/di/component/UserComponent.java | 32 +++ .../java/com/outlay/di/module/AppModule.java | 10 +- .../java/com/outlay/di/module/DaoModule.java | 39 +--- .../java/com/outlay/di/module/UserModule.java | 78 +++++++ .../java/com/outlay/di/scope/UserScope.java | 15 ++ .../firebase/CategoryFirebaseSource.java | 159 ++++++++++++++ .../firebase/ExpenseFirebaseSource.java | 137 ++++++++++++ .../outlay/firebase/FirebaseRxWrapper.java | 70 ++++++ .../com/outlay/firebase/dto/CategoryDto.java | 53 +++++ .../com/outlay/firebase/dto/ExpenseDto.java | 53 +++++ .../firebase/dto/adapter/CategoryAdapter.java | 50 +++++ .../firebase/dto/adapter/ExpenseAdapter.java | 60 ++++++ .../java/com/outlay/impl/AndroidLogger.java | 43 ++++ .../outlay/impl/CategoryRepositoryImpl.java | 15 +- .../java/com/outlay/impl/OutlayAuthImpl.java | 50 +++++ .../outlay/mvp/presenter/AuthPresenter.java | 97 +++++++++ .../presenter/CategoryDetailsPresenter.java | 4 +- .../mvp/presenter/EnterExpensePresenter.java | 2 +- .../presenter/ExpensesDetailsPresenter.java | 2 +- .../mvp/presenter/ExpensesListPresenter.java | 2 +- .../outlay/mvp/presenter/ReportPresenter.java | 1 - .../java/com/outlay/mvp/view/AuthView.java | 11 + .../main/java/com/outlay/view/LoginForm.java | 138 ++++++++++++ .../outlay/view/{Page.java => Navigator.java} | 21 +- .../outlay/view/activity/LoginActivity.java | 56 +++++ .../view/fragment/CategoriesFragment.java | 8 +- .../fragment/CategoryDetailsFragment.java | 4 +- .../fragment/ExpensesDetailsFragment.java | 4 +- .../view/fragment/ExpensesListFragment.java | 12 +- .../outlay/view/fragment/MainFragment.java | 6 +- .../outlay/view/fragment/ReportFragment.java | 8 +- .../outlay/view/helper/AnimationUtils.java | 142 ++++++++++++ .../com/outlay/view/helper/ViewHelper.java | 17 ++ .../src/main/res/layout/activity_login.xml | 17 ++ .../app/src/main/res/layout/fragment_main.xml | 7 +- .../src/main/res/layout/view_login_form.xml | 203 ++++++++++++++++++ outlay/app/src/main/res/values/styles.xml | 4 + outlay/build.gradle | 2 +- .../adapter/CategoryDatabaseMapper.java | 4 +- .../adapter/ExpenseDatabaseMapper.java | 8 +- .../source/CategoryDatabaseSource.java | 10 +- .../source/ExpenseDatabaseSource.java | 14 +- .../src/main/java/com/outlay/data/Test.java | 8 - .../repository/ExpenseRepositoryImpl.java | 4 +- .../data/source/CategoryDataSource.java | 2 +- .../outlay/data/source/ExpenseDataSource.java | 4 +- .../domain/interactor/GetCategoryUseCase.java | 4 +- .../domain/interactor/GetExpenseUseCase.java | 4 +- .../interactor/ResetPasswordUseCase.java | 33 +++ .../domain/interactor/UserSignInUseCase.java | 34 +++ .../domain/interactor/UserSignUpUseCase.java | 34 +++ .../com/outlay/domain/model/Category.java | 6 +- .../com/outlay/domain/model/Credentials.java | 33 +++ .../java/com/outlay/domain/model/Expense.java | 6 +- .../outlay/domain/model/ExpensePeriod.java | 8 +- .../java/com/outlay/domain/model/User.java | 28 +++ .../domain/repository/CategoryRepository.java | 2 +- .../domain/repository/ExpenseRepository.java | 4 +- .../outlay/domain/repository/OutlayAuth.java | 16 ++ 64 files changed, 1831 insertions(+), 146 deletions(-) rename outlay/app/src/main/java/com/outlay/di/{ => component}/AppComponent.java (61%) create mode 100644 outlay/app/src/main/java/com/outlay/di/component/UserComponent.java create mode 100644 outlay/app/src/main/java/com/outlay/di/module/UserModule.java create mode 100644 outlay/app/src/main/java/com/outlay/di/scope/UserScope.java create mode 100644 outlay/app/src/main/java/com/outlay/firebase/CategoryFirebaseSource.java create mode 100644 outlay/app/src/main/java/com/outlay/firebase/ExpenseFirebaseSource.java create mode 100644 outlay/app/src/main/java/com/outlay/firebase/FirebaseRxWrapper.java create mode 100644 outlay/app/src/main/java/com/outlay/firebase/dto/CategoryDto.java create mode 100644 outlay/app/src/main/java/com/outlay/firebase/dto/ExpenseDto.java create mode 100644 outlay/app/src/main/java/com/outlay/firebase/dto/adapter/CategoryAdapter.java create mode 100644 outlay/app/src/main/java/com/outlay/firebase/dto/adapter/ExpenseAdapter.java create mode 100644 outlay/app/src/main/java/com/outlay/impl/AndroidLogger.java create mode 100644 outlay/app/src/main/java/com/outlay/impl/OutlayAuthImpl.java create mode 100644 outlay/app/src/main/java/com/outlay/mvp/presenter/AuthPresenter.java create mode 100644 outlay/app/src/main/java/com/outlay/mvp/view/AuthView.java create mode 100644 outlay/app/src/main/java/com/outlay/view/LoginForm.java rename outlay/app/src/main/java/com/outlay/view/{Page.java => Navigator.java} (76%) create mode 100644 outlay/app/src/main/java/com/outlay/view/activity/LoginActivity.java create mode 100644 outlay/app/src/main/java/com/outlay/view/helper/AnimationUtils.java create mode 100644 outlay/app/src/main/java/com/outlay/view/helper/ViewHelper.java create mode 100644 outlay/app/src/main/res/layout/activity_login.xml create mode 100644 outlay/app/src/main/res/layout/view_login_form.xml delete mode 100644 outlay/domain/src/main/java/com/outlay/data/Test.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/interactor/ResetPasswordUseCase.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/interactor/UserSignInUseCase.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/interactor/UserSignUpUseCase.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/model/Credentials.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/model/User.java create mode 100644 outlay/domain/src/main/java/com/outlay/domain/repository/OutlayAuth.java diff --git a/outlay/.gitignore b/outlay/.gitignore index 5f94008..3830495 100644 --- a/outlay/.gitignore +++ b/outlay/.gitignore @@ -5,3 +5,4 @@ .DS_Store /build /captures +/app/google-services.json diff --git a/outlay/app/build.gradle b/outlay/app/build.gradle index dd8868a..c380604 100644 --- a/outlay/app/build.gradle +++ b/outlay/app/build.gradle @@ -90,9 +90,15 @@ dependencies { compile(group: 'uz.shift', name: 'colorpicker', version: '0.5', ext: 'aar') compile 'com.github.PhilJay:MPAndroidChart:v2.2.0' compile 'com.rengwuxian.materialedittext:library:2.1.4' + compile 'com.github.clans:fab:1.6.4' compile('com.crashlytics.sdk.android:crashlytics:2.5.5@aar') { transitive = true; } + compile 'com.google.firebase:firebase-core:9.8.0' + compile 'com.google.firebase:firebase-auth:9.8.0' + compile 'com.google.firebase:firebase-database:9.8.0' } + +apply plugin: 'com.google.gms.google-services' diff --git a/outlay/app/src/main/AndroidManifest.xml b/outlay/app/src/main/AndroidManifest.xml index b9fb971..a089bdc 100644 --- a/outlay/app/src/main/AndroidManifest.xml +++ b/outlay/app/src/main/AndroidManifest.xml @@ -10,6 +10,14 @@ android:theme="@style/Theme.Material.Custom"> + + + + @@ -17,10 +25,6 @@ - - diff --git a/outlay/app/src/main/java/com/outlay/App.java b/outlay/app/src/main/java/com/outlay/App.java index ce22ed9..2117c61 100644 --- a/outlay/app/src/main/java/com/outlay/App.java +++ b/outlay/app/src/main/java/com/outlay/App.java @@ -3,25 +3,43 @@ import android.app.Application; import android.content.Context; -import com.crashlytics.android.Crashlytics; -import com.outlay.di.AppComponent; -import com.outlay.di.DaggerAppComponent; +import com.google.firebase.database.FirebaseDatabase; +import com.outlay.core.logger.LoggerFactory; +import com.outlay.di.component.AppComponent; +import com.outlay.di.component.DaggerAppComponent; +import com.outlay.di.component.UserComponent; import com.outlay.di.module.AppModule; import com.outlay.di.module.DaoModule; - -import io.fabric.sdk.android.Fabric; +import com.outlay.di.module.UserModule; +import com.outlay.domain.model.User; +import com.outlay.impl.AndroidLogger; /** * Created by Bogdan Melnychuk on 1/15/16. */ public class App extends Application { private AppComponent appComponent; + private UserComponent userComponent; @Override public void onCreate() { super.onCreate(); // Fabric.with(this, new Crashlytics()); initializeInjector(); + LoggerFactory.registerLogger(new AndroidLogger()); + + FirebaseDatabase.getInstance().setPersistenceEnabled(true); + } + + public UserComponent createUserComponent(User user) { + if (userComponent == null) { + userComponent = appComponent.plus(new UserModule(user)); + } + return userComponent; + } + + public void releaseUserComponent() { + userComponent = null; } private void initializeInjector() { @@ -35,7 +53,15 @@ public AppComponent getAppComponent() { return appComponent; } + public UserComponent getUserComponent() { + return userComponent; + } + public static AppComponent getComponent(Context context) { return ((App) context.getApplicationContext()).getAppComponent(); } + + public static UserComponent getUserComponent(Context context) { + return ((App) context.getApplicationContext()).getUserComponent(); + } } diff --git a/outlay/app/src/main/java/com/outlay/di/AppComponent.java b/outlay/app/src/main/java/com/outlay/di/component/AppComponent.java similarity index 61% rename from outlay/app/src/main/java/com/outlay/di/AppComponent.java rename to outlay/app/src/main/java/com/outlay/di/component/AppComponent.java index b5aea2b..f5feaa1 100644 --- a/outlay/app/src/main/java/com/outlay/di/AppComponent.java +++ b/outlay/app/src/main/java/com/outlay/di/component/AppComponent.java @@ -1,12 +1,14 @@ -package com.outlay.di; +package com.outlay.di.component; -import android.app.Application; +import android.content.Context; import com.outlay.di.module.AppModule; import com.outlay.di.module.DaoModule; +import com.outlay.di.module.UserModule; +import com.outlay.view.activity.LoginActivity; import com.outlay.view.activity.MainActivity; -import com.outlay.view.fragment.CategoryDetailsFragment; import com.outlay.view.fragment.CategoriesFragment; +import com.outlay.view.fragment.CategoryDetailsFragment; import com.outlay.view.fragment.ExpensesDetailsFragment; import com.outlay.view.fragment.ExpensesListFragment; import com.outlay.view.fragment.MainFragment; @@ -22,13 +24,11 @@ @Singleton @Component(modules = {AppModule.class, DaoModule.class}) public interface AppComponent { - void inject(MainActivity activity); - void inject(CategoriesFragment fragment); - void inject(CategoryDetailsFragment fragment); - void inject(MainFragment fragment); - void inject(ReportFragment fragment); - void inject(ExpensesListFragment fragment); - void inject(ExpensesDetailsFragment fragment); - - Application getApplication(); + UserComponent plus(UserModule userModule); + + void inject(LoginActivity loginActivity); + + Context getApplication(); + + } diff --git a/outlay/app/src/main/java/com/outlay/di/component/UserComponent.java b/outlay/app/src/main/java/com/outlay/di/component/UserComponent.java new file mode 100644 index 0000000..2682f24 --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/di/component/UserComponent.java @@ -0,0 +1,32 @@ +package com.outlay.di.component; + +import com.outlay.di.module.UserModule; +import com.outlay.di.scope.UserScope; +import com.outlay.view.fragment.CategoriesFragment; +import com.outlay.view.fragment.CategoryDetailsFragment; +import com.outlay.view.fragment.ExpensesDetailsFragment; +import com.outlay.view.fragment.ExpensesListFragment; +import com.outlay.view.fragment.MainFragment; +import com.outlay.view.fragment.ReportFragment; + +import dagger.Subcomponent; + +/** + * Created by bmelnychuk on 10/27/16. + */ + +@UserScope +@Subcomponent(modules = {UserModule.class}) +public interface UserComponent { + void inject(CategoriesFragment fragment); + + void inject(CategoryDetailsFragment fragment); + + void inject(MainFragment fragment); + + void inject(ReportFragment fragment); + + void inject(ExpensesListFragment fragment); + + void inject(ExpensesDetailsFragment fragment); +} diff --git a/outlay/app/src/main/java/com/outlay/di/module/AppModule.java b/outlay/app/src/main/java/com/outlay/di/module/AppModule.java index 15dca8d..85b5216 100644 --- a/outlay/app/src/main/java/com/outlay/di/module/AppModule.java +++ b/outlay/app/src/main/java/com/outlay/di/module/AppModule.java @@ -1,6 +1,6 @@ package com.outlay.di.module; -import android.app.Application; +import android.content.Context; import com.outlay.App; import com.outlay.core.data.AppPreferences; @@ -28,16 +28,10 @@ public AppModule(App mApplication) { @Provides @Singleton - Application provideAppContext() { + Context provideAppContext() { return mApplication; } - @Provides - @Singleton - AndroidPreferencesManager providePreferencesManager() { - return new AndroidPreferencesManager(mApplication); - } - @Provides @Singleton PostExecutionThread providePostExecutionThread(UIThread uiThread) { diff --git a/outlay/app/src/main/java/com/outlay/di/module/DaoModule.java b/outlay/app/src/main/java/com/outlay/di/module/DaoModule.java index 3983cdf..4caea0a 100644 --- a/outlay/app/src/main/java/com/outlay/di/module/DaoModule.java +++ b/outlay/app/src/main/java/com/outlay/di/module/DaoModule.java @@ -1,20 +1,16 @@ package com.outlay.di.module; -import android.app.Application; +import android.content.Context; import android.database.sqlite.SQLiteDatabase; -import com.outlay.data.source.CategoryDataSource; -import com.outlay.data.source.ExpenseDataSource; import com.outlay.database.dao.CategoryDao; import com.outlay.database.dao.DaoMaster; import com.outlay.database.dao.DaoSession; import com.outlay.database.dao.ExpenseDao; import com.outlay.database.source.CategoryDatabaseSource; -import com.outlay.database.source.ExpenseDatabaseSource; -import com.outlay.domain.repository.CategoryRepository; -import com.outlay.domain.repository.ExpenseRepository; -import com.outlay.impl.CategoryRepositoryImpl; -import com.outlay.data.repository.ExpenseRepositoryImpl; +import com.outlay.domain.repository.OutlayAuth; +import com.outlay.firebase.FirebaseRxWrapper; +import com.outlay.impl.OutlayAuthImpl; import javax.inject.Singleton; @@ -30,7 +26,7 @@ public class DaoModule { @Provides @Singleton - public DaoMaster.DevOpenHelper provideDatabaseHelper(Application application) { + public DaoMaster.DevOpenHelper provideDatabaseHelper(Context application) { return new DaoMaster.DevOpenHelper(application, DATABASE_NAME, null); } @@ -66,33 +62,18 @@ public ExpenseDao provideExpenseDaoDao(DaoSession session) { @Provides @Singleton - public ExpenseDataSource provideExpenseDataSource(ExpenseDao expenseDao) { - return new ExpenseDatabaseSource(expenseDao); + public OutlayAuth provideOutlayAuth( + FirebaseRxWrapper firebaseRxWrapper + ) { + return new OutlayAuthImpl(firebaseRxWrapper); } @Provides @Singleton - public CategoryDataSource provideCategoryDataSource( + public CategoryDatabaseSource providerLocalCategoryDataSource( CategoryDao categoryDao, ExpenseDao expenseDao ) { return new CategoryDatabaseSource(categoryDao, expenseDao); } - - @Provides - @Singleton - public CategoryRepository provideCategoryRepository( - CategoryDataSource categoryDataSource, - Application application - ) { - return new CategoryRepositoryImpl(categoryDataSource, application); - } - - @Provides - @Singleton - public ExpenseRepository provideExpenseRepository( - ExpenseDataSource expenseDataSource - ) { - return new ExpenseRepositoryImpl(expenseDataSource); - } } diff --git a/outlay/app/src/main/java/com/outlay/di/module/UserModule.java b/outlay/app/src/main/java/com/outlay/di/module/UserModule.java new file mode 100644 index 0000000..eb4aa7d --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/di/module/UserModule.java @@ -0,0 +1,78 @@ +package com.outlay.di.module; + +import android.content.Context; + +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.FirebaseDatabase; +import com.outlay.data.repository.ExpenseRepositoryImpl; +import com.outlay.database.source.CategoryDatabaseSource; +import com.outlay.di.scope.UserScope; +import com.outlay.domain.model.User; +import com.outlay.domain.repository.CategoryRepository; +import com.outlay.domain.repository.ExpenseRepository; +import com.outlay.firebase.CategoryFirebaseSource; +import com.outlay.firebase.ExpenseFirebaseSource; +import com.outlay.impl.CategoryRepositoryImpl; + +import dagger.Module; +import dagger.Provides; + +/** + * Created by bmelnychuk on 10/27/16. + */ + +@Module +public class UserModule { + private User user; + + public UserModule(User user) { + this.user = user; + } + + @Provides + @UserScope + public DatabaseReference provideDatabseRef( + ) { + return FirebaseDatabase.getInstance().getReference(); + } + + @Provides + @UserScope + public CategoryFirebaseSource provideFirebaseCategoryDataSource( + DatabaseReference databaseReference + ) { + if (user == null) { + return null; + } + return new CategoryFirebaseSource(user, databaseReference); + } + + @Provides + @UserScope + public ExpenseFirebaseSource provideExpenseFirebaseSource( + DatabaseReference databaseReference + ) { + if (user == null) { + return null; + } + return new ExpenseFirebaseSource(user, databaseReference); + } + + @Provides + @UserScope + public CategoryRepository provideCategoryRepository( + CategoryDatabaseSource databaseSource, + CategoryFirebaseSource firebaseSource, + Context application + ) { + return new CategoryRepositoryImpl(databaseSource, firebaseSource, application); + } + + @Provides + @UserScope + public ExpenseRepository provideExpenseRepository( + ExpenseFirebaseSource expenseDataSource + ) { + return new ExpenseRepositoryImpl(expenseDataSource); + } +} diff --git a/outlay/app/src/main/java/com/outlay/di/scope/UserScope.java b/outlay/app/src/main/java/com/outlay/di/scope/UserScope.java new file mode 100644 index 0000000..a649d65 --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/di/scope/UserScope.java @@ -0,0 +1,15 @@ +package com.outlay.di.scope; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +import javax.inject.Scope; + +/** + * Created by bmelnychuk on 10/27/16. + */ + +@Scope +@Retention(RetentionPolicy.RUNTIME) +public @interface UserScope { +} diff --git a/outlay/app/src/main/java/com/outlay/firebase/CategoryFirebaseSource.java b/outlay/app/src/main/java/com/outlay/firebase/CategoryFirebaseSource.java new file mode 100644 index 0000000..41f22bf --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/firebase/CategoryFirebaseSource.java @@ -0,0 +1,159 @@ +package com.outlay.firebase; + +import android.text.TextUtils; + +import com.google.android.gms.tasks.Task; +import com.google.firebase.database.DataSnapshot; +import com.google.firebase.database.DatabaseError; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.ValueEventListener; +import com.outlay.data.source.CategoryDataSource; +import com.outlay.domain.model.Category; +import com.outlay.domain.model.User; +import com.outlay.firebase.dto.CategoryDto; +import com.outlay.firebase.dto.adapter.CategoryAdapter; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import javax.inject.Inject; + +import rx.Observable; + +/** + * Created by bmelnychuk on 10/26/16. + */ + +public class CategoryFirebaseSource implements CategoryDataSource { + + private DatabaseReference mDatabase; + private CategoryAdapter adapter; + private User currentUser; + + @Inject + public CategoryFirebaseSource( + User currentUser, + DatabaseReference databaseReference + ) { + this.currentUser = currentUser; + //TODO as param? + mDatabase = databaseReference; + adapter = new CategoryAdapter(); + } + + @Override + public Observable> getAll() { + return Observable.create(subscriber -> { + mDatabase.child("users").child(currentUser.getId()).child("categories").orderByChild("order") + .addListenerForSingleValueEvent(new ValueEventListener() { + @Override + public void onDataChange(DataSnapshot dataSnapshot) { + List categories = new ArrayList<>(); + for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) { + CategoryDto categoryDto = postSnapshot.getValue(CategoryDto.class); + categories.add(adapter.toCategory(categoryDto)); + } + subscriber.onNext(categories); + subscriber.onCompleted(); + } + + @Override + public void onCancelled(DatabaseError databaseError) { + subscriber.onError(databaseError.toException()); + } + }); + }); + + + } + + @Override + public Observable getById(String id) { + return Observable.create(subscriber -> { + mDatabase.child("users").child(currentUser.getId()).child("categories").child(id) + .addListenerForSingleValueEvent(new ValueEventListener() { + @Override + public void onDataChange(DataSnapshot dataSnapshot) { + CategoryDto categoryDto = dataSnapshot.getValue(CategoryDto.class); + subscriber.onNext(adapter.toCategory(categoryDto)); + subscriber.onCompleted(); + } + + @Override + public void onCancelled(DatabaseError databaseError) { + subscriber.onError(databaseError.toException()); + } + }); + }); + } + + @Override + public Observable> saveAll(List categories) { + return Observable.create(subscriber -> { + List categoryDtos = adapter.fromCategories(categories); + Map categoryDtoMap = new HashMap<>(); + for (CategoryDto categoryDto : categoryDtos) { + categoryDtoMap.put(categoryDto.getId(), categoryDto); + } + Task task = mDatabase.child("users").child(currentUser.getId()) + .child("categories") + .setValue(categoryDtoMap); + task.addOnCompleteListener(resultTask -> { + if (task.isSuccessful()) { + subscriber.onCompleted(); + } else { + Exception e = task.getException(); + subscriber.onError(e); + } + }); + }); + } + + @Override + public Observable save(Category category) { + return Observable.create(subscriber -> { + CategoryDto categoryDto = adapter.fromCategory(category); + + String key = category.getId(); + if(TextUtils.isEmpty(key)) { + key = mDatabase.child("users").child(currentUser.getId()).child("categories").push().getKey(); + categoryDto.setId(key); + category.setId(key); + } + + Task task = mDatabase.child("users").child(currentUser.getId()) + .child("categories").child(key) + .setValue(categoryDto); + task.addOnCompleteListener(resultTask -> { + if (task.isSuccessful()) { + subscriber.onNext(category); + subscriber.onCompleted(); + } else { + Exception e = task.getException(); + subscriber.onError(e); + } + }); + }); + } + + @Override + public Observable remove(Category category) { + return Observable.create(subscriber -> { + CategoryDto categoryDto = adapter.fromCategory(category); + + Task task = mDatabase.child("users").child(currentUser.getId()) + .child("categories").child(categoryDto.getId().toString()) + .removeValue(); + task.addOnCompleteListener(resultTask -> { + if (task.isSuccessful()) { + subscriber.onCompleted(); + } else { + Exception e = task.getException(); + subscriber.onError(e); + } + }); + }); + } +} diff --git a/outlay/app/src/main/java/com/outlay/firebase/ExpenseFirebaseSource.java b/outlay/app/src/main/java/com/outlay/firebase/ExpenseFirebaseSource.java new file mode 100644 index 0000000..4c912b4 --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/firebase/ExpenseFirebaseSource.java @@ -0,0 +1,137 @@ +package com.outlay.firebase; + +import android.text.TextUtils; + +import com.google.android.gms.tasks.Task; +import com.google.firebase.database.DataSnapshot; +import com.google.firebase.database.DatabaseError; +import com.google.firebase.database.DatabaseReference; +import com.google.firebase.database.ValueEventListener; +import com.outlay.data.source.ExpenseDataSource; +import com.outlay.domain.model.Expense; +import com.outlay.domain.model.User; +import com.outlay.firebase.dto.ExpenseDto; +import com.outlay.firebase.dto.adapter.ExpenseAdapter; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import javax.inject.Inject; + +import rx.Observable; + +/** + * Created by bmelnychuk on 10/27/16. + */ + +public class ExpenseFirebaseSource implements ExpenseDataSource { + private DatabaseReference mDatabase; + private ExpenseAdapter adapter; + private User currentUser; + + @Inject + public ExpenseFirebaseSource( + User currentUser, + DatabaseReference databaseReference + ) { + this.currentUser = currentUser; + //TODO as param? + mDatabase = databaseReference; + adapter = new ExpenseAdapter(); + } + + @Override + public Observable saveExpense(Expense expense) { + return Observable.create(subscriber -> { + String key = expense.getId(); + if (TextUtils.isEmpty(key)) { + key = mDatabase.child("users").child(currentUser.getId()).child("expenses").push().getKey(); + expense.setId(key); + } + + ExpenseDto expenseDto = adapter.fromExpense(expense); + + Task task = mDatabase.child("users").child(currentUser.getId()) + .child("expenses").child(key) + .setValue(expenseDto); + task.addOnCompleteListener(resultTask -> { + if (task.isSuccessful()) { + subscriber.onNext(expense); + subscriber.onCompleted(); + } else { + Exception e = task.getException(); + subscriber.onError(e); + } + }); + }); + } + + @Override + public Observable> getExpenses(Date startDate, Date endDate, String categoryId) { + return Observable.create(subscriber -> { + mDatabase.child("users").child(currentUser.getId()).child("expenses") + .orderByChild("reportedAt") + .startAt(startDate.getTime()).endAt(endDate.getTime()) + .addListenerForSingleValueEvent(new ValueEventListener() { + @Override + public void onDataChange(DataSnapshot dataSnapshot) { + List categories = new ArrayList<>(); + for (DataSnapshot postSnapshot : dataSnapshot.getChildren()) { + ExpenseDto expenseDto = postSnapshot.getValue(ExpenseDto.class); + categories.add(adapter.toExpense(expenseDto)); + } + subscriber.onNext(categories); + subscriber.onCompleted(); + } + + @Override + public void onCancelled(DatabaseError databaseError) { + subscriber.onError(databaseError.toException()); + } + }); + }); + } + + @Override + public Observable> getExpenses(Date startDate, Date endDate) { + return getExpenses(startDate, endDate, null); + } + + @Override + public Observable getById(String expenseId) { + return Observable.create(subscriber -> { + mDatabase.child("users").child(currentUser.getId()).child("expenses").child(expenseId) + .addListenerForSingleValueEvent(new ValueEventListener() { + @Override + public void onDataChange(DataSnapshot dataSnapshot) { + ExpenseDto expenseDto = dataSnapshot.getValue(ExpenseDto.class); + subscriber.onNext(adapter.toExpense(expenseDto)); + subscriber.onCompleted(); + } + + @Override + public void onCancelled(DatabaseError databaseError) { + subscriber.onError(databaseError.toException()); + } + }); + }); + } + + @Override + public Observable remove(Expense expense) { + return Observable.create(subscriber -> { + Task task = mDatabase.child("users").child(currentUser.getId()) + .child("expenses").child(expense.getId()) + .removeValue(); + task.addOnCompleteListener(resultTask -> { + if (task.isSuccessful()) { + subscriber.onCompleted(); + } else { + Exception e = task.getException(); + subscriber.onError(e); + } + }); + }); + } +} diff --git a/outlay/app/src/main/java/com/outlay/firebase/FirebaseRxWrapper.java b/outlay/app/src/main/java/com/outlay/firebase/FirebaseRxWrapper.java new file mode 100644 index 0000000..70843c0 --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/firebase/FirebaseRxWrapper.java @@ -0,0 +1,70 @@ +package com.outlay.firebase; + +import com.google.android.gms.tasks.Task; +import com.google.firebase.auth.AuthResult; +import com.google.firebase.auth.FirebaseAuth; +import com.outlay.domain.model.User; + +import javax.inject.Inject; + +import rx.Observable; + +/** + * Created by bmelnychuk on 10/26/16. + */ + +public class FirebaseRxWrapper { + private FirebaseAuth firebaseAuth; + + @Inject + public FirebaseRxWrapper() { + //TODO shoud I provide as param + this.firebaseAuth = FirebaseAuth.getInstance(); + } + + public Observable signUp(String email, String password) { + return Observable.create(subscriber -> { + Task task = firebaseAuth.createUserWithEmailAndPassword(email, password); + task.addOnCompleteListener(resultTask -> { + if (task.isSuccessful()) { + AuthResult authResult = task.getResult(); + subscriber.onNext(authResult); + subscriber.onCompleted(); + } else { + Exception e = task.getException(); + subscriber.onError(e); + } + }); + }); + } + + public Observable signIn(String email, String password) { + return Observable.create(subscriber -> { + Task task = firebaseAuth.signInWithEmailAndPassword(email, password); + task.addOnCompleteListener(resultTask -> { + if (task.isSuccessful()) { + AuthResult authResult = task.getResult(); + subscriber.onNext(authResult); + subscriber.onCompleted(); + } else { + Exception e = task.getException(); + subscriber.onError(e); + } + }); + }); + } + + public Observable resetPassword(User user) { + return Observable.create(subscriber -> { + Task task = firebaseAuth.sendPasswordResetEmail(user.getEmail()); + task.addOnCompleteListener(resultTask -> { + if (task.isSuccessful()) { + subscriber.onCompleted(); + } else { + Exception e = task.getException(); + subscriber.onError(e); + } + }); + }); + } +} diff --git a/outlay/app/src/main/java/com/outlay/firebase/dto/CategoryDto.java b/outlay/app/src/main/java/com/outlay/firebase/dto/CategoryDto.java new file mode 100644 index 0000000..d77a707 --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/firebase/dto/CategoryDto.java @@ -0,0 +1,53 @@ +package com.outlay.firebase.dto; + +/** + * Created by bmelnychuk on 10/26/16. + */ + +public class CategoryDto { + private String id; + private String title; + private String icon; + private int order; + private int color; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } + + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } + + public int getColor() { + return color; + } + + public void setColor(int color) { + this.color = color; + } +} diff --git a/outlay/app/src/main/java/com/outlay/firebase/dto/ExpenseDto.java b/outlay/app/src/main/java/com/outlay/firebase/dto/ExpenseDto.java new file mode 100644 index 0000000..62e8969 --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/firebase/dto/ExpenseDto.java @@ -0,0 +1,53 @@ +package com.outlay.firebase.dto; + +/** + * Created by bmelnychuk on 10/27/16. + */ + +public class ExpenseDto { + private String id; + private String note; + private String amount; + private Long reportedAt; + private CategoryDto category; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getNote() { + return note; + } + + public void setNote(String note) { + this.note = note; + } + + public String getAmount() { + return amount; + } + + public void setAmount(String amount) { + this.amount = amount; + } + + public Long getReportedAt() { + return reportedAt; + } + + public void setReportedAt(Long reportedAt) { + this.reportedAt = reportedAt; + } + + public CategoryDto getCategory() { + return category; + } + + public void setCategory(CategoryDto category) { + this.category = category; + } +} diff --git a/outlay/app/src/main/java/com/outlay/firebase/dto/adapter/CategoryAdapter.java b/outlay/app/src/main/java/com/outlay/firebase/dto/adapter/CategoryAdapter.java new file mode 100644 index 0000000..d9c792f --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/firebase/dto/adapter/CategoryAdapter.java @@ -0,0 +1,50 @@ +package com.outlay.firebase.dto.adapter; + +import com.outlay.domain.model.Category; +import com.outlay.firebase.dto.CategoryDto; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by bmelnychuk on 10/26/16. + */ + +public class CategoryAdapter { + public Category toCategory(CategoryDto categoryDto) { + Category category = new Category(); + category.setColor(categoryDto.getColor()); + category.setIcon(categoryDto.getIcon()); + category.setOrder(categoryDto.getOrder()); + category.setId(categoryDto.getId()); + category.setTitle(categoryDto.getTitle()); + return category; + } + + public CategoryDto fromCategory(Category category) { + CategoryDto categoryDto = new CategoryDto(); + categoryDto.setColor(category.getColor()); + categoryDto.setIcon(category.getIcon()); + categoryDto.setOrder(category.getOrder()); + categoryDto.setId(category.getId()); + categoryDto.setTitle(category.getTitle()); + + return categoryDto; + } + + public List toCategories(List categoryDtos) { + List result = new ArrayList<>(categoryDtos.size()); + for (CategoryDto c : categoryDtos) { + result.add(toCategory(c)); + } + return result; + } + + public List fromCategories(List categoryList) { + List result = new ArrayList<>(categoryList.size()); + for (Category c : categoryList) { + result.add(fromCategory(c)); + } + return result; + } +} diff --git a/outlay/app/src/main/java/com/outlay/firebase/dto/adapter/ExpenseAdapter.java b/outlay/app/src/main/java/com/outlay/firebase/dto/adapter/ExpenseAdapter.java new file mode 100644 index 0000000..bde862b --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/firebase/dto/adapter/ExpenseAdapter.java @@ -0,0 +1,60 @@ +package com.outlay.firebase.dto.adapter; + +import com.outlay.domain.model.Expense; +import com.outlay.firebase.dto.ExpenseDto; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * Created by bmelnychuk on 10/26/16. + */ + +public class ExpenseAdapter { + private CategoryAdapter categoryAdapter; + + public ExpenseAdapter() { + this.categoryAdapter = new CategoryAdapter(); + } + + public Expense toExpense(ExpenseDto expenseDto) { + Expense expense = new Expense(); + expense.setNote(expenseDto.getNote()); + expense.setAmount(new BigDecimal(expenseDto.getAmount())); + expense.setId(expenseDto.getId()); + expense.setReportedAt(new Date(expenseDto.getReportedAt())); + + expense.setCategory(categoryAdapter.toCategory(expenseDto.getCategory())); + + return expense; + } + + public ExpenseDto fromExpense(Expense expense) { + ExpenseDto expenseDto = new ExpenseDto(); + expenseDto.setNote(expense.getNote()); + expenseDto.setAmount(expense.getAmount().toString()); + expenseDto.setId(expense.getId()); + expenseDto.setReportedAt(expense.getReportedAt().getTime()); + expenseDto.setCategory(categoryAdapter.fromCategory(expense.getCategory())); + + return expenseDto; + } + + public List toExpenses(List expenseDtos) { + List result = new ArrayList<>(expenseDtos.size()); + for (ExpenseDto c : expenseDtos) { + result.add(toExpense(c)); + } + return result; + } + + public List fromExpenses(List expenses) { + List result = new ArrayList<>(expenses.size()); + for (Expense c : expenses) { + result.add(fromExpense(c)); + } + return result; + } +} diff --git a/outlay/app/src/main/java/com/outlay/impl/AndroidLogger.java b/outlay/app/src/main/java/com/outlay/impl/AndroidLogger.java new file mode 100644 index 0000000..e19b9aa --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/impl/AndroidLogger.java @@ -0,0 +1,43 @@ +package com.outlay.impl; + +import android.util.Log; + +import com.outlay.core.logger.Logger; + +/** + * Created by bmelnychuk on 10/26/16. + */ + +public class AndroidLogger implements Logger { + private static final String TAG = "Outlay"; + + @Override + public void info(String message) { + Log.i(TAG, message); + } + + @Override + public void warn(String message) { + Log.w(TAG, message); + } + + @Override + public void warn(String message, Throwable e) { + Log.w(TAG, message, e); + } + + @Override + public void debug(String message) { + Log.d(TAG, message); + } + + @Override + public void error(String message) { + Log.e(TAG, message); + } + + @Override + public void error(String message, Throwable e) { + Log.e(TAG, message, e); + } +} diff --git a/outlay/app/src/main/java/com/outlay/impl/CategoryRepositoryImpl.java b/outlay/app/src/main/java/com/outlay/impl/CategoryRepositoryImpl.java index ef3870c..c9c6a2f 100644 --- a/outlay/app/src/main/java/com/outlay/impl/CategoryRepositoryImpl.java +++ b/outlay/app/src/main/java/com/outlay/impl/CategoryRepositoryImpl.java @@ -22,13 +22,16 @@ public class CategoryRepositoryImpl implements CategoryRepository { private Context context; private CategoryDataSource databaseSource; + private CategoryDataSource firebaseSource; @Inject public CategoryRepositoryImpl( CategoryDataSource databaseSource, + CategoryDataSource firebaseSource, Context context ) { this.databaseSource = databaseSource; + this.firebaseSource = firebaseSource; this.context = context; } @@ -60,26 +63,26 @@ public Observable> getDefault() { @Override public Observable> getAll() { - return databaseSource.getAll(); + return firebaseSource.getAll(); } @Override - public Observable getById(Long id) { - return databaseSource.getById(id); + public Observable getById(String id) { + return firebaseSource.getById(id); } @Override public Observable> saveAll(List categories) { - return databaseSource.saveAll(categories); + return firebaseSource.saveAll(categories); } @Override public Observable save(Category category) { - return databaseSource.save(category); + return firebaseSource.save(category); } @Override public Observable remove(Category category) { - return databaseSource.remove(category); + return firebaseSource.remove(category); } } diff --git a/outlay/app/src/main/java/com/outlay/impl/OutlayAuthImpl.java b/outlay/app/src/main/java/com/outlay/impl/OutlayAuthImpl.java new file mode 100644 index 0000000..cd62318 --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/impl/OutlayAuthImpl.java @@ -0,0 +1,50 @@ +package com.outlay.impl; + +import com.outlay.domain.model.Credentials; +import com.outlay.domain.model.User; +import com.outlay.domain.repository.OutlayAuth; +import com.outlay.firebase.FirebaseRxWrapper; + +import javax.inject.Inject; + +import rx.Observable; + +/** + * Created by bmelnychuk on 10/26/16. + */ + +public class OutlayAuthImpl implements OutlayAuth { + private FirebaseRxWrapper firebaseWrapper; + + @Inject + public OutlayAuthImpl(FirebaseRxWrapper firebaseWrapper) { + this.firebaseWrapper = firebaseWrapper; + } + + @Override + public Observable signIn(Credentials credentials) { + return firebaseWrapper.signIn(credentials.getEmail(), credentials.getPassword()) + .map(authResult -> { + User result = new User(); + result.setEmail(authResult.getUser().getEmail()); + result.setId(authResult.getUser().getUid()); + return result; + }); + } + + @Override + public Observable signUp(Credentials credentials) { + return firebaseWrapper.signUp(credentials.getEmail(), credentials.getPassword()) + .map(authResult -> { + User result = new User(); + result.setEmail(authResult.getUser().getEmail()); + result.setId(authResult.getUser().getUid()); + return result; + }); + } + + @Override + public Observable resetPassword(User user) { + return firebaseWrapper.resetPassword(user); + } +} diff --git a/outlay/app/src/main/java/com/outlay/mvp/presenter/AuthPresenter.java b/outlay/app/src/main/java/com/outlay/mvp/presenter/AuthPresenter.java new file mode 100644 index 0000000..5c6f3a5 --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/mvp/presenter/AuthPresenter.java @@ -0,0 +1,97 @@ +package com.outlay.mvp.presenter; + +import android.app.Activity; +import android.content.Context; + +import com.outlay.App; +import com.outlay.core.executor.DefaultSubscriber; +import com.outlay.domain.interactor.ResetPasswordUseCase; +import com.outlay.domain.interactor.UserSignInUseCase; +import com.outlay.domain.interactor.UserSignUpUseCase; +import com.outlay.domain.model.Credentials; +import com.outlay.domain.model.User; +import com.outlay.mvp.view.AuthView; +import com.outlay.view.Navigator; + +import javax.inject.Inject; + +/** + * Created by bmelnychuk on 10/26/16. + */ + +public class AuthPresenter extends MvpPresenter { + private UserSignInUseCase userSignInUseCase; + private UserSignUpUseCase userSignUpUseCase; + private ResetPasswordUseCase resetPasswordUseCase; + + @Inject + public AuthPresenter( + UserSignInUseCase userSignInUseCase, + UserSignUpUseCase userSignUpUseCase, + ResetPasswordUseCase resetPasswordUseCase + ) { + this.userSignInUseCase = userSignInUseCase; + this.userSignUpUseCase = userSignUpUseCase; + this.resetPasswordUseCase = resetPasswordUseCase; + } + + public void signIn(String email, String password, Activity context) { + Credentials credentials = new Credentials(email, password); + getView().setProgress(true); + userSignInUseCase.execute(credentials, new DefaultSubscriber() { + @Override + public void onNext(User user) { + ((App) context.getApplicationContext()).createUserComponent(user); + getView().setProgress(false); + Navigator.goToMainScreen(context); + context.finish(); + } + + @Override + public void onError(Throwable e) { + super.onError(e); + getView().setProgress(false); + getView().error(e.getLocalizedMessage()); + + } + }); + } + + public void signUp(String email, String password, Activity context) { + Credentials credentials = new Credentials(email, password); + getView().setProgress(true); + userSignUpUseCase.execute(credentials, new DefaultSubscriber() { + @Override + public void onNext(User user) { + ((App) context.getApplicationContext()).createUserComponent(user); + getView().setProgress(false); + Navigator.goToMainScreen(context); + context.finish(); + } + + @Override + public void onError(Throwable e) { + super.onError(e); + getView().setProgress(false); + getView().error(e.getLocalizedMessage()); + } + }); + } + + public void resetPassword(String email) { + User user = new User(); + user.setEmail(email); + resetPasswordUseCase.execute(user, new DefaultSubscriber() { + @Override + public void onCompleted() { + getView().info("Email with was sent"); + } + + @Override + public void onError(Throwable e) { + super.onError(e); + getView().error("Problem while resetting your password"); + } + }); + } +} diff --git a/outlay/app/src/main/java/com/outlay/mvp/presenter/CategoryDetailsPresenter.java b/outlay/app/src/main/java/com/outlay/mvp/presenter/CategoryDetailsPresenter.java index f299e14..1225f7d 100644 --- a/outlay/app/src/main/java/com/outlay/mvp/presenter/CategoryDetailsPresenter.java +++ b/outlay/app/src/main/java/com/outlay/mvp/presenter/CategoryDetailsPresenter.java @@ -28,7 +28,7 @@ public CategoryDetailsPresenter( this.getCategoryUseCase = getCategoryUseCase; } - public void loadCategory(Long id) { + public void loadCategory(String id) { getCategoryUseCase.execute(id, new DefaultSubscriber() { @Override public void onNext(Category category) { @@ -50,7 +50,7 @@ public void onNext(Category category) { public void deleteCategory(Category category) { deleteCategoryUseCase.execute(category, new DefaultSubscriber() { @Override - public void onNext(Category category) { + public void onCompleted() { getView().finish(); } }); diff --git a/outlay/app/src/main/java/com/outlay/mvp/presenter/EnterExpensePresenter.java b/outlay/app/src/main/java/com/outlay/mvp/presenter/EnterExpensePresenter.java index 5c00702..aa90590 100644 --- a/outlay/app/src/main/java/com/outlay/mvp/presenter/EnterExpensePresenter.java +++ b/outlay/app/src/main/java/com/outlay/mvp/presenter/EnterExpensePresenter.java @@ -69,7 +69,7 @@ public void onNext(Expense expense) { public void deleteExpense(Expense expense) { deleteExpenseUseCase.execute(expense, new DefaultSubscriber() { @Override - public void onNext(Expense expense) { + public void onCompleted() { loadSummary(new Date()); } }); diff --git a/outlay/app/src/main/java/com/outlay/mvp/presenter/ExpensesDetailsPresenter.java b/outlay/app/src/main/java/com/outlay/mvp/presenter/ExpensesDetailsPresenter.java index d61cbdb..8d892a7 100644 --- a/outlay/app/src/main/java/com/outlay/mvp/presenter/ExpensesDetailsPresenter.java +++ b/outlay/app/src/main/java/com/outlay/mvp/presenter/ExpensesDetailsPresenter.java @@ -35,7 +35,7 @@ public ExpensesDetailsPresenter( this.deleteExpenseUseCase = deleteExpenseUseCase; } - public void loadExpense(Long expenseId) { + public void loadExpense(String expenseId) { getExpenseUseCase.execute(expenseId, new DefaultSubscriber() { @Override public void onNext(Expense expense) { diff --git a/outlay/app/src/main/java/com/outlay/mvp/presenter/ExpensesListPresenter.java b/outlay/app/src/main/java/com/outlay/mvp/presenter/ExpensesListPresenter.java index b849371..173bd64 100644 --- a/outlay/app/src/main/java/com/outlay/mvp/presenter/ExpensesListPresenter.java +++ b/outlay/app/src/main/java/com/outlay/mvp/presenter/ExpensesListPresenter.java @@ -23,7 +23,7 @@ public ExpensesListPresenter( this.loadReportUseCase = loadReportUseCase; } - public void loadExpenses(Date dateFrom, Date dateTo, Long categoryId) { + public void loadExpenses(Date dateFrom, Date dateTo, String categoryId) { ExpensePeriod period = new ExpensePeriod(dateFrom, dateTo, categoryId); loadReportUseCase.execute(period, new DefaultSubscriber() { @Override diff --git a/outlay/app/src/main/java/com/outlay/mvp/presenter/ReportPresenter.java b/outlay/app/src/main/java/com/outlay/mvp/presenter/ReportPresenter.java index 237c988..4ec09ac 100644 --- a/outlay/app/src/main/java/com/outlay/mvp/presenter/ReportPresenter.java +++ b/outlay/app/src/main/java/com/outlay/mvp/presenter/ReportPresenter.java @@ -6,7 +6,6 @@ import com.outlay.domain.model.ExpensePeriod; import com.outlay.domain.model.Report; import com.outlay.mvp.view.StatisticView; -import com.outlay.view.Page; import com.outlay.view.fragment.ReportFragment; import java.util.ArrayList; diff --git a/outlay/app/src/main/java/com/outlay/mvp/view/AuthView.java b/outlay/app/src/main/java/com/outlay/mvp/view/AuthView.java new file mode 100644 index 0000000..6248c45 --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/mvp/view/AuthView.java @@ -0,0 +1,11 @@ +package com.outlay.mvp.view; + +/** + * Created by bmelnychuk on 10/26/16. + */ + +public interface AuthView extends MvpView { + void setProgress(boolean running); + void error(String message); + void info(String message); +} diff --git a/outlay/app/src/main/java/com/outlay/view/LoginForm.java b/outlay/app/src/main/java/com/outlay/view/LoginForm.java new file mode 100644 index 0000000..e96913d --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/view/LoginForm.java @@ -0,0 +1,138 @@ +package com.outlay.view; + +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.Point; +import android.os.Build; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.widget.Button; +import android.widget.RelativeLayout; + +import com.github.clans.fab.FloatingActionButton; +import com.outlay.R; +import com.outlay.view.helper.AnimationUtils; +import com.outlay.view.helper.ViewHelper; + +import butterknife.Bind; +import butterknife.ButterKnife; + +/** + * Created by bmelnychuk on 10/26/16. + */ + +public class LoginForm extends RelativeLayout { + @Bind(R.id.signInForm) + View signInForm; + + @Bind(R.id.signUpForm) + View signUpForm; + + @Bind(R.id.fab) + FloatingActionButton fab; + + @Bind(R.id.signUpButton) + Button signUpButton; + + @Bind(R.id.signInButton) + Button signInButton; + + @Bind(R.id.forgetPassword) + Button forgetPassword; + + private OnSubmitClickListener signInListener; + private OnSubmitClickListener signUpListener; + private OnPasswordForgetClick onPasswordForgetClick; + + public LoginForm(Context context) { + super(context); + init(null); + } + + public LoginForm(Context context, AttributeSet attrs) { + super(context, attrs); + init(attrs); + } + + public LoginForm(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(attrs); + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public LoginForm(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(attrs); + } + + private void init(AttributeSet attrs) { + LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View parent = inflater.inflate(R.layout.view_login_form, this, true); + ButterKnife.bind(this, parent); + + fab.setOnClickListener(v -> { + Point revealPoint = getViewCenter(); + if (signUpForm.getVisibility() == View.VISIBLE) { + AnimationUtils.hideWithReveal(signUpForm, revealPoint); + } else { + AnimationUtils.showWithReveal(signUpForm, revealPoint); + } + }); + + signInButton.setOnClickListener(v -> { + if (signInListener != null) { + signInListener.onSubmit("melnychuk.bogdan@gmail.com", "q1w2e3r4t5", v); + } + }); + + signUpButton.setOnClickListener(v -> { + if (signUpListener != null) { + signUpListener.onSubmit("melnychuk.bogdan@gmail.com", "q1w2e3r4t5", v); + } + }); + + forgetPassword.setOnClickListener(v -> { + if (onPasswordForgetClick != null) { + onPasswordForgetClick.onPasswordForget(); + } + }); + } + + public void setOnSignInClickListener(OnSubmitClickListener signInListener) { + this.signInListener = signInListener; + } + + public void setOnSignUpClickListener(OnSubmitClickListener signUpListener) { + this.signUpListener = signUpListener; + } + + public void setOnPasswordForgetClick(OnPasswordForgetClick onPasswordForgetClick) { + this.onPasswordForgetClick = onPasswordForgetClick; + } + + private void toggleViewVisibility(View view) { + boolean visible = view.getVisibility() == View.VISIBLE; + view.setVisibility(visible ? View.INVISIBLE : View.VISIBLE); + } + + private Point getViewCenter() { + int x = signUpForm.getRight(); + int y = signUpForm.getTop() + ViewHelper.dpToPx(56); + return new Point(x, y); + } + + public void setProgress(boolean running) { + fab.setIndeterminate(running); + fab.setClickable(!running); + signUpButton.setEnabled(!running); + } + + public interface OnSubmitClickListener { + void onSubmit(String email, String password, View src); + } + + public interface OnPasswordForgetClick { + void onPasswordForget(); + } +} diff --git a/outlay/app/src/main/java/com/outlay/view/Page.java b/outlay/app/src/main/java/com/outlay/view/Navigator.java similarity index 76% rename from outlay/app/src/main/java/com/outlay/view/Page.java rename to outlay/app/src/main/java/com/outlay/view/Navigator.java index 18ac0c6..604fbe3 100644 --- a/outlay/app/src/main/java/com/outlay/view/Page.java +++ b/outlay/app/src/main/java/com/outlay/view/Navigator.java @@ -2,9 +2,11 @@ import android.app.Activity; import android.app.Fragment; +import android.content.Intent; import android.os.Bundle; import com.outlay.R; +import com.outlay.view.activity.MainActivity; import com.outlay.view.activity.SingleFragmentActivity; import com.outlay.view.fragment.CategoryDetailsFragment; import com.outlay.view.fragment.ExpensesDetailsFragment; @@ -16,25 +18,30 @@ /** * Created by Bogdan Melnychuk on 1/24/16. */ -public final class Page { - public static void goToCategoryDetails(Activity activityFrom, Long categoryId) { +public final class Navigator { + public static void goToCategoryDetails(Activity activityFrom, String categoryId) { Bundle b = new Bundle(); if (categoryId != null) { - b.putLong(CategoryDetailsFragment.ARG_CATEGORY_PARAM, categoryId); + b.putString(CategoryDetailsFragment.ARG_CATEGORY_PARAM, categoryId); } changeFragment(activityFrom, CategoryDetailsFragment.class, b); } + public static void goToMainScreen(Activity activityFrom) { + Intent intent = new Intent(activityFrom, MainActivity.class); + activityFrom.startActivity(intent); + } + public static void goToReport(Activity activityFrom, Date date) { Bundle b = new Bundle(); b.putLong(ReportFragment.ARG_DATE, date.getTime()); SingleFragmentActivity.start(activityFrom, ReportFragment.class, b); } - public static void goToExpensesList(Activity activityFrom, Date dateFrom, Date dateTo, Long categoryId) { + public static void goToExpensesList(Activity activityFrom, Date dateFrom, Date dateTo, String categoryId) { Bundle b = new Bundle(); if(categoryId != null) { - b.putLong(ExpensesListFragment.ARG_CATEGORY_ID, categoryId); + b.putString(ExpensesListFragment.ARG_CATEGORY_ID, categoryId); } if (dateFrom != null) { b.putLong(ExpensesListFragment.ARG_DATE_FROM, dateFrom.getTime()); @@ -45,10 +52,10 @@ public static void goToExpensesList(Activity activityFrom, Date dateFrom, Date d changeFragment(activityFrom, ExpensesListFragment.class, b); } - public static void goToExpenseDetails(Activity activityFrom, Long expenseId) { + public static void goToExpenseDetails(Activity activityFrom, String expenseId) { Bundle b = new Bundle(); if(expenseId != null) { - b.putLong(ExpensesDetailsFragment.ARG_EXPENSE_ID, expenseId); + b.putString(ExpensesDetailsFragment.ARG_EXPENSE_ID, expenseId); } changeFragment(activityFrom, ExpensesDetailsFragment.class, b); } diff --git a/outlay/app/src/main/java/com/outlay/view/activity/LoginActivity.java b/outlay/app/src/main/java/com/outlay/view/activity/LoginActivity.java new file mode 100644 index 0000000..f42e4ac --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/view/activity/LoginActivity.java @@ -0,0 +1,56 @@ +package com.outlay.view.activity; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.widget.Toast; + +import com.outlay.App; +import com.outlay.R; +import com.outlay.mvp.presenter.AuthPresenter; +import com.outlay.mvp.view.AuthView; +import com.outlay.view.LoginForm; +import com.outlay.view.Navigator; + +import javax.inject.Inject; + +import butterknife.Bind; +import butterknife.ButterKnife; + +public class LoginActivity extends AppCompatActivity implements AuthView { + @Inject + AuthPresenter presenter; + + @Bind(R.id.loginForm) + LoginForm loginForm; + + @Override + protected void onCreate(Bundle savedInstanceState) { + App.getComponent(this).inject(this); + presenter.attachView(this); + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_login); + ButterKnife.bind(this); + + loginForm.setOnSignUpClickListener((email, password, src) -> presenter.signUp(email, password, this)); + loginForm.setOnSignInClickListener((email, password, src) -> { + presenter.signIn(email, password, this); + }); + loginForm.setOnPasswordForgetClick(() -> presenter.resetPassword("melnychuk.bogdan@gmail.com")); + } + + + @Override + public void setProgress(boolean running) { + loginForm.setProgress(running); + } + + @Override + public void error(String message) { + Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); + } + + @Override + public void info(String message) { + Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); + } +} diff --git a/outlay/app/src/main/java/com/outlay/view/fragment/CategoriesFragment.java b/outlay/app/src/main/java/com/outlay/view/fragment/CategoriesFragment.java index f2b4566..e44e729 100644 --- a/outlay/app/src/main/java/com/outlay/view/fragment/CategoriesFragment.java +++ b/outlay/app/src/main/java/com/outlay/view/fragment/CategoriesFragment.java @@ -18,7 +18,7 @@ import com.outlay.mvp.presenter.CategoriesPresenter; import com.outlay.mvp.view.CategoriesView; import com.outlay.utils.ResourceUtils; -import com.outlay.view.Page; +import com.outlay.view.Navigator; import com.outlay.view.helper.itemtouch.OnDragListener; import com.outlay.view.helper.itemtouch.SimpleItemTouchHelperCallback; @@ -52,7 +52,7 @@ public class CategoriesFragment extends BaseFragment implements OnDragListener, @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - App.getComponent(getActivity()).inject(this); + App.getUserComponent(getActivity()).inject(this); presenter.attachView(this); } @@ -76,7 +76,7 @@ public void onViewCreated(View view, Bundle savedInstanceState) { adapter = new CategoriesDraggableGridAdapter(); adapter.setDragListener(this); - adapter.setOnCategoryClickListener(c -> Page.goToCategoryDetails(getActivity(), c.getId())); + adapter.setOnCategoryClickListener(c -> Navigator.goToCategoryDetails(getActivity(), c.getId())); categoriesGrid.setAdapter(adapter); ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(adapter); @@ -84,7 +84,7 @@ public void onViewCreated(View view, Bundle savedInstanceState) { mItemTouchHelper.attachToRecyclerView(categoriesGrid); fab.setImageDrawable(ResourceUtils.getMaterialToolbarIcon(getActivity(), R.string.ic_material_add)); - fab.setOnClickListener(v -> Page.goToCategoryDetails(getActivity(), null)); + fab.setOnClickListener(v -> Navigator.goToCategoryDetails(getActivity(), null)); } @Override diff --git a/outlay/app/src/main/java/com/outlay/view/fragment/CategoryDetailsFragment.java b/outlay/app/src/main/java/com/outlay/view/fragment/CategoryDetailsFragment.java index a69d312..99a687f 100644 --- a/outlay/app/src/main/java/com/outlay/view/fragment/CategoryDetailsFragment.java +++ b/outlay/app/src/main/java/com/outlay/view/fragment/CategoryDetailsFragment.java @@ -64,7 +64,7 @@ public class CategoryDetailsFragment extends BaseFragment implements CategoryDet @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - App.getComponent(getActivity()).inject(this); + App.getUserComponent(getActivity()).inject(this); presenter.attachView(this); } @@ -117,7 +117,7 @@ public void onViewCreated(View view, Bundle savedInstanceState) { colorPicker.setOnColorChangedListener(i -> adapter.setActiveColor(i)); if (getArguments().containsKey(ARG_CATEGORY_PARAM)) { - Long categoryId = getArguments().getLong(ARG_CATEGORY_PARAM); + String categoryId = getArguments().getString(ARG_CATEGORY_PARAM); getActivity().setTitle(getString(R.string.caption_edit_category)); presenter.loadCategory(categoryId); } else { diff --git a/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesDetailsFragment.java b/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesDetailsFragment.java index 7a3742c..9f5dbbf 100644 --- a/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesDetailsFragment.java +++ b/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesDetailsFragment.java @@ -78,7 +78,7 @@ public class ExpensesDetailsFragment extends BaseFragment implements ExpenseDeta @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - App.getComponent(getActivity()).inject(this); + App.getUserComponent(getActivity()).inject(this); presenter.attachView(this); defaultDate = new Date(getArguments().getLong(ARG_DATE, new Date().getTime())); } @@ -141,7 +141,7 @@ public void onViewCreated(View view, Bundle savedInstanceState) { dateEdit.setOnClickListener(v -> showDatePickerDialog()); if (getArguments().containsKey(ARG_EXPENSE_ID)) { - Long expenseId = getArguments().getLong(ARG_EXPENSE_ID); + String expenseId = getArguments().getString(ARG_EXPENSE_ID); getActivity().setTitle(getString(R.string.caption_edit_expense)); presenter.loadExpense(expenseId); } else { diff --git a/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesListFragment.java b/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesListFragment.java index a6e7e31..7a03aa6 100644 --- a/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesListFragment.java +++ b/outlay/app/src/main/java/com/outlay/view/fragment/ExpensesListFragment.java @@ -23,7 +23,7 @@ import com.outlay.mvp.view.ExpensesView; import com.outlay.utils.IconUtils; import com.outlay.utils.ResourceUtils; -import com.outlay.view.Page; +import com.outlay.view.Navigator; import java.util.Date; import java.util.List; @@ -69,12 +69,12 @@ public class ExpensesListFragment extends BaseFragment implements ExpensesView { private ExpenseAdapter adapter; private Date dateFrom; private Date dateTo; - private Long categoryId; + private String categoryId; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - App.getComponent(getActivity()).inject(this); + App.getUserComponent(getActivity()).inject(this); presenter.attachView(this); long dateFromMillis = getArguments().getLong(ARG_DATE_FROM); @@ -83,7 +83,7 @@ public void onCreate(Bundle savedInstanceState) { dateFrom = new Date(dateFromMillis); dateTo = new Date(dateToMillis); if (getArguments().containsKey(ARG_CATEGORY_ID)) { - categoryId = getArguments().getLong(ARG_CATEGORY_ID); + categoryId = getArguments().getString(ARG_CATEGORY_ID); } } @@ -105,10 +105,10 @@ public void onViewCreated(View view, Bundle savedInstanceState) { StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL); recyclerView.setLayoutManager(staggeredGridLayoutManager); adapter = new ExpenseAdapter(); - adapter.setOnExpenseClickListener(expense -> Page.goToExpenseDetails(getActivity(), expense.getId())); + adapter.setOnExpenseClickListener(expense -> Navigator.goToExpenseDetails(getActivity(), expense.getId())); recyclerView.setAdapter(adapter); fab.setImageDrawable(ResourceUtils.getMaterialToolbarIcon(getActivity(), R.string.ic_material_add)); - fab.setOnClickListener(v -> Page.goToExpenseDetails(getActivity(), null)); + fab.setOnClickListener(v -> Navigator.goToExpenseDetails(getActivity(), null)); filterDateLabel.setText(getDateLabel()); } diff --git a/outlay/app/src/main/java/com/outlay/view/fragment/MainFragment.java b/outlay/app/src/main/java/com/outlay/view/fragment/MainFragment.java index 0d2f232..815e5c9 100644 --- a/outlay/app/src/main/java/com/outlay/view/fragment/MainFragment.java +++ b/outlay/app/src/main/java/com/outlay/view/fragment/MainFragment.java @@ -29,7 +29,7 @@ import com.outlay.utils.DeviceUtils; import com.outlay.utils.FormatUtils; import com.outlay.utils.ResourceUtils; -import com.outlay.view.Page; +import com.outlay.view.Navigator; import com.outlay.view.activity.BaseActivity; import com.outlay.view.adapter.CategoriesGridAdapter; import com.outlay.view.alert.Alert; @@ -98,7 +98,7 @@ public void onInvalidInput(String value) { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - App.getComponent(getActivity()).inject(this); + App.getUserComponent(getActivity()).inject(this); presenter.attachView(this); } @@ -194,7 +194,7 @@ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.action_summary: - Page.goToReport(getActivity(), selectedDate); + Navigator.goToReport(getActivity(), selectedDate); break; } return super.onOptionsItemSelected(item); diff --git a/outlay/app/src/main/java/com/outlay/view/fragment/ReportFragment.java b/outlay/app/src/main/java/com/outlay/view/fragment/ReportFragment.java index 3e9bcdd..3d42e09 100644 --- a/outlay/app/src/main/java/com/outlay/view/fragment/ReportFragment.java +++ b/outlay/app/src/main/java/com/outlay/view/fragment/ReportFragment.java @@ -22,7 +22,7 @@ import com.outlay.mvp.presenter.ReportPresenter; import com.outlay.mvp.view.StatisticView; import com.outlay.utils.ResourceUtils; -import com.outlay.view.Page; +import com.outlay.view.Navigator; import com.outlay.view.dialog.DatePickerFragment; import java.util.Calendar; @@ -66,7 +66,7 @@ public class ReportFragment extends BaseFragment implements StatisticView { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - App.getComponent(getActivity()).inject(this); + App.getUserComponent(getActivity()).inject(this); presenter.attachView(this); selectedDate = new Date(getArguments().getLong(ARG_DATE, new Date().getTime())); } @@ -173,7 +173,7 @@ public void goToExpensesList(Date date, int selectedPeriod) { this.goToExpensesList(date, selectedPeriod, null); } - public void goToExpensesList(Date date, int selectedPeriod, Long category) { + public void goToExpensesList(Date date, int selectedPeriod, String category) { date = DateUtils.fillCurrentTime(date); Date startDate = date; Date endDate = date; @@ -192,6 +192,6 @@ public void goToExpensesList(Date date, int selectedPeriod, Long category) { endDate = DateUtils.getMonthEnd(date); break; } - Page.goToExpensesList(getActivity(), startDate, endDate, category); + Navigator.goToExpensesList(getActivity(), startDate, endDate, category); } } \ No newline at end of file diff --git a/outlay/app/src/main/java/com/outlay/view/helper/AnimationUtils.java b/outlay/app/src/main/java/com/outlay/view/helper/AnimationUtils.java new file mode 100644 index 0000000..2a3bb9c --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/view/helper/AnimationUtils.java @@ -0,0 +1,142 @@ +package com.outlay.view.helper; + +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; +import android.annotation.TargetApi; +import android.graphics.Point; +import android.os.Build; +import android.view.View; +import android.view.ViewAnimationUtils; +import android.view.ViewGroup; +import android.view.animation.AccelerateDecelerateInterpolator; + +/** + * Created by bogdan.melnychuk on 09.12.2014. + */ +public final class AnimationUtils { + public static void animateBounceIn(final View target) { + AnimatorSet set = new AnimatorSet(); + set.playTogether( + ObjectAnimator.ofFloat(target, "alpha", 0, 1, 1, 1), + ObjectAnimator.ofFloat(target, "scaleX", 0.3f, 1.05f, 0.9f, 1), + ObjectAnimator.ofFloat(target, "scaleY", 0.3f, 1.05f, 0.9f, 1) + ); + set.setDuration(500); + target.setVisibility(View.VISIBLE); + set.start(); + } + + public static void animatePulse(final View target) { + AnimatorSet set = new AnimatorSet(); + set.playTogether( + ObjectAnimator.ofFloat(target, "scaleY", 1, 1.1f, 1), + ObjectAnimator.ofFloat(target, "scaleX", 1, 1.1f, 1) + ); + set.setDuration(1000); + set.start(); + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public static void showWithReveal(View view) { + int x = (view.getRight() - view.getLeft()) / 2; + int y = view.getBottom(); + showWithReveal(view, new Point(x, y)); + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public static void showWithReveal(View view, Point point) { + view.setVisibility(View.VISIBLE); + + // get the final radius for the clipping circle + int endRadius = (int) Math.hypot(view.getWidth(), view.getHeight()); + + Animator animator = + ViewAnimationUtils.createCircularReveal(view, point.x, point.y, 0, endRadius); + animator.setInterpolator(new AccelerateDecelerateInterpolator()); + animator.setDuration(500); + animator.start(); + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public static void hideWithReveal(View view) { + int cx = (view.getRight() - view.getLeft()) / 2; + int cy = view.getBottom(); + hideWithReveal(view, new Point(cx, cy)); + + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public static void hideWithReveal(View view, Point point) { + // get the initial radius for the clipping circle + int initialRadius = (int) Math.hypot(view.getWidth(), view.getHeight()); + + // create the animation (the final radius is zero) + Animator animator = ViewAnimationUtils.createCircularReveal(view, point.x, point.y, initialRadius, 0); + + // make the view invisible when the animation is done + animator.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + super.onAnimationEnd(animation); + view.setVisibility(View.INVISIBLE); + } + }); + + animator.setInterpolator(new AccelerateDecelerateInterpolator()); + animator.setDuration(500); + animator.start(); + } + + private static ValueAnimator slideAnimator(int start, int end, final View summary) { + ValueAnimator animator = ValueAnimator.ofInt(start, end); + animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { + @Override + public void onAnimationUpdate(ValueAnimator valueAnimator) { + //Update Height + int value = (Integer) valueAnimator.getAnimatedValue(); + + ViewGroup.LayoutParams layoutParams = summary.getLayoutParams(); + layoutParams.height = value; + summary.setLayoutParams(layoutParams); + } + }); + return animator; + } + + public static void expand(View summary) { + //set Visible + summary.setVisibility(View.VISIBLE); + final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); + summary.measure(widthSpec, 300); + ValueAnimator mAnimator = slideAnimator(0, 300, summary); + mAnimator.start(); + } + + public static void collapse(final View summary) { + int finalHeight = summary.getHeight(); + ValueAnimator mAnimator = slideAnimator(finalHeight, 0, summary); + mAnimator.addListener(new Animator.AnimatorListener() { + @Override + public void onAnimationEnd(Animator animator) { + //Height=0, but it set visibility to GONE + summary.setVisibility(View.GONE); + } + + @Override + public void onAnimationStart(Animator animator) { + } + + @Override + public void onAnimationCancel(Animator animator) { + } + + @Override + public void onAnimationRepeat(Animator animator) { + } + }); + mAnimator.start(); + } +} diff --git a/outlay/app/src/main/java/com/outlay/view/helper/ViewHelper.java b/outlay/app/src/main/java/com/outlay/view/helper/ViewHelper.java new file mode 100644 index 0000000..b998163 --- /dev/null +++ b/outlay/app/src/main/java/com/outlay/view/helper/ViewHelper.java @@ -0,0 +1,17 @@ +package com.outlay.view.helper; + +import android.content.res.Resources; + +/** + * Created by bmelnychuk on 10/26/16. + */ + +public class ViewHelper { + public static int dpToPx(int dp) { + return Math.round(dp * Resources.getSystem().getDisplayMetrics().density); + } + + public static int pxToDp(int px) { + return Math.round(px / Resources.getSystem().getDisplayMetrics().density); + } +} diff --git a/outlay/app/src/main/res/layout/activity_login.xml b/outlay/app/src/main/res/layout/activity_login.xml new file mode 100644 index 0000000..0170b97 --- /dev/null +++ b/outlay/app/src/main/res/layout/activity_login.xml @@ -0,0 +1,17 @@ + + + + + diff --git a/outlay/app/src/main/res/layout/fragment_main.xml b/outlay/app/src/main/res/layout/fragment_main.xml index 081604b..e45a452 100644 --- a/outlay/app/src/main/res/layout/fragment_main.xml +++ b/outlay/app/src/main/res/layout/fragment_main.xml @@ -80,11 +80,14 @@ android:layout_marginRight="@dimen/default_height" android:layout_marginTop="@dimen/spacing_small" android:background="@android:color/transparent" - android:editable="false" android:fontFamily="sans-serif-light" android:gravity="center" + android:clickable="false" + android:cursorVisible="false" + android:focusable="false" + android:focusableInTouchMode="false" android:hint="0.0" - android:singleLine="true" + android:lines="1" android:textColor="@color/text_primary" android:textColorHint="@color/text_disabled" android:textSize="@dimen/text_amount" /> diff --git a/outlay/app/src/main/res/layout/view_login_form.xml b/outlay/app/src/main/res/layout/view_login_form.xml new file mode 100644 index 0000000..8fc2d64 --- /dev/null +++ b/outlay/app/src/main/res/layout/view_login_form.xml @@ -0,0 +1,203 @@ + + + + + + + + + + + + + + + + + + + + + + + + +