├── .github └── workflows │ └── main.yml ├── .gitignore ├── LICENCE.txt ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── ajitsingh │ │ └── com │ │ └── expensemanager │ │ ├── activity │ │ └── MainActivityTest.java │ │ ├── database │ │ └── ExpenseDatabaseHelperTest.java │ │ └── rule │ │ └── DatabaseResetRule.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── ajitsingh │ │ │ └── com │ │ │ └── expensemanager │ │ │ ├── activity │ │ │ ├── AddCategoryActivity.java │ │ │ ├── CurrentMonthExpenseFragment.java │ │ │ ├── CurrentWeekExpenseFragment.java │ │ │ ├── ExpenseFragment.java │ │ │ ├── MainActivity.java │ │ │ └── TodaysExpenseFragment.java │ │ │ ├── adapter │ │ │ ├── CurrentWeeksExpenseAdapter.java │ │ │ ├── DrawerListViewAdapter.java │ │ │ ├── HomeViewPagerAdapter.java │ │ │ └── TodaysExpenseListViewAdapter.java │ │ │ ├── database │ │ │ └── ExpenseDatabaseHelper.java │ │ │ ├── model │ │ │ ├── Expense.java │ │ │ └── ExpenseType.java │ │ │ ├── notification │ │ │ └── FillExpenseNotificationScheduler.java │ │ │ ├── presenter │ │ │ ├── CategoryPresenter.java │ │ │ ├── CurrentMonthExpensePresenter.java │ │ │ ├── CurrentWeekExpensePresenter.java │ │ │ ├── ExpensePresenter.java │ │ │ ├── NavigationDrawerPresenter.java │ │ │ └── TodaysExpensePresenter.java │ │ │ ├── receiver │ │ │ └── FillExpenseNotificationReceiver.java │ │ │ ├── table │ │ │ ├── ExpenseTable.java │ │ │ └── ExpenseTypeTable.java │ │ │ ├── utils │ │ │ ├── DateUtil.java │ │ │ └── ExpenseCollection.java │ │ │ └── view │ │ │ ├── AddCategoryView.java │ │ │ ├── CurrentMonthExpenseView.java │ │ │ ├── CurrentWeekExpenseView.java │ │ │ ├── ExpenseView.java │ │ │ ├── NavigationDrawerItemView.java │ │ │ └── TodaysExpenseView.java │ └── res │ │ ├── drawable │ │ ├── background_color.xml │ │ ├── button_primary.xml │ │ ├── button_secondary.xml │ │ └── header_background_color.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── current_week_expenses.xml │ │ ├── drawer_list_item.xml │ │ ├── expense_graph.xml │ │ ├── expense_header_text_box.xml │ │ ├── expense_text_box.xml │ │ ├── new_category.xml │ │ ├── new_expense.xml │ │ └── todays_expenses.xml │ │ ├── menu │ │ └── menu_main.xml │ │ ├── mipmap-hdpi │ │ ├── drawer_shadow.9.png │ │ ├── ic_launcher.png │ │ ├── ic_menu_closed.png │ │ └── ic_menu_opened.png │ │ ├── mipmap-mdpi │ │ ├── drawer_shadow.9.png │ │ ├── ic_launcher.png │ │ ├── ic_menu_closed.png │ │ └── ic_menu_opened.png │ │ ├── mipmap-xhdpi │ │ ├── drawer_shadow.9.png │ │ ├── ic_launcher.png │ │ ├── ic_menu_closed.png │ │ └── ic_menu_opened.png │ │ ├── mipmap-xxhdpi │ │ ├── drawer_shadow.9.png │ │ ├── ic_launcher.png │ │ ├── ic_menu_closed.png │ │ └── ic_menu_opened.png │ │ ├── values-v21 │ │ └── styles.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── arrays.xml │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── ajitsingh │ └── com │ └── expensemanager │ ├── presenter │ ├── CategoryPresenterTest.java │ ├── CurrentMonthExpensePresenterTest.java │ ├── CurrentWeekExpensePresenterTest.java │ ├── ExpensePresenterTest.java │ ├── NavigationDrawerPresenterTest.java │ └── TodaysExpensePresenterTest.java │ └── utils │ ├── DateUtilTest.java │ └── ExpenseCollectionTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── pre_commit.sh └── settings.gradle /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | push: 4 | branches: [ master ] 5 | pull_request: 6 | branches: [ master ] 7 | workflow_dispatch: 8 | 9 | jobs: 10 | lint: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Checkout the code 14 | uses: actions/checkout@v2 15 | 16 | - name: Upload html test report 17 | uses: actions/upload-artifact@v2 18 | with: 19 | name: lint.html 20 | path: app/build/reports/lint-results-debug.html 21 | 22 | unit-test: 23 | needs: [lint] 24 | runs-on: ubuntu-latest 25 | steps: 26 | - name: Checkout the code 27 | uses: actions/checkout@v2 28 | 29 | - name: Run tests 30 | run: ./gradlew test 31 | 32 | - name: Upload test report 33 | uses: actions/upload-artifact@v2 34 | with: 35 | name: unit_test_report 36 | path: app/build/reports/tests/testDebugUnitTest/ 37 | 38 | instrumentation-test: 39 | needs: [unit-test] 40 | runs-on: macos-latest 41 | steps: 42 | - name: Checkout the code 43 | uses: actions/checkout@v2 44 | 45 | - name: Run espresso tests 46 | uses: reactivecircus/android-emulator-runner@v2 47 | with: 48 | api-level: 29 49 | script: ./gradlew connectedCheck 50 | 51 | - name: Upload test report 52 | uses: actions/upload-artifact@v2 53 | with: 54 | name: instrumentation_test_report 55 | path: app/build/reports/androidTests/connected/ 56 | 57 | static-code-analysis: 58 | needs: [instrumentation-test] 59 | runs-on: ubuntu-latest 60 | steps: 61 | - name: Checkout the code 62 | uses: actions/checkout@v2 63 | 64 | - name: set up JDK 11 65 | uses: actions/setup-java@v1 66 | with: 67 | java-version: 11 68 | 69 | - name: SonarCloud Scan 70 | run: ./gradlew app:sonarqube -Dsonar.login=${{ secrets.SONAR_TOKEN }} 71 | env: 72 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 73 | 74 | package: 75 | needs: [static-code-analysis] 76 | name: Generate APK 77 | runs-on: ubuntu-latest 78 | steps: 79 | - name: Checkout the code 80 | uses: actions/checkout@v2 81 | 82 | - name: set up JDK 1.8 83 | uses: actions/setup-java@v1 84 | with: 85 | java-version: 1.8 86 | 87 | - name: Build debug APK 88 | run: ./gradlew assembleDebug --stacktrace 89 | 90 | - name: Upload APK 91 | uses: actions/upload-artifact@v2 92 | with: 93 | name: expense-manager.apk 94 | path: app/build/outputs/apk/debug/app-debug.apk 95 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .gradle 2 | /local.properties 3 | /.idea 4 | .DS_Store 5 | /build 6 | /captures 7 | *.iml 8 | *.jks 9 | *.apk 10 | -------------------------------------------------------------------------------- /LICENCE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Ajit Singh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ExpenseManager 2 | 3 | [![Maintenance](https://img.shields.io/badge/Maintained%3F-no-red.svg)](https://GitHub.com/ajitsing/ExpenseManager/graphs/commit-activity) 4 | [![Twitter Follow](https://img.shields.io/twitter/follow/Ajit5ingh.svg?style=social)](https://twitter.com/Ajit5ingh) 5 | [![HitCount](http://hits.dwyl.io/ajitsing/ExpenseManager.svg)](http://hits.dwyl.io/ajitsing/ExpenseManager) 6 | 7 | Android app to manage expenses 8 | 9 | #### Download the latest app from the release tab 10 | 11 | ## Video series on instrumentation testing 12 | [![](https://github.com/ajitsing/ScreenShots/blob/master/expense_manager/instrumentation_testing_series.png)](https://www.youtube.com/watch?v=gdsxVfq-yNM&list=PLFYf87MeyEq588ibGPTu5lEhnJZG6KsmR&index=1) 13 | 14 | ## CI/CD for Android app using Github Actions 15 | [![](https://github.com/ajitsing/ScreenShots/blob/master/expense_manager/github_actions_series.png)](https://www.youtube.com/watch?v=2mCJZHEhsxc&list=PLFYf87MeyEq5Os9Vsnd9k9kgUHzy8eff_) 16 | 17 | #### Few Screen Shots Of The App 18 | 19 | ![Alt text](https://github.com/ajitsing/ScreenShots/blob/master/expense_manager/em_new_expense.png) 20 | 21 | ![Alt text](https://github.com/ajitsing/ScreenShots/blob/master/expense_manager/em_today.png) 22 | 23 | ![Alt text](https://github.com/ajitsing/ScreenShots/blob/master/expense_manager/em_navigation.png) 24 | 25 | ![Alt text](https://github.com/ajitsing/ScreenShots/blob/master/expense_manager/em_week.png) 26 | 27 | ![Alt text](https://github.com/ajitsing/ScreenShots/blob/master/expense_manager/em_month_graph.png) 28 | 29 | 30 | #### Licence 31 | 32 | The MIT License (MIT) 33 | 34 | Copyright (c) 2016 Ajit Singh 35 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: "org.sonarqube" 3 | 4 | android { 5 | compileSdkVersion 29 6 | buildToolsVersion "29.0.3" 7 | 8 | defaultConfig { 9 | applicationId "ajitsingh.com.expensemanager" 10 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 11 | minSdkVersion 24 12 | targetSdkVersion 29 13 | versionCode 1 14 | versionName "1.1" 15 | 16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 17 | } 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | 25 | packagingOptions { 26 | exclude 'LICENSE.txt' 27 | } 28 | } 29 | 30 | dependencies { 31 | compile fileTree(dir: 'libs', include: ['*.jar']) 32 | implementation 'androidx.appcompat:appcompat:1.2.0' 33 | implementation 'org.quanqi:android-holo-graph:0.1.0' 34 | implementation 'joda-time:joda-time:2.8.2' 35 | 36 | testImplementation 'junit:junit:4.12' 37 | testImplementation 'org.mockito:mockito-core:1.10.19' 38 | testImplementation 'joda-time:joda-time:2.8.2' 39 | 40 | testImplementation 'junit:junit:4.12' 41 | androidTestImplementation 'androidx.test.ext:junit:1.1.2' 42 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' 43 | androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.3.0' 44 | androidTestImplementation 'androidx.test.espresso:espresso-intents:3.3.0' 45 | } 46 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/ajitsingh/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/ajitsingh/com/expensemanager/activity/MainActivityTest.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.activity; 2 | 3 | import androidx.test.ext.junit.rules.ActivityScenarioRule; 4 | import androidx.test.ext.junit.runners.AndroidJUnit4; 5 | 6 | import org.junit.Rule; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import ajitsingh.com.expensemanager.R; 11 | 12 | import static androidx.test.espresso.Espresso.onView; 13 | import static androidx.test.espresso.assertion.ViewAssertions.matches; 14 | import static androidx.test.espresso.matcher.ViewMatchers.withId; 15 | import static androidx.test.espresso.matcher.ViewMatchers.withText; 16 | 17 | @RunWith(AndroidJUnit4.class) 18 | public class MainActivityTest { 19 | @Rule 20 | public ActivityScenarioRule activityTestRule = new ActivityScenarioRule(MainActivity.class); 21 | 22 | @Test 23 | public void shouldDisplayExpenseButton() throws Exception { 24 | onView(withId(R.id.add_expense)).check(matches(withText("Add Expense"))); 25 | } 26 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/ajitsingh/com/expensemanager/database/ExpenseDatabaseHelperTest.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.database; 2 | 3 | import android.content.Context; 4 | 5 | import androidx.test.ext.junit.runners.AndroidJUnit4; 6 | import androidx.test.platform.app.InstrumentationRegistry; 7 | 8 | import org.joda.time.DateTime; 9 | import org.joda.time.DateTimeUtils; 10 | import org.junit.After; 11 | import org.junit.Before; 12 | import org.junit.Test; 13 | import org.junit.runner.RunWith; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | import ajitsingh.com.expensemanager.model.Expense; 19 | import ajitsingh.com.expensemanager.model.ExpenseType; 20 | import ajitsingh.com.expensemanager.table.ExpenseTable; 21 | import ajitsingh.com.expensemanager.table.ExpenseTypeTable; 22 | 23 | import static org.hamcrest.core.Is.is; 24 | import static org.hamcrest.core.IsCollectionContaining.hasItems; 25 | import static org.junit.Assert.assertThat; 26 | import static org.junit.Assert.assertTrue; 27 | 28 | @RunWith(AndroidJUnit4.class) 29 | public class ExpenseDatabaseHelperTest { 30 | 31 | private ExpenseDatabaseHelper database; 32 | 33 | @Before 34 | public void setUp() throws Exception { 35 | getTargetContext().deleteDatabase(ExpenseDatabaseHelper.EXPENSE_DB); 36 | database = new ExpenseDatabaseHelper(getTargetContext()); 37 | database.truncate(ExpenseTypeTable.TABLE_NAME); 38 | database.truncate(ExpenseTable.TABLE_NAME); 39 | 40 | freezeDate("2015-10-02"); 41 | } 42 | 43 | @After 44 | public void tearDown() throws Exception { 45 | database.close(); 46 | DateTimeUtils.setCurrentMillisSystem(); 47 | } 48 | 49 | @Test 50 | public void shouldAddExpenseType() throws Exception { 51 | database.addExpenseType(new ExpenseType("Food")); 52 | 53 | List expenseTypes = database.getExpenseTypes(); 54 | assertThat(expenseTypes.size(), is(1)); 55 | assertTrue(expenseTypes.get(0).equals("Food")); 56 | } 57 | 58 | @Test 59 | public void shouldReturnCurrentMonthsExpenses() throws Exception { 60 | database.addExpense(new Expense(100l, "Food", "31-09-2015")); 61 | database.addExpense(new Expense(200l, "Food", "02-10-2015")); 62 | 63 | List expenses = database.getExpensesForCurrentMonthGroupByCategory(); 64 | 65 | assertThat(expenses.size(), is(1)); 66 | assertThat(expenses.get(0).getAmount(), is(200l)); 67 | } 68 | 69 | @Test 70 | public void shouldReturnCurrentMonthsExpensesForMultipleExpenseTypes() throws Exception { 71 | database.addExpense(new Expense(100l, "Food", "31-09-2015")); 72 | database.addExpense(new Expense(200l, "Food", "02-10-2015")); 73 | 74 | database.addExpense(new Expense(300l, "Travel", "10-10-2015")); 75 | database.addExpense(new Expense(200l, "Travel", "02-10-2015")); 76 | 77 | database.addExpense(new Expense(500l, "Movie", "02-09-2015")); 78 | 79 | List expenses = database.getExpensesForCurrentMonthGroupByCategory(); 80 | 81 | assertThat(expenses.size(), is(2)); 82 | } 83 | 84 | @Test 85 | public void shouldReturnTodaysExpenses() throws Exception { 86 | database.addExpense(new Expense(200l, "Food", "02-10-2015")); 87 | 88 | List expenses = database.getTodaysExpenses(); 89 | 90 | assertThat(expenses.size(), is(1)); 91 | assertThat(expenses.get(0).getAmount(), is(200l)); 92 | assertThat(expenses.get(0).getType(), is("Food")); 93 | assertThat(expenses.get(0).getDate(), is("02-10-2015")); 94 | } 95 | 96 | @Test 97 | public void shouldReturnTodaysExpensesForMultipleExpenseTypes() throws Exception { 98 | database.addExpense(new Expense(200l, "Food", "02-10-2015")); 99 | database.addExpense(new Expense(300l, "Travel", "02-10-2015")); 100 | 101 | List expenses = database.getTodaysExpenses(); 102 | 103 | assertThat(expenses.size(), is(2)); 104 | } 105 | 106 | @Test 107 | public void shouldReturnCurrentWeeksExpenses() throws Exception { 108 | database.addExpense(new Expense(200l, "Food", "02-10-2015")); 109 | database.addExpense(new Expense(100l, "Food", "02-10-2015")); 110 | database.addExpense(new Expense(300l, "Travel", "03-10-2015")); 111 | database.addExpense(new Expense(400l, "Movie", "04-10-2015")); 112 | 113 | List expenses = database.getCurrentWeeksExpenses(); 114 | ArrayList expenseTypes = new ArrayList<>(); 115 | for (Expense expense : expenses) { 116 | expenseTypes.add(expense.getType()); 117 | } 118 | 119 | assertThat(expenses.size(), is(3)); 120 | assertThat(expenseTypes, hasItems("Food", "Travel", "Movie")); 121 | } 122 | 123 | private void freezeDate(String date) { 124 | DateTimeUtils.setCurrentMillisFixed(new DateTime(date).getMillis()); 125 | } 126 | 127 | private Context getTargetContext() { 128 | return InstrumentationRegistry.getInstrumentation().getTargetContext(); 129 | } 130 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/ajitsingh/com/expensemanager/rule/DatabaseResetRule.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.rule; 2 | 3 | import org.junit.rules.TestRule; 4 | import org.junit.runner.Description; 5 | import org.junit.runners.model.Statement; 6 | 7 | import java.util.List; 8 | 9 | import ajitsingh.com.expensemanager.database.ExpenseDatabaseHelper; 10 | import ajitsingh.com.expensemanager.model.ExpenseType; 11 | import ajitsingh.com.expensemanager.table.ExpenseTypeTable; 12 | 13 | import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 14 | 15 | public class DatabaseResetRule implements TestRule { 16 | 17 | @Override 18 | public Statement apply(final Statement base, Description description) { 19 | return new Statement() { 20 | @Override 21 | public void evaluate() throws Throwable { 22 | try { 23 | clearDatabase(); 24 | seedData(); 25 | base.evaluate(); 26 | } finally { 27 | clearDatabase(); 28 | } 29 | } 30 | }; 31 | } 32 | 33 | private void clearDatabase() { 34 | final ExpenseDatabaseHelper database = new ExpenseDatabaseHelper(getInstrumentation().getTargetContext()); 35 | database.deleteAll(); 36 | database.close(); 37 | } 38 | 39 | private void seedData() { 40 | final ExpenseDatabaseHelper database = new ExpenseDatabaseHelper(getInstrumentation().getTargetContext()); 41 | List expenseTypes = ExpenseTypeTable.seedData(); 42 | for (ExpenseType expenseType : expenseTypes) { 43 | database.addExpenseType(expenseType); 44 | } 45 | database.close(); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/activity/AddCategoryActivity.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.activity; 2 | 3 | import android.os.Bundle; 4 | import android.view.View; 5 | import android.widget.TextView; 6 | import android.widget.Toast; 7 | 8 | import androidx.fragment.app.FragmentActivity; 9 | 10 | import ajitsingh.com.expensemanager.R; 11 | import ajitsingh.com.expensemanager.database.ExpenseDatabaseHelper; 12 | import ajitsingh.com.expensemanager.presenter.CategoryPresenter; 13 | import ajitsingh.com.expensemanager.view.AddCategoryView; 14 | 15 | import static ajitsingh.com.expensemanager.activity.MainActivity.ADD_NEW_CAT; 16 | 17 | 18 | public class AddCategoryActivity extends FragmentActivity implements AddCategoryView { 19 | 20 | @Override 21 | protected void onCreate(Bundle savedInstanceState){ 22 | super.onCreate(savedInstanceState); 23 | setContentView(R.layout.new_category); 24 | } 25 | 26 | public void addCategory(View view) { 27 | ExpenseDatabaseHelper expenseDatabaseHelper = new ExpenseDatabaseHelper(this); 28 | CategoryPresenter categoryPresenter = new CategoryPresenter(this, expenseDatabaseHelper); 29 | 30 | if(categoryPresenter.addCategory()) 31 | Toast.makeText(this, getString(R.string.add_category_success), Toast.LENGTH_LONG).show(); 32 | 33 | expenseDatabaseHelper.close(); 34 | finishActivity(ADD_NEW_CAT); 35 | } 36 | 37 | @Override 38 | public String getCategory() { 39 | TextView categoryInput = (TextView) findViewById(R.id.category); 40 | return categoryInput.getText().toString(); 41 | } 42 | 43 | @Override 44 | public void displayError() { 45 | TextView view = (TextView) this.findViewById(R.id.category); 46 | view.setError(this.getString(R.string.category_empty_error)); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/activity/CurrentMonthExpenseFragment.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.activity; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.TextView; 8 | 9 | import androidx.fragment.app.Fragment; 10 | 11 | import com.echo.holographlibrary.Bar; 12 | import com.echo.holographlibrary.BarGraph; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | import ajitsingh.com.expensemanager.R; 18 | import ajitsingh.com.expensemanager.database.ExpenseDatabaseHelper; 19 | import ajitsingh.com.expensemanager.presenter.CurrentMonthExpensePresenter; 20 | import ajitsingh.com.expensemanager.view.CurrentMonthExpenseView; 21 | 22 | public class CurrentMonthExpenseFragment extends Fragment implements CurrentMonthExpenseView { 23 | 24 | @Override 25 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 26 | return inflater.inflate(R.layout.expense_graph, container, false); 27 | } 28 | 29 | @Override 30 | public void onActivityCreated(Bundle savedInstanceState) { 31 | super.onActivityCreated(savedInstanceState); 32 | 33 | ExpenseDatabaseHelper expenseDatabaseHelper = new ExpenseDatabaseHelper(getActivity()); 34 | CurrentMonthExpensePresenter presenter = new CurrentMonthExpensePresenter(this, expenseDatabaseHelper); 35 | 36 | presenter.plotGraph(); 37 | presenter.showTotalExpense(); 38 | expenseDatabaseHelper.close(); 39 | } 40 | 41 | @Override 42 | public void displayGraph(List points) { 43 | BarGraph graph = (BarGraph)getActivity().findViewById(R.id.graph); 44 | graph.setBars((ArrayList) points); 45 | } 46 | 47 | @Override 48 | public void displayTotalExpense(Long totalExpense) { 49 | TextView totalExpenseTextBox = (TextView) getActivity().findViewById(R.id.current_months_total_expense); 50 | totalExpenseTextBox.setText(getString(R.string.total_expense) + " " + getString(R.string.rupee_sym) + totalExpense); 51 | } 52 | 53 | @Override 54 | public int getGraphColor() { 55 | return getActivity().getResources().getColor(R.color.light_blue); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/activity/CurrentWeekExpenseFragment.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.activity; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.ExpandableListView; 8 | import android.widget.TextView; 9 | 10 | import androidx.fragment.app.Fragment; 11 | 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | import ajitsingh.com.expensemanager.R; 16 | import ajitsingh.com.expensemanager.adapter.CurrentWeeksExpenseAdapter; 17 | import ajitsingh.com.expensemanager.database.ExpenseDatabaseHelper; 18 | import ajitsingh.com.expensemanager.model.Expense; 19 | import ajitsingh.com.expensemanager.presenter.CurrentWeekExpensePresenter; 20 | import ajitsingh.com.expensemanager.view.CurrentWeekExpenseView; 21 | 22 | public class CurrentWeekExpenseFragment extends Fragment implements CurrentWeekExpenseView { 23 | 24 | @Override 25 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 26 | return inflater.inflate(R.layout.current_week_expenses, container, false); 27 | } 28 | 29 | @Override 30 | public void onActivityCreated(Bundle savedInstanceState) { 31 | super.onActivityCreated(savedInstanceState); 32 | 33 | ExpenseDatabaseHelper expenseDatabaseHelper = new ExpenseDatabaseHelper(getActivity()); 34 | CurrentWeekExpensePresenter presenter = new CurrentWeekExpensePresenter(expenseDatabaseHelper, this); 35 | presenter.renderTotalExpenses(); 36 | presenter.renderCurrentWeeksExpenses(); 37 | expenseDatabaseHelper.close(); 38 | } 39 | 40 | @Override 41 | public void displayCurrentWeeksExpenses(Map> expensesByDate) { 42 | ExpandableListView listView = (ExpandableListView) getActivity().findViewById(R.id.current_week_expenses_list); 43 | listView.setAdapter(new CurrentWeeksExpenseAdapter(getActivity(), expensesByDate)); 44 | } 45 | 46 | @Override 47 | public void displayTotalExpenses(Long totalExpense) { 48 | TextView totalExpenseTextBox = (TextView) getActivity().findViewById(R.id.current_week_expense); 49 | totalExpenseTextBox.setText(getString(R.string.total_expense) + " " + getString(R.string.rupee_sym) + totalExpense); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/activity/ExpenseFragment.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.activity; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.ArrayAdapter; 8 | import android.widget.Button; 9 | import android.widget.Spinner; 10 | import android.widget.TextView; 11 | import android.widget.Toast; 12 | 13 | import androidx.fragment.app.Fragment; 14 | 15 | import java.util.List; 16 | 17 | import ajitsingh.com.expensemanager.R; 18 | import ajitsingh.com.expensemanager.database.ExpenseDatabaseHelper; 19 | import ajitsingh.com.expensemanager.presenter.ExpensePresenter; 20 | import ajitsingh.com.expensemanager.view.ExpenseView; 21 | 22 | public class ExpenseFragment extends Fragment implements ExpenseView, View.OnClickListener { 23 | 24 | @Override 25 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 26 | return inflater.inflate(R.layout.new_expense, container, false); 27 | } 28 | 29 | @Override 30 | public void onActivityCreated(Bundle savedInstanceState) { 31 | super.onActivityCreated(savedInstanceState); 32 | 33 | ExpenseDatabaseHelper expenseDatabaseHelper = new ExpenseDatabaseHelper(this.getActivity()); 34 | ExpensePresenter expensePresenter = new ExpensePresenter(expenseDatabaseHelper, this); 35 | expensePresenter.setExpenseTypes(); 36 | expenseDatabaseHelper.close(); 37 | 38 | Button addExpenseButton = (Button) getActivity().findViewById(R.id.add_expense); 39 | addExpenseButton.setOnClickListener(this); 40 | } 41 | 42 | @Override 43 | public String getAmount() { 44 | TextView view = (TextView) getActivity().findViewById(R.id.amount); 45 | return view.getText().toString(); 46 | } 47 | 48 | @Override 49 | public String getType() { 50 | Spinner spinner = (Spinner) getActivity().findViewById(R.id.expense_type); 51 | return (String) spinner.getSelectedItem(); 52 | } 53 | 54 | @Override 55 | public void renderExpenseTypes(List expenseTypes) { 56 | Spinner spinner = (Spinner) getActivity().findViewById(R.id.expense_type); 57 | ArrayAdapter adapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_spinner_dropdown_item, expenseTypes); 58 | spinner.setAdapter(adapter); 59 | } 60 | 61 | @Override 62 | public void displayError() { 63 | TextView view = (TextView) getActivity().findViewById(R.id.amount); 64 | view.setError(getActivity().getString(R.string.amount_empty_error)); 65 | } 66 | 67 | @Override 68 | public void onClick(View view) { 69 | ExpenseDatabaseHelper expenseDatabaseHelper = new ExpenseDatabaseHelper(this.getActivity()); 70 | ExpensePresenter expensePresenter = new ExpensePresenter(expenseDatabaseHelper, this); 71 | if(expensePresenter.addExpense()){ 72 | MainActivity activity = (MainActivity) getActivity(); 73 | Toast.makeText(activity, R.string.expense_add_successfully, Toast.LENGTH_LONG).show(); 74 | activity.onExpenseAdded(); 75 | } 76 | expenseDatabaseHelper.close(); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.activity; 2 | 3 | import android.app.ActionBar; 4 | import android.app.FragmentTransaction; 5 | import android.content.Intent; 6 | import android.content.res.Configuration; 7 | import android.os.Bundle; 8 | import android.view.Menu; 9 | import android.view.MenuItem; 10 | import android.view.View; 11 | import android.widget.AdapterView; 12 | import android.widget.FrameLayout; 13 | import android.widget.ListView; 14 | 15 | import androidx.appcompat.app.ActionBarDrawerToggle; 16 | import androidx.core.view.GravityCompat; 17 | import androidx.drawerlayout.widget.DrawerLayout; 18 | import androidx.fragment.app.Fragment; 19 | import androidx.fragment.app.FragmentActivity; 20 | import androidx.fragment.app.FragmentManager; 21 | import androidx.viewpager.widget.ViewPager; 22 | 23 | import ajitsingh.com.expensemanager.R; 24 | import ajitsingh.com.expensemanager.adapter.DrawerListViewAdapter; 25 | import ajitsingh.com.expensemanager.adapter.HomeViewPagerAdapter; 26 | import ajitsingh.com.expensemanager.notification.FillExpenseNotificationScheduler; 27 | import ajitsingh.com.expensemanager.presenter.NavigationDrawerPresenter; 28 | import ajitsingh.com.expensemanager.view.NavigationDrawerItemView; 29 | 30 | 31 | public class MainActivity extends FragmentActivity implements NavigationDrawerItemView, ActionBar.TabListener { 32 | 33 | private ActionBar actionBar; 34 | private ViewPager viewPager; 35 | private ActionBarDrawerToggle actionBarDrawerToggle; 36 | private DrawerLayout drawerLayout; 37 | public static final int ADD_NEW_CAT = 9991; 38 | private static Boolean isNotificationScheduled = false; 39 | private HomeViewPagerAdapter homeViewPagerAdapter; 40 | 41 | @Override 42 | protected void onCreate(Bundle savedInstanceState) { 43 | super.onCreate(savedInstanceState); 44 | setContentView(R.layout.activity_main); 45 | 46 | configureDrawer(); 47 | configureActionBar(); 48 | 49 | if (!isNotificationScheduled) scheduleReminder(); 50 | } 51 | 52 | @Override 53 | public void render(Fragment fragment) { 54 | FragmentManager fragmentManager = getSupportFragmentManager(); 55 | Fragment existingFragment = fragmentManager.findFragmentById(R.id.main_frame); 56 | 57 | if(existingFragment != null && existingFragment.getClass() == fragment.getClass()) return; 58 | 59 | fragmentManager.beginTransaction() 60 | .addToBackStack(MainActivity.class.getSimpleName()) 61 | .replace(R.id.main_frame, fragment, fragment.getClass().getSimpleName()) 62 | .commit(); 63 | 64 | actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD); 65 | } 66 | 67 | @Override 68 | public void goToHome() { 69 | Intent intent = new Intent(this, MainActivity.class); 70 | startActivity(intent); 71 | } 72 | 73 | @Override 74 | public void onBackPressed() { 75 | super.onBackPressed(); 76 | int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount(); 77 | if (backStackEntryCount == 0) 78 | actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 79 | actionBar.setTitle(R.string.app_name); 80 | } 81 | 82 | @Override 83 | public boolean onCreateOptionsMenu(Menu menu) { 84 | getMenuInflater().inflate(R.menu.menu_main, menu); 85 | return true; 86 | } 87 | 88 | @Override 89 | public void onConfigurationChanged(Configuration newConfig) { 90 | super.onConfigurationChanged(newConfig); 91 | actionBarDrawerToggle.onConfigurationChanged(newConfig); 92 | } 93 | 94 | @Override 95 | protected void onPostCreate(Bundle savedInstanceState) { 96 | super.onPostCreate(savedInstanceState); 97 | actionBarDrawerToggle.syncState(); 98 | } 99 | 100 | @Override 101 | public boolean onOptionsItemSelected(MenuItem item) { 102 | int id = item.getItemId(); 103 | 104 | if (id == R.id.action_add_category) { 105 | Intent intent = new Intent(this, AddCategoryActivity.class); 106 | startActivityForResult(intent, ADD_NEW_CAT); 107 | return true; 108 | } 109 | 110 | return actionBarDrawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item); 111 | } 112 | 113 | @Override 114 | protected void onActivityResult(int requestCode, int resultCode, Intent data) { 115 | super.onActivityResult(requestCode, resultCode, data); 116 | if (requestCode == ADD_NEW_CAT) { 117 | viewPager.setAdapter(new HomeViewPagerAdapter(getSupportFragmentManager())); 118 | viewPager.setCurrentItem(0); 119 | } 120 | } 121 | 122 | public void onExpenseAdded() { 123 | viewPager.setAdapter(homeViewPagerAdapter); 124 | actionBar.setSelectedNavigationItem(1); 125 | } 126 | 127 | @Override 128 | public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { 129 | viewPager.setCurrentItem(tab.getPosition()); 130 | } 131 | 132 | @Override 133 | public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { 134 | 135 | } 136 | 137 | @Override 138 | public void onTabReselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) { 139 | 140 | } 141 | 142 | private void configureDrawer() { 143 | drawerLayout = (DrawerLayout) findViewById(R.id.drawer); 144 | 145 | actionBarDrawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.app_name, R.string.action_settings) { 146 | @Override 147 | public void onDrawerOpened(View drawerView) { 148 | super.onDrawerOpened(drawerView); 149 | drawerView.bringToFront(); 150 | } 151 | }; 152 | drawerLayout.setDrawerListener(actionBarDrawerToggle); 153 | drawerLayout.setDrawerShadow(R.mipmap.drawer_shadow, GravityCompat.START); 154 | getActionBar().setHomeButtonEnabled(true); 155 | getActionBar().setDisplayHomeAsUpEnabled(true); 156 | 157 | ListView drawerList = (ListView) findViewById(R.id.drawer_list); 158 | drawerList.setAdapter(new DrawerListViewAdapter(this)); 159 | 160 | onDrawerItemSelected(); 161 | } 162 | 163 | private void onDrawerItemSelected() { 164 | final ListView drawerList = (ListView) findViewById(R.id.drawer_list); 165 | final NavigationDrawerPresenter navigationDrawerPresenter = new NavigationDrawerPresenter(this); 166 | drawerList.setOnItemClickListener(new AdapterView.OnItemClickListener() { 167 | @Override 168 | public void onItemClick(AdapterView parent, View view, int position, long id) { 169 | String[] drawerItems = getResources().getStringArray(R.array.drawer_items); 170 | drawerList.setItemChecked(position, true); 171 | getActionBar().setTitle(drawerItems[position]); 172 | drawerLayout.closeDrawer(GravityCompat.START); 173 | drawerList.bringToFront(); 174 | navigationDrawerPresenter.onItemSelected(drawerItems[position]); 175 | FrameLayout mainFrame = (FrameLayout) findViewById(R.id.main_frame); 176 | mainFrame.bringToFront(); 177 | } 178 | }); 179 | } 180 | 181 | private void configureActionBar() { 182 | actionBar = getActionBar(); 183 | actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 184 | 185 | viewPager = (ViewPager) findViewById(R.id.view_pager); 186 | homeViewPagerAdapter = new HomeViewPagerAdapter(getSupportFragmentManager()); 187 | viewPager.setAdapter(homeViewPagerAdapter); 188 | 189 | addTabs(); 190 | 191 | viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { 192 | @Override 193 | public void onPageScrolled(int i, float v, int i2) { 194 | } 195 | 196 | @Override 197 | public void onPageSelected(int i) { 198 | actionBar.setSelectedNavigationItem(i); 199 | } 200 | 201 | @Override 202 | public void onPageScrollStateChanged(int i) { 203 | } 204 | }); 205 | } 206 | 207 | private void addTabs() { 208 | ActionBar.Tab addNewExpenseTab = actionBar.newTab(); 209 | addNewExpenseTab.setTabListener(this); 210 | addNewExpenseTab.setText("Add New"); 211 | actionBar.addTab(addNewExpenseTab); 212 | 213 | ActionBar.Tab todayTab = actionBar.newTab(); 214 | todayTab.setTabListener(this); 215 | todayTab.setText("Today"); 216 | actionBar.addTab(todayTab); 217 | } 218 | 219 | private void scheduleReminder() { 220 | new FillExpenseNotificationScheduler().schedule(this); 221 | isNotificationScheduled = true; 222 | } 223 | } 224 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/activity/TodaysExpenseFragment.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.activity; 2 | 3 | import android.os.Bundle; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.ListView; 8 | import android.widget.TextView; 9 | 10 | import androidx.fragment.app.Fragment; 11 | 12 | import java.util.List; 13 | 14 | import ajitsingh.com.expensemanager.R; 15 | import ajitsingh.com.expensemanager.adapter.TodaysExpenseListViewAdapter; 16 | import ajitsingh.com.expensemanager.database.ExpenseDatabaseHelper; 17 | import ajitsingh.com.expensemanager.model.Expense; 18 | import ajitsingh.com.expensemanager.presenter.TodaysExpensePresenter; 19 | import ajitsingh.com.expensemanager.view.TodaysExpenseView; 20 | 21 | public class TodaysExpenseFragment extends Fragment implements TodaysExpenseView { 22 | 23 | @Override 24 | public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 25 | return inflater.inflate(R.layout.todays_expenses, container, false); 26 | } 27 | 28 | @Override 29 | public void onActivityCreated(Bundle savedInstanceState) { 30 | super.onActivityCreated(savedInstanceState); 31 | ExpenseDatabaseHelper expenseDatabaseHelper = new ExpenseDatabaseHelper(this.getActivity()); 32 | TodaysExpensePresenter todaysExpensePresenter = new TodaysExpensePresenter(this, expenseDatabaseHelper); 33 | 34 | todaysExpensePresenter.renderTodaysExpenses(); 35 | todaysExpensePresenter.renderTotalExpense(); 36 | expenseDatabaseHelper.close(); 37 | } 38 | 39 | @Override 40 | public void displayTotalExpense(Long totalExpense) { 41 | TextView totalExpenseTextBox = (TextView) getActivity().findViewById(R.id.total_expense); 42 | totalExpenseTextBox.setText(getActivity().getString(R.string.total_expense) + " " + getActivity().getString(R.string.rupee_sym) + totalExpense.toString()); 43 | } 44 | 45 | @Override 46 | public void displayTodaysExpenses(List expenses) { 47 | ListView listView = (ListView) getActivity().findViewById(R.id.todays_expenses_list); 48 | listView.setAdapter(new TodaysExpenseListViewAdapter(expenses, getActivity(), android.R.layout.simple_list_item_1)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/adapter/CurrentWeeksExpenseAdapter.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.adapter; 2 | 3 | import android.content.Context; 4 | import android.database.DataSetObserver; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.ExpandableListAdapter; 9 | import android.widget.TextView; 10 | 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | import ajitsingh.com.expensemanager.R; 15 | import ajitsingh.com.expensemanager.model.Expense; 16 | import ajitsingh.com.expensemanager.utils.ExpenseCollection; 17 | 18 | import static ajitsingh.com.expensemanager.utils.DateUtil.getDayName; 19 | 20 | public class CurrentWeeksExpenseAdapter implements ExpandableListAdapter { 21 | private Context context; 22 | private final Map> expenses; 23 | 24 | public CurrentWeeksExpenseAdapter(Context context, Map> expenses) { 25 | this.context = context; 26 | this.expenses = expenses; 27 | } 28 | 29 | @Override 30 | public void registerDataSetObserver(DataSetObserver dataSetObserver) { 31 | 32 | } 33 | 34 | @Override 35 | public void unregisterDataSetObserver(DataSetObserver dataSetObserver) { 36 | 37 | } 38 | 39 | @Override 40 | public int getGroupCount() { 41 | return expenses.keySet().size(); 42 | } 43 | 44 | @Override 45 | public int getChildrenCount(int position) { 46 | return expenses.get(expenses.keySet().toArray()[position]).size(); 47 | } 48 | 49 | @Override 50 | public Object getGroup(int position) { 51 | String date = (String) this.expenses.keySet().toArray()[position]; 52 | Long totalExpense = new ExpenseCollection(this.expenses.get(date)).getTotalExpense(); 53 | 54 | return date + " (" + getDayName(date) + ") - " + context.getString(R.string.rupee_sym) + totalExpense; 55 | } 56 | 57 | @Override 58 | public Object getChild(int parent, int child) { 59 | Expense expense = expenses.get(expenses.keySet().toArray()[parent]).get(child); 60 | return expense.getType() + " - " + expense.getAmount(); 61 | } 62 | 63 | @Override 64 | public long getGroupId(int position) { 65 | return position; 66 | } 67 | 68 | @Override 69 | public long getChildId(int parent, int child) { 70 | return child; 71 | } 72 | 73 | @Override 74 | public boolean hasStableIds() { 75 | return false; 76 | } 77 | 78 | @Override 79 | public View getGroupView(int parent, boolean isExpanded, View converterView, ViewGroup viewGroup) { 80 | String parentText = (String) getGroup(parent); 81 | 82 | if(converterView == null){ 83 | LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 84 | converterView = inflater.inflate(R.layout.expense_header_text_box, viewGroup, false); 85 | } 86 | 87 | TextView textBox = (TextView) converterView.findViewById(R.id.expense_header_text_box); 88 | textBox.setText(parentText); 89 | 90 | return converterView; 91 | } 92 | 93 | @Override 94 | public View getChildView(int parent, int child, boolean lastChild, View converterView, ViewGroup viewGroup) { 95 | String childText = (String) getChild(parent, child); 96 | 97 | if(converterView == null){ 98 | LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 99 | converterView = inflater.inflate(R.layout.expense_text_box, viewGroup, false); 100 | } 101 | 102 | TextView textBox = (TextView) converterView.findViewById(R.id.expense_text_box); 103 | textBox.setText(childText); 104 | 105 | return converterView; 106 | } 107 | 108 | @Override 109 | public boolean isChildSelectable(int i, int i1) { 110 | return false; 111 | } 112 | 113 | @Override 114 | public boolean areAllItemsEnabled() { 115 | return false; 116 | } 117 | 118 | @Override 119 | public boolean isEmpty() { 120 | return false; 121 | } 122 | 123 | @Override 124 | public void onGroupExpanded(int i) { 125 | 126 | } 127 | 128 | @Override 129 | public void onGroupCollapsed(int i) { 130 | 131 | } 132 | 133 | @Override 134 | public long getCombinedChildId(long l, long l1) { 135 | return 0; 136 | } 137 | 138 | @Override 139 | public long getCombinedGroupId(long l) { 140 | return 0; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/adapter/DrawerListViewAdapter.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.adapter; 2 | 3 | import android.content.Context; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.BaseAdapter; 8 | import android.widget.TextView; 9 | 10 | import ajitsingh.com.expensemanager.R; 11 | 12 | public class DrawerListViewAdapter extends BaseAdapter{ 13 | 14 | private final String[] drawerItems; 15 | private Context context; 16 | 17 | public DrawerListViewAdapter(Context context) { 18 | this.context = context; 19 | drawerItems = context.getResources().getStringArray(R.array.drawer_items); 20 | } 21 | 22 | @Override 23 | public int getCount() { 24 | return drawerItems.length; 25 | } 26 | 27 | @Override 28 | public String getItem(int position) { 29 | return drawerItems[position]; 30 | } 31 | 32 | @Override 33 | public long getItemId(int position) { 34 | return position; 35 | } 36 | 37 | @Override 38 | public View getView(int position, View convertView, ViewGroup viewGroup) { 39 | if(convertView==null) { 40 | LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 41 | convertView = inflater.inflate(R.layout.drawer_list_item, null); 42 | } 43 | 44 | TextView item = (TextView) convertView.findViewById(R.id.drawer_list_item); 45 | item.setText(drawerItems[position]); 46 | 47 | return convertView; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/adapter/HomeViewPagerAdapter.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.adapter; 2 | 3 | import android.view.View; 4 | 5 | import androidx.fragment.app.Fragment; 6 | import androidx.fragment.app.FragmentManager; 7 | import androidx.fragment.app.FragmentStatePagerAdapter; 8 | 9 | import ajitsingh.com.expensemanager.activity.ExpenseFragment; 10 | import ajitsingh.com.expensemanager.activity.TodaysExpenseFragment; 11 | 12 | public class HomeViewPagerAdapter extends FragmentStatePagerAdapter { 13 | public HomeViewPagerAdapter(FragmentManager supportFragmentManager) { 14 | super(supportFragmentManager); 15 | } 16 | 17 | @Override 18 | public Fragment getItem(int position) { 19 | switch (position){ 20 | case 0: 21 | return new ExpenseFragment(); 22 | case 1: 23 | return new TodaysExpenseFragment(); 24 | } 25 | 26 | return null; 27 | } 28 | 29 | @Override 30 | public int getCount() { 31 | return 2; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/adapter/TodaysExpenseListViewAdapter.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.adapter; 2 | 3 | import android.content.Context; 4 | import android.widget.ArrayAdapter; 5 | 6 | import java.util.List; 7 | 8 | import ajitsingh.com.expensemanager.model.Expense; 9 | 10 | public class TodaysExpenseListViewAdapter extends ArrayAdapter{ 11 | private final List expenses; 12 | 13 | public TodaysExpenseListViewAdapter(List expenses, Context context, int resource) { 14 | super(context, resource); 15 | this.expenses = expenses; 16 | } 17 | 18 | @Override 19 | public int getCount() { 20 | return expenses.size(); 21 | } 22 | 23 | @Override 24 | public Object getItem(int position) { 25 | Expense expense = expenses.get(position); 26 | return expense.getType() + " - " + expense.getAmount(); 27 | } 28 | 29 | @Override 30 | public long getItemId(int position) { 31 | return expenses.get(position).getId(); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/database/ExpenseDatabaseHelper.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.database; 2 | 3 | import android.content.ContentValues; 4 | import android.content.Context; 5 | import android.database.Cursor; 6 | import android.database.sqlite.SQLiteDatabase; 7 | import android.database.sqlite.SQLiteOpenHelper; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | import ajitsingh.com.expensemanager.model.Expense; 13 | import ajitsingh.com.expensemanager.model.ExpenseType; 14 | import ajitsingh.com.expensemanager.table.ExpenseTable; 15 | import ajitsingh.com.expensemanager.table.ExpenseTypeTable; 16 | import ajitsingh.com.expensemanager.utils.DateUtil; 17 | 18 | import static ajitsingh.com.expensemanager.utils.DateUtil.getCurrentDate; 19 | import static ajitsingh.com.expensemanager.utils.DateUtil.getCurrentWeeksDates; 20 | import static java.lang.Integer.parseInt; 21 | import static java.lang.Long.parseLong; 22 | 23 | public class ExpenseDatabaseHelper extends SQLiteOpenHelper { 24 | public static final String EXPENSE_DB = "expense"; 25 | 26 | public ExpenseDatabaseHelper(Context context) { 27 | super(context, EXPENSE_DB, null, 1); 28 | } 29 | 30 | @Override 31 | public void onCreate(SQLiteDatabase sqLiteDatabase) { 32 | sqLiteDatabase.execSQL(ExpenseTable.CREATE_TABLE_QUERY); 33 | sqLiteDatabase.execSQL(ExpenseTypeTable.CREATE_TABLE_QUERY); 34 | seedExpenseTypes(sqLiteDatabase); 35 | } 36 | 37 | @Override 38 | public void onUpgrade(SQLiteDatabase sqLiteDatabase, int oldVersion, int newVersion) { 39 | 40 | } 41 | 42 | public List getExpenseTypes() { 43 | ArrayList expenseTypes = new ArrayList<>(); 44 | 45 | SQLiteDatabase database = this.getWritableDatabase(); 46 | Cursor cursor = database.rawQuery(ExpenseTypeTable.SELECT_ALL, null); 47 | 48 | if(isCursorPopulated(cursor)){ 49 | do { 50 | String type = cursor.getString(cursor.getColumnIndex(ExpenseTypeTable.TYPE)); 51 | expenseTypes.add(type); 52 | } while(cursor.moveToNext()); 53 | } 54 | 55 | return expenseTypes; 56 | } 57 | 58 | public void deleteAll() { 59 | SQLiteDatabase database = this.getWritableDatabase(); 60 | database.delete(ExpenseTypeTable.TABLE_NAME, "", new String[]{}); 61 | database.delete(ExpenseTable.TABLE_NAME, "", new String[]{}); 62 | database.close(); 63 | } 64 | 65 | public void addExpense(Expense expense) { 66 | SQLiteDatabase database = this.getWritableDatabase(); 67 | ContentValues values = new ContentValues(); 68 | values.put(ExpenseTable.AMOUNT, expense.getAmount()); 69 | values.put(ExpenseTable.TYPE, expense.getType()); 70 | values.put(ExpenseTable.DATE, expense.getDate()); 71 | 72 | database.insert(ExpenseTable.TABLE_NAME, null, values); 73 | } 74 | 75 | public List getExpenses() { 76 | SQLiteDatabase database = this.getWritableDatabase(); 77 | Cursor cursor = database.rawQuery(ExpenseTable.SELECT_ALL, null); 78 | 79 | return buildExpenses(cursor); 80 | } 81 | 82 | public List getTodaysExpenses() { 83 | SQLiteDatabase database = this.getWritableDatabase(); 84 | Cursor cursor = database.rawQuery(ExpenseTable.getExpensesForDate(getCurrentDate()), null); 85 | 86 | return buildExpenses(cursor); 87 | } 88 | 89 | public List getCurrentWeeksExpenses() { 90 | SQLiteDatabase database = this.getWritableDatabase(); 91 | Cursor cursor = database.rawQuery(ExpenseTable.getConsolidatedExpensesForDates(getCurrentWeeksDates()), null); 92 | return buildExpenses(cursor); 93 | } 94 | 95 | public List getExpensesGroupByCategory() { 96 | SQLiteDatabase database = this.getWritableDatabase(); 97 | Cursor cursor = database.rawQuery(ExpenseTable.SELECT_ALL_GROUP_BY_CATEGORY, null); 98 | return buildExpenses(cursor); 99 | } 100 | 101 | public List getExpensesForCurrentMonthGroupByCategory() { 102 | SQLiteDatabase database = this.getWritableDatabase(); 103 | Cursor cursor = database.rawQuery(ExpenseTable.getExpenseForCurrentMonth(DateUtil.currentMonthOfYear()), null); 104 | return buildExpenses(cursor); 105 | } 106 | 107 | public void addExpenseType(ExpenseType type) { 108 | SQLiteDatabase database = this.getWritableDatabase(); 109 | ContentValues values = new ContentValues(); 110 | values.put(ExpenseTable.TYPE, type.getType()); 111 | 112 | database.insert(ExpenseTypeTable.TABLE_NAME, null, values); 113 | } 114 | 115 | public void truncate(String tableName) { 116 | SQLiteDatabase database = this.getWritableDatabase(); 117 | database.execSQL("delete from " + tableName); 118 | } 119 | 120 | private List buildExpenses(Cursor cursor) { 121 | List expenses = new ArrayList<>(); 122 | if(isCursorPopulated(cursor)){ 123 | do { 124 | String type = cursor.getString(cursor.getColumnIndex(ExpenseTable.TYPE)); 125 | String amount = cursor.getString(cursor.getColumnIndex(ExpenseTable.AMOUNT)); 126 | String date = cursor.getString(cursor.getColumnIndex(ExpenseTable.DATE)); 127 | String id = cursor.getString(cursor.getColumnIndex(ExpenseTable._ID)); 128 | 129 | Expense expense = id == null ? new Expense(parseLong(amount), type, date) : new Expense(parseInt(id), parseLong(amount), type, date); 130 | expenses.add(expense); 131 | } while(cursor.moveToNext()); 132 | } 133 | 134 | return expenses; 135 | } 136 | 137 | private boolean isCursorPopulated(Cursor cursor) { 138 | return cursor != null && cursor.moveToFirst(); 139 | } 140 | 141 | private void seedExpenseTypes(SQLiteDatabase sqLiteDatabase) { 142 | List expenseTypes = ExpenseTypeTable.seedData(); 143 | for (ExpenseType expenseType : expenseTypes) { 144 | ContentValues contentValues = new ContentValues(); 145 | contentValues.put(ExpenseTypeTable.TYPE, expenseType.getType()); 146 | 147 | sqLiteDatabase.insert(ExpenseTypeTable.TABLE_NAME, null, contentValues); 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/model/Expense.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.model; 2 | 3 | public class Expense { 4 | private String type; 5 | private String date; 6 | private Long amount; 7 | private Integer id; 8 | 9 | public Expense(Long amount, String type, String date) { 10 | this.type = type; 11 | this.date = date; 12 | this.amount = amount; 13 | } 14 | 15 | public Expense(Integer id, Long amount, String type, String date) { 16 | this(amount, type, date); 17 | this.id = id; 18 | } 19 | 20 | public Long getAmount() { 21 | return amount; 22 | } 23 | 24 | public String getType() { 25 | return type; 26 | } 27 | 28 | public String getDate() { 29 | return date; 30 | } 31 | 32 | public Integer getId() { 33 | return id; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/model/ExpenseType.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.model; 2 | 3 | public class ExpenseType { 4 | private String type; 5 | 6 | public ExpenseType(String type) { 7 | this.type = type; 8 | } 9 | 10 | public String getType() { 11 | return type; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/notification/FillExpenseNotificationScheduler.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.notification; 2 | 3 | import android.app.AlarmManager; 4 | import android.app.PendingIntent; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | 8 | import java.util.Calendar; 9 | 10 | public class FillExpenseNotificationScheduler { 11 | 12 | public void schedule(Context context) { 13 | AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 14 | 15 | Intent notificationIntent = new Intent("android.media.action.DISPLAY_NOTIFICATION"); 16 | notificationIntent.addCategory("android.intent.category.DEFAULT"); 17 | 18 | PendingIntent broadcast = PendingIntent.getBroadcast(context, 100, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT); 19 | 20 | Calendar calendar = Calendar.getInstance(); 21 | calendar.set(Calendar.HOUR_OF_DAY, 21); 22 | calendar.set(Calendar.MINUTE, 00); 23 | calendar.set(Calendar.SECOND, 00); 24 | 25 | alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), broadcast); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/presenter/CategoryPresenter.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.presenter; 2 | 3 | import ajitsingh.com.expensemanager.database.ExpenseDatabaseHelper; 4 | import ajitsingh.com.expensemanager.model.ExpenseType; 5 | import ajitsingh.com.expensemanager.view.AddCategoryView; 6 | 7 | public class CategoryPresenter { 8 | private final AddCategoryView view; 9 | private final ExpenseDatabaseHelper database; 10 | 11 | public CategoryPresenter(AddCategoryView view, ExpenseDatabaseHelper database) { 12 | this.view = view; 13 | this.database = database; 14 | } 15 | 16 | public boolean addCategory() { 17 | String newCategory = view.getCategory(); 18 | if(newCategory.isEmpty()){ 19 | view.displayError(); 20 | return false; 21 | } 22 | 23 | database.addExpenseType(new ExpenseType(newCategory)); 24 | return true; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/presenter/CurrentMonthExpensePresenter.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.presenter; 2 | 3 | import com.echo.holographlibrary.Bar; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import ajitsingh.com.expensemanager.database.ExpenseDatabaseHelper; 9 | import ajitsingh.com.expensemanager.model.Expense; 10 | import ajitsingh.com.expensemanager.utils.ExpenseCollection; 11 | import ajitsingh.com.expensemanager.view.CurrentMonthExpenseView; 12 | 13 | 14 | public class CurrentMonthExpensePresenter { 15 | private final CurrentMonthExpenseView view; 16 | private final ExpenseCollection expenseCollection; 17 | 18 | public CurrentMonthExpensePresenter(CurrentMonthExpenseView view, ExpenseDatabaseHelper database) { 19 | this.view = view; 20 | List expenses = database.getExpensesForCurrentMonthGroupByCategory(); 21 | expenseCollection = new ExpenseCollection(expenses); 22 | } 23 | 24 | public void plotGraph() { 25 | List points = new ArrayList(); 26 | 27 | for (Expense expense : expenseCollection.withoutMoneyTransfer()) { 28 | Bar bar = new Bar(); 29 | bar.setColor(view.getGraphColor()); 30 | bar.setName(expense.getType()); 31 | bar.setValue(expense.getAmount()); 32 | points.add(bar); 33 | } 34 | 35 | view.displayGraph(points); 36 | } 37 | 38 | public void showTotalExpense() { 39 | view.displayTotalExpense(expenseCollection.getTotalExpense()); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/presenter/CurrentWeekExpensePresenter.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.presenter; 2 | 3 | import ajitsingh.com.expensemanager.database.ExpenseDatabaseHelper; 4 | import ajitsingh.com.expensemanager.utils.ExpenseCollection; 5 | import ajitsingh.com.expensemanager.view.CurrentWeekExpenseView; 6 | 7 | public class CurrentWeekExpensePresenter { 8 | 9 | private CurrentWeekExpenseView view; 10 | private ExpenseDatabaseHelper database; 11 | private ExpenseCollection expenseCollection; 12 | 13 | public CurrentWeekExpensePresenter(ExpenseDatabaseHelper database, CurrentWeekExpenseView view) { 14 | this.database = database; 15 | this.view = view; 16 | expenseCollection = new ExpenseCollection(this.database.getCurrentWeeksExpenses()); 17 | } 18 | 19 | public void renderTotalExpenses() { 20 | view.displayTotalExpenses(expenseCollection.getTotalExpense()); 21 | } 22 | 23 | public void renderCurrentWeeksExpenses() { 24 | view.displayCurrentWeeksExpenses(expenseCollection.groupByDate()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/presenter/ExpensePresenter.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.presenter; 2 | 3 | import ajitsingh.com.expensemanager.database.ExpenseDatabaseHelper; 4 | import ajitsingh.com.expensemanager.model.Expense; 5 | import ajitsingh.com.expensemanager.view.ExpenseView; 6 | 7 | import static ajitsingh.com.expensemanager.utils.DateUtil.getCurrentDate; 8 | 9 | public class ExpensePresenter { 10 | 11 | private ExpenseDatabaseHelper database; 12 | private ExpenseView view; 13 | 14 | public ExpensePresenter(ExpenseDatabaseHelper expenseDatabaseHelper, ExpenseView view) { 15 | this.database = expenseDatabaseHelper; 16 | this.view = view; 17 | } 18 | 19 | public boolean addExpense() { 20 | String amount = view.getAmount(); 21 | 22 | if(amount.isEmpty()) { 23 | view.displayError(); 24 | return false; 25 | } 26 | 27 | Expense expense = new Expense(Long.valueOf(amount), view.getType(), getCurrentDate()); 28 | database.addExpense(expense); 29 | return true; 30 | } 31 | 32 | public void setExpenseTypes() { 33 | view.renderExpenseTypes(database.getExpenseTypes()); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/presenter/NavigationDrawerPresenter.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.presenter; 2 | 3 | import ajitsingh.com.expensemanager.activity.CurrentMonthExpenseFragment; 4 | import ajitsingh.com.expensemanager.activity.CurrentWeekExpenseFragment; 5 | import ajitsingh.com.expensemanager.view.NavigationDrawerItemView; 6 | 7 | public class NavigationDrawerPresenter { 8 | 9 | public static final String THIS_WEEK = "This Week"; 10 | public static final String THIS_MONTH = "This Month"; 11 | public static final String HOME = "Home"; 12 | private NavigationDrawerItemView view; 13 | 14 | public NavigationDrawerPresenter(NavigationDrawerItemView view) { 15 | this.view = view; 16 | } 17 | 18 | public void onItemSelected(String drawerItem) { 19 | switch (drawerItem){ 20 | case THIS_WEEK: 21 | view.render(new CurrentWeekExpenseFragment()); 22 | break; 23 | case THIS_MONTH: 24 | view.render(new CurrentMonthExpenseFragment()); 25 | break; 26 | case HOME: 27 | view.goToHome(); 28 | break; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/presenter/TodaysExpensePresenter.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.presenter; 2 | 3 | import java.util.List; 4 | 5 | import ajitsingh.com.expensemanager.database.ExpenseDatabaseHelper; 6 | import ajitsingh.com.expensemanager.model.Expense; 7 | import ajitsingh.com.expensemanager.view.TodaysExpenseView; 8 | 9 | public class TodaysExpensePresenter { 10 | 11 | private TodaysExpenseView view; 12 | private final List expenses; 13 | 14 | public TodaysExpensePresenter(TodaysExpenseView view, ExpenseDatabaseHelper expenseDatabaseHelper) { 15 | this.view = view; 16 | expenses = expenseDatabaseHelper.getTodaysExpenses(); 17 | } 18 | 19 | public void renderTotalExpense() { 20 | Long totalExpense = 0l; 21 | for (Expense expense : expenses) 22 | totalExpense += expense.getAmount(); 23 | 24 | view.displayTotalExpense(totalExpense); 25 | } 26 | 27 | public void renderTodaysExpenses() { 28 | view.displayTodaysExpenses(expenses); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/receiver/FillExpenseNotificationReceiver.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.receiver; 2 | 3 | import android.app.Notification; 4 | import android.app.NotificationManager; 5 | import android.app.PendingIntent; 6 | import android.app.TaskStackBuilder; 7 | import android.content.BroadcastReceiver; 8 | import android.content.Context; 9 | import android.content.Intent; 10 | 11 | import androidx.core.app.NotificationCompat; 12 | 13 | import ajitsingh.com.expensemanager.R; 14 | import ajitsingh.com.expensemanager.activity.MainActivity; 15 | 16 | public class FillExpenseNotificationReceiver extends BroadcastReceiver { 17 | @Override 18 | public void onReceive(Context context, Intent intent) { 19 | Intent notificationIntent = new Intent(context, MainActivity.class); 20 | 21 | TaskStackBuilder stackBuilder = TaskStackBuilder.create(context); 22 | stackBuilder.addParentStack(MainActivity.class); 23 | stackBuilder.addNextIntent(notificationIntent); 24 | 25 | PendingIntent pendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_ONE_SHOT); 26 | 27 | NotificationCompat.Builder builder = new NotificationCompat.Builder(context); 28 | 29 | Notification notification = builder.setContentTitle("Fill Today's Expense") 30 | .setContentText("Never miss an expense, fill it now.") 31 | .setTicker("Every expense is important!") 32 | .setSmallIcon(R.mipmap.ic_launcher) 33 | .setAutoCancel(true) 34 | .setContentIntent(pendingIntent).build(); 35 | 36 | NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); 37 | notificationManager.notify(0, notification); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/table/ExpenseTable.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.table; 2 | 3 | import android.provider.BaseColumns; 4 | 5 | import java.util.ArrayList; 6 | 7 | public class ExpenseTable implements BaseColumns { 8 | public static final String TABLE_NAME = "expenses"; 9 | public static final String AMOUNT = "amount"; 10 | public static final String TYPE = "type"; 11 | public static final String DATE = "date"; 12 | 13 | public static final String CREATE_TABLE_QUERY = "create table " + TABLE_NAME + " ("+ 14 | _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, "+ 15 | AMOUNT +" INTEGER,"+ 16 | TYPE +" TEXT, "+ 17 | DATE +" TEXT )"; 18 | 19 | public static final String SELECT_ALL = "SELECT * FROM " + TABLE_NAME + " ORDER BY " + _ID + " DESC"; 20 | 21 | public static final String SELECT_ALL_GROUP_BY_CATEGORY = "SELECT "+_ID+", date, type, sum(amount) as amount FROM " + TABLE_NAME + " GROUP BY type"; 22 | 23 | public static String getExpensesForDate(String date){ 24 | return "SELECT * FROM " + TABLE_NAME + " WHERE date like '"+date+"%' ORDER BY " + _ID + " DESC"; 25 | } 26 | 27 | public static String getConsolidatedExpensesForDates(ArrayList dates) { 28 | String dateLike = ""; 29 | for (String date : dates){ 30 | dateLike += "date like '" + date + "%' " + (dates.get(dates.size() - 1) == date ? "" : "or "); 31 | } 32 | 33 | return "SELECT "+ _ID +", date, type, sum(amount) as amount FROM " + TABLE_NAME + " WHERE " + dateLike + " GROUP BY date, type"; 34 | } 35 | 36 | public static String getExpenseForCurrentMonth(String currentMonthOfYear) { 37 | String currentMonthsExpenses = "(SELECT " + _ID + ", date, type, amount FROM " + 38 | TABLE_NAME + " WHERE date like '%-" + currentMonthOfYear + "')"; 39 | 40 | return "SELECT " + _ID + ", date, type, sum(amount) as amount FROM " + 41 | currentMonthsExpenses + " GROUP BY type"; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/table/ExpenseTypeTable.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.table; 2 | 3 | import android.provider.BaseColumns; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | import ajitsingh.com.expensemanager.model.ExpenseType; 9 | 10 | public class ExpenseTypeTable implements BaseColumns { 11 | public static final String TABLE_NAME = "expense_types"; 12 | public static final String TYPE = "type"; 13 | 14 | public static final String CREATE_TABLE_QUERY = "create table " + TABLE_NAME + " ("+ _ID +" INTEGER PRIMARY KEY AUTOINCREMENT, "+ TYPE +" TEXT)"; 15 | public static final String SELECT_ALL = "SELECT * FROM " + TABLE_NAME; 16 | 17 | public static List seedData(){ 18 | ArrayList expenseTypes = new ArrayList<>(); 19 | expenseTypes.add(new ExpenseType("Food")); 20 | expenseTypes.add(new ExpenseType("Travel")); 21 | expenseTypes.add(new ExpenseType("Health")); 22 | expenseTypes.add(new ExpenseType("Shopping")); 23 | expenseTypes.add(new ExpenseType("Rent")); 24 | expenseTypes.add(new ExpenseType("Money-Transfer")); 25 | expenseTypes.add(new ExpenseType("Other")); 26 | 27 | return expenseTypes; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/utils/DateUtil.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.utils; 2 | 3 | import org.joda.time.DateTime; 4 | import org.joda.time.DateTimeConstants; 5 | import org.joda.time.LocalDate; 6 | 7 | import java.text.DateFormat; 8 | import java.text.ParseException; 9 | import java.text.SimpleDateFormat; 10 | import java.util.ArrayList; 11 | import java.util.Calendar; 12 | import java.util.Date; 13 | import java.util.Locale; 14 | 15 | public class DateUtil { 16 | public static final String DATE_FORMAT = "dd-MM-yyyy"; 17 | 18 | public static String getCurrentDate(){ 19 | return DateTime.now().toString(DATE_FORMAT); 20 | } 21 | 22 | public static ArrayList getCurrentWeeksDates(){ 23 | ArrayList dates = new ArrayList<>(); 24 | 25 | LocalDate now = new LocalDate(); 26 | for (int day = DateTimeConstants.MONDAY; day <= DateTimeConstants.SUNDAY; day++){ 27 | LocalDate localDate = now.withDayOfWeek(day); 28 | dates.add(getFormattedDate(localDate, DATE_FORMAT)); 29 | } 30 | 31 | return dates; 32 | } 33 | 34 | public static String currentMonthOfYear() { 35 | String date = DateTime.now().toString(DATE_FORMAT); 36 | String[] split = date.split("-"); 37 | return split[1] + "-" + split[2]; 38 | } 39 | 40 | public static String getDayName(String dateString) { 41 | DateFormat formatter = new SimpleDateFormat("dd-MM-yyyy"); 42 | Date date = null; 43 | try { date = formatter.parse(dateString); } 44 | catch (ParseException e) {} 45 | 46 | Calendar calendar = Calendar.getInstance(); 47 | calendar.setTime(date); 48 | 49 | return calendar.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.SHORT, Locale.US); 50 | } 51 | 52 | private static String getFormattedDate(LocalDate date, String format){ 53 | SimpleDateFormat simpleDateFormat = new SimpleDateFormat(format); 54 | return simpleDateFormat.format(date.toDate()); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/utils/ExpenseCollection.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.utils; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.Objects; 8 | 9 | import ajitsingh.com.expensemanager.model.Expense; 10 | 11 | public class ExpenseCollection { 12 | private List expenses; 13 | 14 | public ExpenseCollection(List expenses) { 15 | this.expenses = expenses; 16 | } 17 | 18 | public Long getTotalExpense(){ 19 | Long totalExpense = 0l; 20 | for (Expense expense : expenses) { 21 | totalExpense += expense.getAmount(); 22 | } 23 | 24 | return totalExpense; 25 | } 26 | 27 | public Map> groupByDate() { 28 | Map> expensesByDate = new HashMap<>(); 29 | for (Expense expense : expenses) { 30 | if(expensesByDate.get(expense.getDate()) == null){ 31 | List expensesList = new ArrayList<>(); 32 | expensesList.add(expense); 33 | expensesByDate.put(expense.getDate(), expensesList); 34 | 35 | } else { 36 | expensesByDate.get(expense.getDate()).add(expense); 37 | } 38 | } 39 | 40 | return expensesByDate; 41 | } 42 | 43 | public List withoutMoneyTransfer() { 44 | ArrayList expenses = new ArrayList<>(); 45 | for (Expense expense : this.expenses) { 46 | if(!Objects.equals(expense.getType(), "Money-Transfer")) 47 | expenses.add(expense); 48 | } 49 | 50 | return expenses; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/view/AddCategoryView.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.view; 2 | 3 | public interface AddCategoryView { 4 | String getCategory(); 5 | 6 | void displayError(); 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/view/CurrentMonthExpenseView.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.view; 2 | 3 | import com.echo.holographlibrary.Bar; 4 | 5 | import java.util.List; 6 | 7 | public interface CurrentMonthExpenseView { 8 | void displayGraph(List points); 9 | 10 | void displayTotalExpense(Long totalExpense); 11 | 12 | int getGraphColor(); 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/view/CurrentWeekExpenseView.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.view; 2 | 3 | import java.util.List; 4 | import java.util.Map; 5 | 6 | import ajitsingh.com.expensemanager.model.Expense; 7 | 8 | public interface CurrentWeekExpenseView { 9 | void displayCurrentWeeksExpenses(Map> expensesByDate); 10 | 11 | void displayTotalExpenses(Long totalExpense); 12 | } 13 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/view/ExpenseView.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.view; 2 | 3 | import java.util.List; 4 | 5 | public interface ExpenseView { 6 | String getAmount(); 7 | String getType(); 8 | void renderExpenseTypes(List expenseTypes); 9 | void displayError(); 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/view/NavigationDrawerItemView.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.view; 2 | 3 | 4 | import androidx.fragment.app.Fragment; 5 | 6 | public interface NavigationDrawerItemView { 7 | void render(Fragment fragment); 8 | 9 | void goToHome(); 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/ajitsingh/com/expensemanager/view/TodaysExpenseView.java: -------------------------------------------------------------------------------- 1 | package ajitsingh.com.expensemanager.view; 2 | 3 | import java.util.List; 4 | 5 | import ajitsingh.com.expensemanager.model.Expense; 6 | 7 | public interface TodaysExpenseView { 8 | void displayTotalExpense(Long totalExpense); 9 | void displayTodaysExpenses(List expenses); 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/background_color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/button_primary.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/button_secondary.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/header_background_color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | 14 | 23 | 24 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/current_week_expenses.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 18 | 19 | 25 | 26 | 34 | 35 | 41 | 42 | 47 | 48 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /app/src/main/res/layout/drawer_list_item.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/expense_graph.xml: -------------------------------------------------------------------------------- 1 | 9 | 10 | 13 | 14 | 22 | 23 | 29 | 30 | 38 | 39 | 45 | 46 | 51 | 52 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /app/src/main/res/layout/expense_header_text_box.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/expense_text_box.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/new_category.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 19 | 20 | 26 | 27 | 35 | 36 | 41 | 42 |