[
  {
    "path": ".gitignore",
    "content": "*.iml\n.gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\n/captures\n=======\n# Built application files\n*.apk\n*.ap_\n\n# Files for the Dalvik VM\n*.dex\n\n# Java class files\n*.class\n\n# Generated files\nbin/\ngen/\n\n# Gradle files\n.gradle/\nbuild/\n/*/build/\n\n# Local configuration file (sdk path, etc)\nlocal.properties\n\n# Proguard folder generated by Eclipse\nproguard/\n\n# Log Files\n*.log\n"
  },
  {
    "path": ".idea/.name",
    "content": "My Costs"
  },
  {
    "path": ".idea/codeStyleSettings.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ProjectCodeStyleSettingsManager\">\n    <option name=\"PER_PROJECT_SETTINGS\">\n      <value>\n        <option name=\"CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND\" value=\"99\" />\n        <option name=\"NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND\" value=\"99\" />\n        <option name=\"PACKAGES_TO_USE_IMPORT_ON_DEMAND\">\n          <value />\n        </option>\n        <option name=\"IMPORT_LAYOUT_TABLE\">\n          <value>\n            <package name=\"android\" withSubpackages=\"true\" static=\"false\" />\n            <emptyLine />\n            <package name=\"com\" withSubpackages=\"true\" static=\"false\" />\n            <emptyLine />\n            <package name=\"junit\" withSubpackages=\"true\" static=\"false\" />\n            <emptyLine />\n            <package name=\"net\" withSubpackages=\"true\" static=\"false\" />\n            <emptyLine />\n            <package name=\"org\" withSubpackages=\"true\" static=\"false\" />\n            <emptyLine />\n            <package name=\"java\" withSubpackages=\"true\" static=\"false\" />\n            <emptyLine />\n            <package name=\"javax\" withSubpackages=\"true\" static=\"false\" />\n            <emptyLine />\n            <package name=\"\" withSubpackages=\"true\" static=\"false\" />\n            <emptyLine />\n            <package name=\"\" withSubpackages=\"true\" static=\"true\" />\n            <emptyLine />\n          </value>\n        </option>\n        <option name=\"RIGHT_MARGIN\" value=\"100\" />\n        <AndroidXmlCodeStyleSettings>\n          <option name=\"USE_CUSTOM_SETTINGS\" value=\"true\" />\n        </AndroidXmlCodeStyleSettings>\n        <Objective-C-extensions>\n          <option name=\"GENERATE_INSTANCE_VARIABLES_FOR_PROPERTIES\" value=\"ASK\" />\n          <option name=\"RELEASE_STYLE\" value=\"IVAR\" />\n          <option name=\"TYPE_QUALIFIERS_PLACEMENT\" value=\"BEFORE\" />\n          <file>\n            <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Import\" />\n            <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Macro\" />\n            <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Typedef\" />\n            <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Enum\" />\n            <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Constant\" />\n            <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Global\" />\n            <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Struct\" />\n            <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"FunctionPredecl\" />\n            <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Function\" />\n          </file>\n          <class>\n            <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Property\" />\n            <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"Synthesize\" />\n            <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"InitMethod\" />\n            <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"StaticMethod\" />\n            <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"InstanceMethod\" />\n            <option name=\"com.jetbrains.cidr.lang.util.OCDeclarationKind\" value=\"DeallocMethod\" />\n          </class>\n          <extensions>\n            <pair source=\"cpp\" header=\"h\" />\n            <pair source=\"c\" header=\"h\" />\n          </extensions>\n        </Objective-C-extensions>\n        <XML>\n          <option name=\"XML_LEGACY_SETTINGS_IMPORTED\" value=\"true\" />\n        </XML>\n        <codeStyleSettings language=\"XML\">\n          <option name=\"FORCE_REARRANGE_MODE\" value=\"1\" />\n          <indentOptions>\n            <option name=\"CONTINUATION_INDENT_SIZE\" value=\"4\" />\n          </indentOptions>\n          <arrangement>\n            <rules>\n              <section>\n                <rule>\n                  <match>\n                    <AND>\n                      <NAME>xmlns:android</NAME>\n                      <XML_NAMESPACE>Namespace:</XML_NAMESPACE>\n                    </AND>\n                  </match>\n                </rule>\n              </section>\n              <section>\n                <rule>\n                  <match>\n                    <AND>\n                      <NAME>xmlns:.*</NAME>\n                      <XML_NAMESPACE>Namespace:</XML_NAMESPACE>\n                    </AND>\n                  </match>\n                  <order>BY_NAME</order>\n                </rule>\n              </section>\n              <section>\n                <rule>\n                  <match>\n                    <AND>\n                      <NAME>.*:id</NAME>\n                      <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\n                    </AND>\n                  </match>\n                </rule>\n              </section>\n              <section>\n                <rule>\n                  <match>\n                    <AND>\n                      <NAME>.*:name</NAME>\n                      <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\n                    </AND>\n                  </match>\n                </rule>\n              </section>\n              <section>\n                <rule>\n                  <match>\n                    <AND>\n                      <NAME>name</NAME>\n                      <XML_NAMESPACE>^$</XML_NAMESPACE>\n                    </AND>\n                  </match>\n                </rule>\n              </section>\n              <section>\n                <rule>\n                  <match>\n                    <AND>\n                      <NAME>style</NAME>\n                      <XML_NAMESPACE>^$</XML_NAMESPACE>\n                    </AND>\n                  </match>\n                </rule>\n              </section>\n              <section>\n                <rule>\n                  <match>\n                    <AND>\n                      <NAME>.*</NAME>\n                      <XML_NAMESPACE>^$</XML_NAMESPACE>\n                    </AND>\n                  </match>\n                  <order>BY_NAME</order>\n                </rule>\n              </section>\n              <section>\n                <rule>\n                  <match>\n                    <AND>\n                      <NAME>.*:layout_width</NAME>\n                      <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\n                    </AND>\n                  </match>\n                </rule>\n              </section>\n              <section>\n                <rule>\n                  <match>\n                    <AND>\n                      <NAME>.*:layout_height</NAME>\n                      <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\n                    </AND>\n                  </match>\n                </rule>\n              </section>\n              <section>\n                <rule>\n                  <match>\n                    <AND>\n                      <NAME>.*:layout_.*</NAME>\n                      <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\n                    </AND>\n                  </match>\n                  <order>BY_NAME</order>\n                </rule>\n              </section>\n              <section>\n                <rule>\n                  <match>\n                    <AND>\n                      <NAME>.*:width</NAME>\n                      <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\n                    </AND>\n                  </match>\n                  <order>BY_NAME</order>\n                </rule>\n              </section>\n              <section>\n                <rule>\n                  <match>\n                    <AND>\n                      <NAME>.*:height</NAME>\n                      <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\n                    </AND>\n                  </match>\n                  <order>BY_NAME</order>\n                </rule>\n              </section>\n              <section>\n                <rule>\n                  <match>\n                    <AND>\n                      <NAME>.*</NAME>\n                      <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>\n                    </AND>\n                  </match>\n                  <order>BY_NAME</order>\n                </rule>\n              </section>\n              <section>\n                <rule>\n                  <match>\n                    <AND>\n                      <NAME>.*</NAME>\n                      <XML_NAMESPACE>.*</XML_NAMESPACE>\n                    </AND>\n                  </match>\n                  <order>BY_NAME</order>\n                </rule>\n              </section>\n            </rules>\n          </arrangement>\n        </codeStyleSettings>\n      </value>\n    </option>\n    <option name=\"PREFERRED_PROJECT_CODE_STYLE\" value=\"Dario's\" />\n  </component>\n</project>"
  },
  {
    "path": ".idea/compiler.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"CompilerConfiguration\">\n    <option name=\"DEFAULT_COMPILER\" value=\"Javac\" />\n    <resourceExtensions />\n    <wildcardResourcePatterns>\n      <entry name=\"!?*.java\" />\n      <entry name=\"!?*.form\" />\n      <entry name=\"!?*.class\" />\n      <entry name=\"!?*.groovy\" />\n      <entry name=\"!?*.scala\" />\n      <entry name=\"!?*.flex\" />\n      <entry name=\"!?*.kt\" />\n      <entry name=\"!?*.clj\" />\n    </wildcardResourcePatterns>\n    <annotationProcessing>\n      <profile default=\"true\" name=\"Default\" enabled=\"false\">\n        <processorPath useClasspath=\"true\" />\n      </profile>\n    </annotationProcessing>\n  </component>\n</project>"
  },
  {
    "path": ".idea/copyright/profiles_settings.xml",
    "content": "<component name=\"CopyrightManager\">\n  <settings default=\"\" />\n</component>"
  },
  {
    "path": ".idea/gradle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"GradleSettings\">\n    <option name=\"linkedExternalProjectsSettings\">\n      <GradleProjectSettings>\n        <option name=\"distributionType\" value=\"LOCAL\" />\n        <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n        <option name=\"gradleHome\" value=\"$APPLICATION_HOME_DIR$/gradle/gradle-2.8\" />\n        <option name=\"gradleJvm\" value=\"1.7\" />\n        <option name=\"modules\">\n          <set>\n            <option value=\"$PROJECT_DIR$\" />\n            <option value=\"$PROJECT_DIR$/app\" />\n          </set>\n        </option>\n      </GradleProjectSettings>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": ".idea/misc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"EntryPointsManager\">\n    <entry_points version=\"2.0\" />\n  </component>\n  <component name=\"NullableNotNullManager\">\n    <option name=\"myDefaultNullable\" value=\"android.support.annotation.Nullable\" />\n    <option name=\"myDefaultNotNull\" value=\"android.support.annotation.NonNull\" />\n    <option name=\"myNullables\">\n      <value>\n        <list size=\"4\">\n          <item index=\"0\" class=\"java.lang.String\" itemvalue=\"org.jetbrains.annotations.Nullable\" />\n          <item index=\"1\" class=\"java.lang.String\" itemvalue=\"javax.annotation.Nullable\" />\n          <item index=\"2\" class=\"java.lang.String\" itemvalue=\"edu.umd.cs.findbugs.annotations.Nullable\" />\n          <item index=\"3\" class=\"java.lang.String\" itemvalue=\"android.support.annotation.Nullable\" />\n        </list>\n      </value>\n    </option>\n    <option name=\"myNotNulls\">\n      <value>\n        <list size=\"4\">\n          <item index=\"0\" class=\"java.lang.String\" itemvalue=\"org.jetbrains.annotations.NotNull\" />\n          <item index=\"1\" class=\"java.lang.String\" itemvalue=\"javax.annotation.Nonnull\" />\n          <item index=\"2\" class=\"java.lang.String\" itemvalue=\"edu.umd.cs.findbugs.annotations.NonNull\" />\n          <item index=\"3\" class=\"java.lang.String\" itemvalue=\"android.support.annotation.NonNull\" />\n        </list>\n      </value>\n    </option>\n  </component>\n  <component name=\"ProjectLevelVcsManager\" settingsEditedManually=\"false\">\n    <OptionsSetting value=\"true\" id=\"Add\" />\n    <OptionsSetting value=\"true\" id=\"Remove\" />\n    <OptionsSetting value=\"true\" id=\"Checkout\" />\n    <OptionsSetting value=\"true\" id=\"Update\" />\n    <OptionsSetting value=\"true\" id=\"Status\" />\n    <OptionsSetting value=\"true\" id=\"Edit\" />\n    <ConfirmationsSetting value=\"0\" id=\"Add\" />\n    <ConfirmationsSetting value=\"0\" id=\"Remove\" />\n  </component>\n  <component name=\"ProjectRootManager\" version=\"2\" languageLevel=\"JDK_1_7\" assert-keyword=\"true\" jdk-15=\"true\" project-jdk-name=\"1.7\" project-jdk-type=\"JavaSDK\">\n    <output url=\"file://$PROJECT_DIR$/build/classes\" />\n  </component>\n  <component name=\"ProjectType\">\n    <option name=\"id\" value=\"Android\" />\n  </component>\n  <component name=\"masterDetails\">\n    <states>\n      <state key=\"GlobalLibrariesConfigurable.UI\">\n        <settings>\n          <splitter-proportions>\n            <option name=\"proportions\">\n              <list>\n                <option value=\"0.2\" />\n              </list>\n            </option>\n          </splitter-proportions>\n        </settings>\n      </state>\n      <state key=\"JdkListConfigurable.UI\">\n        <settings>\n          <last-edited>Android SDK</last-edited>\n          <splitter-proportions>\n            <option name=\"proportions\">\n              <list>\n                <option value=\"0.2\" />\n              </list>\n            </option>\n          </splitter-proportions>\n        </settings>\n      </state>\n      <state key=\"ProjectJDKs.UI\">\n        <settings>\n          <last-edited>Android API 21 Platform</last-edited>\n          <splitter-proportions>\n            <option name=\"proportions\">\n              <list>\n                <option value=\"0.2\" />\n              </list>\n            </option>\n          </splitter-proportions>\n        </settings>\n      </state>\n      <state key=\"ProjectLibrariesConfigurable.UI\">\n        <settings>\n          <splitter-proportions>\n            <option name=\"proportions\">\n              <list>\n                <option value=\"0.2\" />\n              </list>\n            </option>\n          </splitter-proportions>\n        </settings>\n      </state>\n      <state key=\"ScopeChooserConfigurable.UI\">\n        <settings>\n          <splitter-proportions>\n            <option name=\"proportions\">\n              <list>\n                <option value=\"0.2\" />\n              </list>\n            </option>\n          </splitter-proportions>\n        </settings>\n      </state>\n    </states>\n  </component>\n</project>"
  },
  {
    "path": ".idea/modules.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ProjectModuleManager\">\n    <modules>\n      <module fileurl=\"file://$PROJECT_DIR$/MyCosts.iml\" filepath=\"$PROJECT_DIR$/MyCosts.iml\" />\n      <module fileurl=\"file://$PROJECT_DIR$/app/app.iml\" filepath=\"$PROJECT_DIR$/app/app.iml\" />\n    </modules>\n  </component>\n</project>"
  },
  {
    "path": ".idea/runConfigurations.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"RunConfigurationProducerService\">\n    <option name=\"ignoredProducers\">\n      <set>\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer\" />\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer\" />\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer\" />\n      </set>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": ".idea/vcs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"VcsDirectoryMappings\">\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n    <mapping directory=\"$PROJECT_DIR$\" vcs=\"Git\" />\n  </component>\n</project>"
  },
  {
    "path": "LICENSE",
    "content": "The MIT License (MIT)\n\nCopyright (c) 2015 Dario Miličić\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "QA/findbugs/findbugs-filter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<FindBugsFilter>\n    <Match>\n        <!-- ignore all issues in resource generation -->\n        <Class name=\"~.*\\.R\\$.*\" />\n    </Match>\n    <Match>\n        <Class name=\"~.*\\.Manifest\\$.*\" />\n    </Match>\n\n    <!-- ignore all bugs in test classes, except for those bugs specifically relating to JUnit tests -->\n    <Match>\n        <Class name=\"~.*\\.*Test\" />\n        <!-- test classes are suffixed by 'Test' -->\n        <Not>\n            <Bug code=\"IJU\" />\n            <!-- 'IJU' is the code for bugs related to JUnit test code -->\n        </Not>\n    </Match>\n</FindBugsFilter>"
  },
  {
    "path": "QA/quality.gradle",
    "content": "apply plugin: 'findbugs'\n\ntask findbugs(type: FindBugs) {\n    ignoreFailures = true\n    effort = \"default\"\n    reportLevel = \"medium\"\n    excludeFilter = new File(\"${project.rootDir}/QA/findbugs/findbugs-filter.xml\")\n    classes = files(\"${project.rootDir}/app/build/intermediates/classes\")\n    source = fileTree('src/main/java/')\n    classpath = files()\n    reports {\n        xml.enabled = false\n        html.enabled = true\n        html {\n            destination \"${project.buildDir}/reports/findbugs/findbugs-output.html\"\n        }\n    }\n}"
  },
  {
    "path": "README.md",
    "content": "# Android Clean - Cost Tracker\nA sample cost-tracker app that showcases my Clean architecture approach to build Android applications. It is a simple app with **core features** that include:\n\n- Adding, editing and deleting a cost with a date, category, description and amount\n- Displaying a list of summarized costs day by day\n- Clicking on a summarized cost should display details of all costs for that day\n\nThat's it. For now.\n\nYou are free to download it, modify it, fork it and do anything you want with it.\n\n## What is Clean Architecture?\n\nIn Clean, code is separated into layers in an onion shape. The outer layers of the onion depend on the inner layers but the opposite is not true. It can have an arbitrary amount of layers but for most applications there are 3 layers:\n\n- Outer: Implementation layer\n- Middle:  Presenter/Controller layer\n- Inner: Business logic layer\n\nThe **implementation layer** is where everything framework specific happens. Framework specific code includes every line of code that is not solving your problem, this includes all Android stuff like creating activities and fragments, sending intents, and more general code like networking code and databases. The purpose of the **presenter/controller layer** is to act as a connector between your business logic and framework specific code.\n\nThe most important layer is the **business logic layer**. This is where you actually solve the problem you want to solve building your app. This layer does not contain any framework specific code and you should be able to run it without an emulator. This way you can have your business logic code that is easy to test, develop and maintain. **That is the main benefit of Clean Architecture.**\n\nMore general info about Clean Architecture can be found on this [blog]. This is a general explanation so let me explain how should it look like specifically in Android and how exactly do I build apps using Clean.\n\n## How this app is structured\n\nI've found that the most practical way to build Android apps with Clean is with the following approach. This is how this sample app is structured:\n\n#### Outer layers\n- The **presentation** layer has a standard [MVP] structure. All Activites and Fragments, everything view related and user-facing is put into the layer.\n- Database specific code is inside the **storage** layer.\n- Network specific code is inside the **network** layer.\n- Any other framework specific code would be put into its own layer, for example in Android a **bluetooth** layer is something I often have.\n\n#### Inner/Core layer\n- Business logic is put into the **domain** layer.\n\nAlthough I am omitting a middle layer, that is not actually true. Because my presentation layer actually includes **Presenters**, this provides a good separation of code between presentation and domain layers. Communication between layers is done using interfaces as explained in the blog linked above. In short, the inner layer only uses an interface while its the job of the outer layer to implement it. This way the inner layer only cares about calling methods on an interface, without actually knowing what is going on under the hood.\n\nYou can read more about it in my [detailed guide].\n\n### Syncing to backend\n\nThere is a rails app I made that syncs all cost items to the server. You can find it here: https://mycosts-app.herokuapp.com/. This is a very simple app just to showcase the sync feature and its [source code] is open. Cost items are synced in real-time so you can see costs appearing on the website when you create them on Android. Editing and deleting are currently not supported.\n\n## Future improvements\n\nThis list of future improvements for this app and is something I may or may not actively work on. If anyone wants to get into contributing to open source, these can be a great way to start and are relatively easy to implement. I would be happy if it helps you learn and would accept a pull request if any of these are implemented:\n\n- There is only a list of daily costs, there should be a list for weekly, monthly and perhaps yearly.\n- A graph detailing how much a user spends on each category would be very useful. *Hint: Maybe use [MPAndroidChart]*\n- There is a lot of dependency injection going on, someone can reduce the amount of code by using a DI framework. *Hint: Dagger 2*\n- Nothing is displayed on the main screen when there are no costs. *This is the easiest one, we just need to display something to tell users there is nothing to display, doh.*\n- Add support for more than one currency.\n- There is a fixed amount of categories, someone could add a way to manually add more categories.\n- ???\n\n\nLicense\n----\n\nMIT\n\n\n[//]: # (These are reference links used in the body of this note and get stripped out when the markdown processor does its job. There is no need to format nicely because it shouldn't be seen. Thanks SO - http://stackoverflow.com/questions/4823468/store-comments-in-markdown-syntax)\n\n\n[source code]: <https://github.com/dmilicic/mycosts-rails-backend>\n[detailed guide]: <https://medium.com/@dmilicic/a-detailed-guide-on-developing-android-apps-using-the-clean-architecture-pattern-d38d71e94029>\n[blog]: <https://blog.8thlight.com/uncle-bob/2012/08/13/the-clean-architecture.html>\n[MVP]: <https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter>\n[MPAndroidChart]: <https://github.com/PhilJay/MPAndroidChart>\n[DBFlow]: <https://github.com/Raizlabs/DBFlow>\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\napply plugin: 'com.neenbedankt.android-apt'\napply from: \"${project.rootDir}/QA/quality.gradle\"\n\nandroid {\n    compileSdkVersion 23\n    buildToolsVersion \"23.0.2\"\n\n    defaultConfig {\n        applicationId \"com.kodelabs.mycosts\"\n        minSdkVersion 15\n        targetSdkVersion 23\n        versionCode 1\n        versionName \"1.0\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n}\n\ndef dbflow_version = \"3.0.0-beta3\"\n\n\ndependencies {\n    compile fileTree(dir: 'libs', include: ['*.jar'])\n\n    // general\n    compile 'com.android.support:appcompat-v7:23.1.1'\n    compile 'com.android.support:design:23.1.1'\n    compile 'com.jakewharton:butterknife:7.0.1'\n    compile 'com.jakewharton.timber:timber:4.1.0'\n\n    // inspection\n    compile 'com.facebook.stetho:stetho:1.3.0'\n    compile 'com.facebook.stetho:stetho-okhttp3:1.3.0'\n\n    // network\n    compile 'com.squareup.retrofit2:retrofit:2.0.0-beta4'\n    compile 'com.squareup.retrofit2:converter-gson:+'\n    compile 'com.squareup.okhttp3:logging-interceptor:3.0.1'\n\n    // database\n    apt \"com.github.Raizlabs.DBFlow:dbflow-processor:${dbflow_version}\"\n    compile \"com.github.Raizlabs.DBFlow:dbflow-core:${dbflow_version}\"\n    compile \"com.github.Raizlabs.DBFlow:dbflow:${dbflow_version}\"\n\n    // material design\n    compile 'com.android.support:cardview-v7:23.1.1'\n    compile 'com.android.support:recyclerview-v7:23.1.1'\n    compile('com.github.ozodrukh:CircularReveal:1.1.1@aar') {\n        transitive = true;\n    }\n\n    // tests\n    testCompile 'junit:junit:4.12'\n    testCompile \"org.mockito:mockito-core:1.+\"\n}\n"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/dmilicic/Documents/android-sdk-macosx/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "app/src/androidTest/java/com/kodelabs/mycosts/ApplicationTest.java",
    "content": "package com.kodelabs.mycosts;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a href=\"http://d.android.com/tools/testing/testing_android.html\">Testing Fundamentals</a>\n */\npublic class ApplicationTest extends ApplicationTestCase<Application> {\n    public ApplicationTest() {\n        super(Application.class);\n    }\n}"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest\n    package=\"com.kodelabs.mycosts\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.READ_SYNC_SETTINGS\" />\n    <uses-permission android:name=\"android.permission.WRITE_SYNC_SETTINGS\" />\n    <uses-permission android:name=\"android.permission.AUTHENTICATE_ACCOUNTS\" />\n    <uses-permission android:name=\"android.permission.GET_ACCOUNTS\" />\n\n    <application\n        android:name=\".AndroidApplication\"\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:theme=\"@style/AppTheme\">\n        <activity\n            android:name=\".presentation.ui.activities.MainActivity\"\n            android:label=\"@string/app_name\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n        <activity\n            android:name=\".presentation.ui.activities.AddCostActivity\"\n            android:label=\"Add cost\"\n            android:theme=\"@style/AppTheme\" />\n        <activity\n            android:name=\".presentation.ui.activities.EditCostActivity\"\n            android:label=\"Edit cost\"\n            android:theme=\"@style/AppTheme\" />\n        <activity\n            android:name=\".presentation.ui.activities.AboutActivity\"\n            android:label=\"@string/title_activity_about\"\n            android:parentActivityName=\".presentation.ui.activities.MainActivity\"\n            android:theme=\"@style/AppTheme\">\n            <meta-data\n                android:name=\"android.support.PARENT_ACTIVITY\"\n                android:value=\"com.kodelabs.mycosts.presentation.ui.activities.MainActivity\" />\n        </activity>\n\n\n        <!-- Sync service section -->\n        <service\n            android:name=\".sync.auth.AuthenticatorService\"\n            android:exported=\"false\">\n            <intent-filter>\n                <action android:name=\"android.accounts.AccountAuthenticator\" />\n            </intent-filter>\n            <meta-data\n                android:name=\"android.accounts.AccountAuthenticator\"\n                android:resource=\"@xml/authenticator\" />\n        </service>\n        <service\n            android:name=\".sync.SyncService\"\n            android:exported=\"false\"\n            android:process=\":sync\">\n            <intent-filter>\n                <action android:name=\"android.content.SyncAdapter\" />\n            </intent-filter>\n            <meta-data\n                android:name=\"android.content.SyncAdapter\"\n                android:resource=\"@xml/syncadapter\" />\n        </service>\n\n        <provider\n            android:name=\".storage.contentprovider.StubProvider\"\n            android:authorities=\"@string/stub_content_authority\"\n            android:exported=\"false\"\n            android:syncable=\"true\" />\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/AndroidApplication.java",
    "content": "package com.kodelabs.mycosts;\n\nimport android.app.Application;\n\nimport com.facebook.stetho.Stetho;\nimport com.raizlabs.android.dbflow.config.FlowManager;\n\nimport timber.log.Timber;\nimport timber.log.Timber.DebugTree;\n\n/**\n * Created by dmilicic on 12/10/15.\n */\npublic class AndroidApplication extends Application {\n    @Override\n    public void onCreate() {\n        super.onCreate();\n\n        // init database\n        FlowManager.init(this);\n\n        // enable logging\n        Timber.plant(new DebugTree());\n\n        // enable stetho\n        Stetho.initializeWithDefaults(this);\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/domain/executor/Executor.java",
    "content": "package com.kodelabs.mycosts.domain.executor;\n\nimport com.kodelabs.mycosts.domain.interactors.base.AbstractInteractor;\n\n/**\n * This executor is responsible for running interactors on background threads.\n * <p/>\n * Created by dmilicic on 7/29/15.\n */\npublic interface Executor {\n\n    /**\n     * This method should call the interactor's run method and thus start the interactor. This should be called\n     * on a background thread as interactors might do lengthy operations.\n     *\n     * @param interactor The interactor to run.\n     */\n    void execute(final AbstractInteractor interactor);\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/domain/executor/MainThread.java",
    "content": "package com.kodelabs.mycosts.domain.executor;\n\n/**\n * This interface will define a class that will enable interactors to run certain operations on the main (UI) thread. For example,\n * if an interactor needs to show an object to the UI this can be used to make sure the show method is called on the UI\n * thread.\n * <p/>\n * Created by dmilicic on 7/29/15.\n */\npublic interface MainThread {\n\n    /**\n     * Make runnable operation run in the main thread.\n     *\n     * @param runnable The runnable to run.\n     */\n    void post(final Runnable runnable);\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/domain/executor/impl/ThreadExecutor.java",
    "content": "package com.kodelabs.mycosts.domain.executor.impl;\n\nimport com.kodelabs.mycosts.domain.executor.Executor;\nimport com.kodelabs.mycosts.domain.interactors.base.AbstractInteractor;\n\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * This singleton class will make sure that each interactor operation gets a background thread.\n * <p/>\n * Created by dmilicic on 7/29/15.\n */\npublic class ThreadExecutor implements Executor {\n\n    // This is a singleton\n    private static volatile ThreadExecutor sThreadExecutor;\n\n    private static final int                     CORE_POOL_SIZE  = 3;\n    private static final int                     MAX_POOL_SIZE   = 5;\n    private static final int                     KEEP_ALIVE_TIME = 120;\n    private static final TimeUnit                TIME_UNIT       = TimeUnit.SECONDS;\n    private static final BlockingQueue<Runnable> WORK_QUEUE      = new LinkedBlockingQueue<Runnable>();\n\n    private ThreadPoolExecutor mThreadPoolExecutor;\n\n    private ThreadExecutor() {\n        long keepAlive = KEEP_ALIVE_TIME;\n        mThreadPoolExecutor = new ThreadPoolExecutor(\n                CORE_POOL_SIZE,\n                MAX_POOL_SIZE,\n                keepAlive,\n                TIME_UNIT,\n                WORK_QUEUE);\n    }\n\n    @Override\n    public void execute(final AbstractInteractor interactor) {\n        mThreadPoolExecutor.submit(new Runnable() {\n            @Override\n            public void run() {\n                // run the main logic\n                interactor.run();\n\n                // mark it as finished\n                interactor.onFinished();\n            }\n        });\n    }\n\n    /**\n     * Returns a singleton instance of this executor. If the executor is not initialized then it initializes it and returns\n     * the instance.\n     */\n    public static Executor getInstance() {\n        if (sThreadExecutor == null) {\n            sThreadExecutor = new ThreadExecutor();\n        }\n\n        return sThreadExecutor;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/domain/interactors/AddCostInteractor.java",
    "content": "package com.kodelabs.mycosts.domain.interactors;\n\nimport com.kodelabs.mycosts.domain.interactors.base.Interactor;\n\n/**\n * Created by dmilicic on 12/23/15.\n */\npublic interface AddCostInteractor extends Interactor {\n\n    interface Callback {\n        void onCostAdded();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/domain/interactors/DeleteCostInteractor.java",
    "content": "package com.kodelabs.mycosts.domain.interactors;\n\nimport com.kodelabs.mycosts.domain.interactors.base.Interactor;\nimport com.kodelabs.mycosts.domain.model.Cost;\n\n/**\n * Created by dmilicic on 12/26/15.\n */\npublic interface DeleteCostInteractor extends Interactor {\n\n    interface Callback {\n        void onCostDeleted(Cost cost);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/domain/interactors/EditCostInteractor.java",
    "content": "package com.kodelabs.mycosts.domain.interactors;\n\nimport com.kodelabs.mycosts.domain.interactors.base.Interactor;\nimport com.kodelabs.mycosts.domain.model.Cost;\n\n/**\n * Created by dmilicic on 12/26/15.\n */\npublic interface EditCostInteractor extends Interactor {\n\n    interface Callback {\n\n        void onCostUpdated(Cost cost);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/domain/interactors/GetAllCostsInteractor.java",
    "content": "package com.kodelabs.mycosts.domain.interactors;\n\nimport com.kodelabs.mycosts.domain.interactors.base.Interactor;\nimport com.kodelabs.mycosts.domain.model.Cost;\n\nimport java.util.List;\n\n/**\n * Created by dmilicic on 12/10/15.\n * <p/>\n * This interactor is responsible for retrieving a list of costs from the database.\n */\npublic interface GetAllCostsInteractor extends Interactor {\n\n    interface Callback {\n        void onCostsRetrieved(List<Cost> costList);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/domain/interactors/GetCostByIdInteractor.java",
    "content": "package com.kodelabs.mycosts.domain.interactors;\n\nimport com.kodelabs.mycosts.domain.interactors.base.Interactor;\nimport com.kodelabs.mycosts.domain.model.Cost;\n\n/**\n * Created by dmilicic on 12/27/15.\n */\npublic interface GetCostByIdInteractor extends Interactor {\n\n    interface Callback {\n        void onCostRetrieved(Cost cost);\n\n        void noCostFound();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/domain/interactors/base/AbstractInteractor.java",
    "content": "package com.kodelabs.mycosts.domain.interactors.base;\n\nimport com.kodelabs.mycosts.domain.executor.Executor;\nimport com.kodelabs.mycosts.domain.executor.MainThread;\n\n/**\n * Created by dmilicic on 8/4/15.\n * <p/>\n * This abstract class implements some common methods for all interactors. Cancelling an interactor, check if its running\n * and finishing an interactor has mostly the same code throughout so that is why this class was created. Field methods\n * are declared volatile as we might use these methods from different threads (mainly from UI).\n * <p/>\n * For example, when an activity is getting destroyed then we should probably cancel an interactor\n * but the request will come from the UI thread unless the request was specifically assigned to a background thread.\n */\npublic abstract class AbstractInteractor implements Interactor {\n\n    protected Executor   mThreadExecutor;\n    protected MainThread mMainThread;\n\n    protected volatile boolean mIsCanceled;\n    protected volatile boolean mIsRunning;\n\n    public AbstractInteractor(Executor threadExecutor, MainThread mainThread) {\n        mThreadExecutor = threadExecutor;\n        mMainThread = mainThread;\n    }\n\n    /**\n     * This method contains the actual business logic of the interactor. It SHOULD NOT BE USED DIRECTLY but, instead, a\n     * developer should call the execute() method of an interactor to make sure the operation is done on a background thread.\n     * <p/>\n     * This method should only be called directly while doing unit/integration tests. That is the only reason it is declared\n     * public as to help with easier testing.\n     */\n    public abstract void run();\n\n    public void cancel() {\n        mIsCanceled = true;\n        mIsRunning = false;\n    }\n\n    public boolean isRunning() {\n        return mIsRunning;\n    }\n\n    public void onFinished() {\n        mIsRunning = false;\n        mIsCanceled = false;\n    }\n\n    public void execute() {\n\n        // mark this interactor as running\n        this.mIsRunning = true;\n\n        // start running this interactor in a background thread\n        mThreadExecutor.execute(this);\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/domain/interactors/base/Interactor.java",
    "content": "package com.kodelabs.mycosts.domain.interactors.base;\n\n/**\n * Created by dmilicic on 12/13/15.\n */\npublic interface Interactor {\n\n    /**\n     * This is the main method that starts an interactor. It will make sure that the interactor operation is done on a\n     * background thread.\n     */\n    void execute();\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/domain/interactors/impl/AddCostInteractorImpl.java",
    "content": "package com.kodelabs.mycosts.domain.interactors.impl;\n\nimport com.kodelabs.mycosts.domain.executor.Executor;\nimport com.kodelabs.mycosts.domain.executor.MainThread;\nimport com.kodelabs.mycosts.domain.interactors.AddCostInteractor;\nimport com.kodelabs.mycosts.domain.interactors.base.AbstractInteractor;\nimport com.kodelabs.mycosts.domain.model.Cost;\nimport com.kodelabs.mycosts.domain.repository.CostRepository;\n\nimport java.util.Date;\n\n/**\n * This interactor is responsible for creating and adding a new cost item into the database. It should get all the data needed to create\n * a new cost object and it should insert it in our repository.\n * <p/>\n * Created by dmilicic on 12/23/15.\n */\npublic class AddCostInteractorImpl extends AbstractInteractor implements AddCostInteractor {\n\n    private AddCostInteractor.Callback mCallback;\n    private CostRepository             mCostRepository;\n\n    private String mCategory;\n    private String mDescription;\n    private Date   mDate;\n    private double mAmount;\n\n    public AddCostInteractorImpl(Executor threadExecutor, MainThread mainThread,\n                                 Callback callback, CostRepository costRepository, String category,\n                                 String description, Date date, double amount) {\n        super(threadExecutor, mainThread);\n        mCallback = callback;\n        mCostRepository = costRepository;\n        mCategory = category;\n        mDescription = description;\n        mDate = date;\n        mAmount = amount;\n    }\n\n    @Override\n    public void run() {\n        // create a new cost object and insert it\n        Cost cost = new Cost(mCategory, mDescription, mDate, mAmount);\n        mCostRepository.insert(cost);\n\n        // notify on the main thread that we have inserted this item\n        mMainThread.post(new Runnable() {\n            @Override\n            public void run() {\n                mCallback.onCostAdded();\n            }\n        });\n\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/domain/interactors/impl/DeleteCostInteractorImpl.java",
    "content": "package com.kodelabs.mycosts.domain.interactors.impl;\n\nimport com.kodelabs.mycosts.domain.executor.Executor;\nimport com.kodelabs.mycosts.domain.executor.MainThread;\nimport com.kodelabs.mycosts.domain.interactors.DeleteCostInteractor;\nimport com.kodelabs.mycosts.domain.interactors.base.AbstractInteractor;\nimport com.kodelabs.mycosts.domain.model.Cost;\nimport com.kodelabs.mycosts.domain.repository.CostRepository;\n\n/**\n * Interactor responsible for deleting a cost from the database.\n * <p/>\n * Created by dmilicic on 12/26/15.\n */\npublic class DeleteCostInteractorImpl extends AbstractInteractor implements DeleteCostInteractor {\n\n    private long                          mCostId;\n    private DeleteCostInteractor.Callback mCallback;\n    private CostRepository                mCostRepository;\n\n    public DeleteCostInteractorImpl(Executor threadExecutor,\n                                    MainThread mainThread, long costId,\n                                    Callback callback, CostRepository costRepository) {\n        super(threadExecutor, mainThread);\n        mCostId = costId;\n        mCallback = callback;\n        mCostRepository = costRepository;\n    }\n\n    @Override\n    public void run() {\n\n        // check if this object even exists\n        final Cost cost = mCostRepository.getCostById(mCostId);\n\n        // delete this cost item\n        if (cost != null) mCostRepository.delete(cost);\n\n        // notify on the main thread\n        mMainThread.post(new Runnable() {\n            @Override\n            public void run() {\n                mCallback.onCostDeleted(cost);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/domain/interactors/impl/EditCostInteractorImpl.java",
    "content": "package com.kodelabs.mycosts.domain.interactors.impl;\n\nimport com.kodelabs.mycosts.domain.executor.Executor;\nimport com.kodelabs.mycosts.domain.executor.MainThread;\nimport com.kodelabs.mycosts.domain.interactors.EditCostInteractor;\nimport com.kodelabs.mycosts.domain.interactors.base.AbstractInteractor;\nimport com.kodelabs.mycosts.domain.model.Cost;\nimport com.kodelabs.mycosts.domain.repository.CostRepository;\n\nimport java.util.Date;\n\n/**\n * This interactor handles editing a cost item. It should update it if it exists in the database, otherwise it should insert it.\n * <p/>\n * Created by dmilicic on 12/26/15.\n */\npublic class EditCostInteractorImpl extends AbstractInteractor implements EditCostInteractor {\n\n\n    private EditCostInteractor.Callback mCallback;\n    private CostRepository              mCostRepository;\n\n    private Cost mUpdatedCost;\n\n    private String mCategory;\n    private String mDescription;\n    private Date   mDate;\n    private double mAmount;\n\n\n    public EditCostInteractorImpl(Executor threadExecutor, MainThread mainThread,\n                                  Callback callback, CostRepository costRepository,\n                                  Cost updatedCost, String category, String description, Date date, double amount) {\n        super(threadExecutor, mainThread);\n        mCallback = callback;\n        mCostRepository = costRepository;\n        mUpdatedCost = updatedCost;\n        mCategory = category;\n        mDescription = description;\n        mDate = date;\n        mAmount = amount;\n    }\n\n    @Override\n    public void run() {\n\n        // check if it exists in the database\n        long costId = mUpdatedCost.getId();\n        Cost costToEdit = mCostRepository.getCostById(costId);\n\n        // there is no item with this ID in the database, lets insert it\n        if (costToEdit == null) {\n            costToEdit = new Cost(mCategory, mDescription, mDate, mAmount);\n            mCostRepository.insert(costToEdit);\n        } else { // we found the item in the database, lets update it\n\n            // update the cost\n            costToEdit.setAmount(mAmount);\n            costToEdit.setCategory(mCategory);\n            costToEdit.setDate(mDate);\n            costToEdit.setDescription(mDescription);\n\n            // update in the db\n            mCostRepository.update(costToEdit);\n        }\n\n        // notify on main thread that the update was successful\n        mMainThread.post(new Runnable() {\n            @Override\n            public void run() {\n                mCallback.onCostUpdated(mUpdatedCost);\n            }\n        });\n\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/domain/interactors/impl/GetAllCostsInteractorImpl.java",
    "content": "package com.kodelabs.mycosts.domain.interactors.impl;\n\nimport com.kodelabs.mycosts.domain.executor.Executor;\nimport com.kodelabs.mycosts.domain.executor.MainThread;\nimport com.kodelabs.mycosts.domain.interactors.GetAllCostsInteractor;\nimport com.kodelabs.mycosts.domain.interactors.base.AbstractInteractor;\nimport com.kodelabs.mycosts.domain.model.Cost;\nimport com.kodelabs.mycosts.domain.repository.CostRepository;\n\nimport java.util.Collections;\nimport java.util.Comparator;\nimport java.util.List;\n\n/**\n * This interactor handles getting all costs from the database in a sorted manner. Costs should be sorted by date with\n * the most recent one coming first and the oldest one coming last.\n * <p/>\n * Created by dmilicic on 12/10/15.\n */\npublic class GetAllCostsInteractorImpl extends AbstractInteractor implements GetAllCostsInteractor {\n\n    private Callback       mCallback;\n    private CostRepository mCostRepository;\n\n    private Comparator<Cost> mCostComparator = new Comparator<Cost>() {\n        @Override\n        public int compare(Cost lhs, Cost rhs) {\n\n            if (lhs.getDate().before(rhs.getDate()))\n                return 1;\n\n            if (rhs.getDate().before(lhs.getDate()))\n                return -1;\n\n            return 0;\n        }\n    };\n\n    public GetAllCostsInteractorImpl(Executor threadExecutor, MainThread mainThread, CostRepository costRepository,\n                                     Callback callback) {\n        super(threadExecutor, mainThread);\n\n        if (costRepository == null || callback == null) {\n            throw new IllegalArgumentException(\"Arguments can not be null!\");\n        }\n\n        mCostRepository = costRepository;\n        mCallback = callback;\n    }\n\n    @Override\n    public void run() {\n        // retrieve the costs from the database\n        final List<Cost> costs = mCostRepository.getAllCosts();\n\n        // sort them so the most recent cost items come first, and oldest comes last\n        Collections.sort(costs, mCostComparator);\n\n        // Show costs on the main thread\n        mMainThread.post(new Runnable() {\n            @Override\n            public void run() {\n                mCallback.onCostsRetrieved(costs);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/domain/interactors/impl/GetCostByIdInteractorImpl.java",
    "content": "package com.kodelabs.mycosts.domain.interactors.impl;\n\nimport com.kodelabs.mycosts.domain.executor.Executor;\nimport com.kodelabs.mycosts.domain.executor.MainThread;\nimport com.kodelabs.mycosts.domain.interactors.GetCostByIdInteractor;\nimport com.kodelabs.mycosts.domain.interactors.base.AbstractInteractor;\nimport com.kodelabs.mycosts.domain.model.Cost;\nimport com.kodelabs.mycosts.domain.repository.CostRepository;\n\n/**\n * Interactor responsible for getting a single cost item from the database using its ID. It should return the cost item\n * or notify if there isn't one.\n * <p/>\n * Created by dmilicic on 12/27/15.\n */\npublic class GetCostByIdInteractorImpl extends AbstractInteractor implements GetCostByIdInteractor {\n\n    private long                           mCostId;\n    private CostRepository                 mCostRepository;\n    private GetCostByIdInteractor.Callback mCallback;\n\n\n    public GetCostByIdInteractorImpl(Executor threadExecutor, MainThread mainThread, long costId,\n                                     CostRepository costRepository,\n                                     Callback callback) {\n        super(threadExecutor, mainThread);\n        mCostId = costId;\n        mCostRepository = costRepository;\n        mCallback = callback;\n    }\n\n    @Override\n    public void run() {\n        final Cost cost = mCostRepository.getCostById(mCostId);\n\n        if (cost == null) { // we didn't find the cost we were looking for\n\n            // notify this on the main thread\n            mMainThread.post(new Runnable() {\n                @Override\n                public void run() {\n                    mCallback.noCostFound();\n                }\n            });\n        } else { // we found it!\n\n            // send it on the main thread\n            mMainThread.post(new Runnable() {\n                @Override\n                public void run() {\n                    mCallback.onCostRetrieved(cost);\n                }\n            });\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/domain/model/Cost.java",
    "content": "package com.kodelabs.mycosts.domain.model;\n\nimport java.util.Date;\n\n/**\n * Created by dmilicic on 12/10/15.\n */\npublic class Cost {\n    private long   mId;\n    private String mCategory;\n    private String mDescription;\n    private Date   mDate;\n    private double mAmount;\n\n    public Cost(String category, String description, Date date, double amount) {\n\n        // cost will be \"uniquely\" identified by the current timestamp\n        mId = new Date().getTime();\n\n        mCategory = category;\n        mDescription = description;\n        mDate = date;\n        mAmount = amount;\n    }\n\n\n    /**\n     * This constructor should be used when we are converting an already existing cost item to this POJO, so we already have\n     * an id variable.\n     */\n    public Cost(String category, String description, Date date, double amount, long id) {\n        mId = id;\n        mCategory = category;\n        mDescription = description;\n        mDate = date;\n        mAmount = amount;\n    }\n\n    public void setCategory(String category) {\n        mCategory = category;\n    }\n\n    public void setDescription(String description) {\n        mDescription = description;\n    }\n\n    public void setDate(Date date) {\n        mDate = date;\n    }\n\n    public void setAmount(double amount) {\n        mAmount = amount;\n    }\n\n    public long getId() {\n        return mId;\n    }\n\n    public String getCategory() {\n        return mCategory;\n    }\n\n    public String getDescription() {\n        return mDescription;\n    }\n\n    public Date getDate() {\n        return mDate;\n    }\n\n    public double getAmount() {\n        return mAmount;\n    }\n\n    @Override\n    public boolean equals(Object o) {\n        if (this == o) return true;\n        if (o == null || getClass() != o.getClass()) return false;\n\n        Cost cost = (Cost) o;\n\n        return mId == cost.mId;\n\n    }\n\n    @Override\n    public int hashCode() {\n        return (int) (mId ^ (mId >>> 32));\n    }\n\n    @Override\n    public String toString() {\n        return \"Cost{\" +\n                \"mId=\" + mId +\n                \", mCategory='\" + mCategory + '\\'' +\n                \", mDescription='\" + mDescription + '\\'' +\n                \", mDate=\" + mDate +\n                \", mAmount=\" + mAmount +\n                '}';\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/domain/repository/CostRepository.java",
    "content": "package com.kodelabs.mycosts.domain.repository;\n\nimport com.kodelabs.mycosts.domain.model.Cost;\n\nimport java.util.List;\n\n/**\n * Created by dmilicic on 12/13/15.\n */\npublic interface CostRepository {\n\n    void insert(Cost cost);\n\n    void update(Cost cost);\n\n    Cost getCostById(long id);\n\n    List<Cost> getAllCosts();\n\n    List<Cost> getAllUnsyncedCosts();\n\n    void markSynced(List<Cost> costs);\n\n    void delete(Cost cost);\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/network/RestClient.java",
    "content": "package com.kodelabs.mycosts.network;\n\nimport com.facebook.stetho.okhttp3.StethoInterceptor;\n\nimport okhttp3.OkHttpClient;\nimport okhttp3.logging.HttpLoggingInterceptor;\nimport retrofit2.Retrofit;\nimport retrofit2.converter.gson.GsonConverterFactory;\n\n/**\n * <p/>\n * This is the main entry point for network communication. Use this class for instancing REST services which do the\n * actual communication.\n */\npublic class RestClient {\n\n    /**\n     * This is our main backend/server URL.\n     */\n    public static final String REST_API_URL = \"https://mycosts-app.herokuapp.com/\";\n//    public static final String REST_API_URL = \"http://192.168.0.12:3000\";\n\n\n    private static Retrofit s_retrofit;\n\n    static {\n\n        // enable logging\n        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();\n        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);\n\n        OkHttpClient client = new OkHttpClient.Builder()\n                .addInterceptor(interceptor)\n                .addNetworkInterceptor(new StethoInterceptor())\n                .build();\n\n        s_retrofit = new Retrofit.Builder()\n                .baseUrl(REST_API_URL)\n                .addConverterFactory(GsonConverterFactory.create())\n                .client(client)\n                .build();\n    }\n\n    public static <T> T getService(Class<T> serviceClass) {\n        return s_retrofit.create(serviceClass);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/network/converters/RESTModelConverter.java",
    "content": "package com.kodelabs.mycosts.network.converters;\n\nimport com.kodelabs.mycosts.domain.model.Cost;\nimport com.kodelabs.mycosts.network.model.RESTCost;\n\nimport java.util.Date;\n\n/**\n * Created by dmilicic on 2/14/16.\n */\npublic class RESTModelConverter {\n\n    public static RESTCost convertToRestModel(Cost cost) {\n\n        String desc = cost.getDescription();\n        double amount = cost.getAmount();\n        String category = cost.getCategory();\n        Date date = cost.getDate();\n        long id = cost.getId();\n\n        return new RESTCost(id, category, desc, date, amount);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/network/model/Payload.java",
    "content": "package com.kodelabs.mycosts.network.model;\n\nimport com.google.gson.Gson;\nimport com.google.gson.annotations.SerializedName;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * Created by dmilicic on 2/14/16.\n */\npublic class Payload {\n\n    @SerializedName(\"user\")\n    private String mUsername;\n\n    @SerializedName(\"costs\")\n    private List<RESTCost> mCosts;\n\n    public Payload(String username) {\n        mUsername = username;\n        mCosts = new ArrayList<>();\n    }\n\n    public String getUsername() {\n        return mUsername;\n    }\n\n    public List<RESTCost> getCosts() {\n        return mCosts;\n    }\n\n    public void addCost(RESTCost cost) {\n        mCosts.add(cost);\n    }\n\n    public static void main(String[] args) {\n        Gson gson = new Gson();\n\n        RESTCost cost = new RESTCost(100, \"category\", \"desc\", new Date(), 100.0);\n        String username = \"user\";\n\n        Payload data = new Payload(username);\n        data.addCost(cost);\n\n        cost = new RESTCost(200, \"category\", \"desc\", new Date(), 100.0);\n        data.addCost(cost);\n\n        cost = new RESTCost(300, \"category\", \"desc\", new Date(), 100.0);\n        data.addCost(cost);\n\n        String json = gson.toJson(data);\n\n        System.out.println(json);\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/network/model/RESTCost.java",
    "content": "package com.kodelabs.mycosts.network.model;\n\nimport com.google.gson.annotations.SerializedName;\n\nimport java.util.Date;\n\n/**\n * Created by dmilicic on 2/14/16.\n */\npublic class RESTCost {\n\n    @SerializedName(\"id\")\n    private long mId;\n\n    @SerializedName(\"category\")\n    private String mCategory;\n\n    @SerializedName(\"description\")\n    private String mDescription;\n\n    @SerializedName(\"date\")\n    private Date mDate;\n\n    @SerializedName(\"amount\")\n    private double mAmount;\n\n\n    public RESTCost(long id, String category, String description, Date date, double amount) {\n        mId = id;\n        mCategory = category;\n        mDescription = description;\n        mDate = date;\n        mAmount = amount;\n    }\n\n    public long getId() {\n        return mId;\n    }\n\n    public String getCategory() {\n        return mCategory;\n    }\n\n    public String getDescription() {\n        return mDescription;\n    }\n\n    public Date getDate() {\n        return mDate;\n    }\n\n    public double getAmount() {\n        return mAmount;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/network/services/SyncService.java",
    "content": "package com.kodelabs.mycosts.network.services;\n\nimport com.kodelabs.mycosts.network.model.Payload;\n\nimport retrofit2.Call;\nimport retrofit2.http.Body;\nimport retrofit2.http.Headers;\nimport retrofit2.http.POST;\n\n/**\n * A REST interface describing how data will be synced with the backend.\n * <p/>\n */\npublic interface SyncService {\n\n    /**\n     * This endpoint will be used to send new costs created on this device.\n     */\n    @Headers(\"Connection: close\")\n    @POST(\"/costs\")\n    Call<Void> uploadData(@Body Payload data);\n}"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/presentation/animation/AnimatorFactory.java",
    "content": "package com.kodelabs.mycosts.presentation.animation;\n\nimport android.animation.Animator;\nimport android.animation.Animator.AnimatorListener;\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.os.Build.VERSION;\nimport android.os.Build.VERSION_CODES;\nimport android.view.View;\nimport android.view.ViewAnimationUtils;\nimport android.view.ViewGroup;\n\nimport com.kodelabs.mycosts.R;\n\nimport io.codetail.animation.SupportAnimator;\n\n/**\n * Created by dmilicic on 1/7/16.\n */\npublic class AnimatorFactory {\n\n    public static final int REVEAL_ANIMATION_LENGTH = 350; // in milliseconds\n\n    /**\n     * Creates a circural reveal animation from a given source view. While revealing it uses the reveal layout and after\n     * the animation completes, it starts the activity given in the intent.\n     *\n     * @param src          The source view from which the circular animation starts.\n     * @param revealLayout The layout to reveal in the animation.\n     * @param intent       The intent used to start another activity.\n     * @param activity     The activity is needed as a context object.\n     */\n    public static void enterReveal(ViewGroup revealLayout, final Intent intent, final Activity activity) {\n\n        int cx = (revealLayout.getLeft() + revealLayout.getRight());\n        int cy = revealLayout.getTop();\n        int finalRadius = Math.max(revealLayout.getWidth(), revealLayout.getHeight());\n\n        AnimatorListener animatorListener = new AnimatorListener() {\n            @Override\n            public void onAnimationStart(Animator animation) {\n\n            }\n\n            @Override\n            public void onAnimationEnd(Animator animation) {\n                activity.startActivity(intent);\n                activity.overridePendingTransition(0, R.anim.hold);\n            }\n\n            @Override\n            public void onAnimationCancel(Animator animation) {\n\n            }\n\n            @Override\n            public void onAnimationRepeat(Animator animation) {\n\n            }\n        };\n\n\n//        src.setVisibility(View.INVISIBLE);\n\n        // make the view visible and start the animation\n        revealLayout.setVisibility(View.VISIBLE);\n\n        if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {\n\n            Animator anim =\n                    ViewAnimationUtils.createCircularReveal(revealLayout, cx, cy, 0, finalRadius);\n\n            anim.setDuration(REVEAL_ANIMATION_LENGTH);\n            anim.addListener(animatorListener);\n            anim.start();\n        } else {\n            // create the animator for this view (the start radius is zero)\n            SupportAnimator anim =\n                    io.codetail.animation.ViewAnimationUtils.createCircularReveal(revealLayout, cx, cy, 0, finalRadius);\n\n            anim.setDuration(REVEAL_ANIMATION_LENGTH);\n            anim.addListener(new SupportAnimator.AnimatorListener() {\n                @Override\n                public void onAnimationStart() {\n\n                }\n\n                @Override\n                public void onAnimationEnd() {\n                    activity.startActivity(intent);\n                    activity.overridePendingTransition(0, R.anim.hold);\n                }\n\n                @Override\n                public void onAnimationCancel() {\n\n                }\n\n                @Override\n                public void onAnimationRepeat() {\n\n                }\n            });\n            anim.start();\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/presentation/converter/DailyTotalCostConverter.java",
    "content": "package com.kodelabs.mycosts.presentation.converter;\n\nimport com.kodelabs.mycosts.domain.model.Cost;\nimport com.kodelabs.mycosts.presentation.model.DailyTotalCost;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * Created by dmilicic on 1/4/16.\n */\npublic class DailyTotalCostConverter {\n\n    public static List<DailyTotalCost> convertCostsToDailyCosts(List<Cost> costList) {\n\n        List<DailyTotalCost> result = new ArrayList<>();\n\n        if (costList == null || costList.size() == 0) {\n            // return an empty array if we have nothing to convert\n            return result;\n        }\n\n        // declare and initialize data vars\n        List<Cost> dailyCosts = new ArrayList<>();\n        Date currentDate = costList.get(0).getDate();\n        Cost cost;\n\n        // iterate over all cost items received\n        for (int idx = 0; idx < costList.size(); idx++) {\n\n            cost = costList.get(idx);\n\n            if (idx == 0) { // in case this is the first element\n\n                // initialize the process\n                dailyCosts = new ArrayList<>();\n                currentDate = cost.getDate();\n            }\n\n            // add the current element to the list of daily costs - for the current date\n            dailyCosts.add(cost);\n\n            // check if this is the last element\n            if (idx == costList.size() - 1) {\n                // create a new daily total match\n                DailyTotalCost dailyTotalCost = new DailyTotalCost(dailyCosts, currentDate);\n                result.add(dailyTotalCost);\n\n                continue;\n            }\n\n            // get the next element\n            Cost nextCost = costList.get(idx + 1);\n\n            // check if the next element is from a different day\n            if (!nextCost.getDate().equals(currentDate)) {\n                // create a new daily total match\n                DailyTotalCost dailyTotalCost = new DailyTotalCost(dailyCosts, currentDate);\n                result.add(dailyTotalCost);\n\n                // repeat the process with the next item\n                dailyCosts = new ArrayList<>();\n                currentDate = nextCost.getDate();\n            }\n        }\n\n        return result;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/presentation/model/DailyTotalCost.java",
    "content": "package com.kodelabs.mycosts.presentation.model;\n\nimport com.kodelabs.mycosts.domain.model.Cost;\n\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * Created by dmilicic on 1/4/16.\n */\npublic class DailyTotalCost {\n\n    private List<Cost> mCostList;\n\n    private Date   mDate;\n    private double mTotalCost;\n\n    public DailyTotalCost(List<Cost> costList, Date date) {\n        mCostList = costList;\n        mDate = date;\n\n        // eagerly calculate the total cost\n        mTotalCost = 0.0;\n        for (int idx = 0; idx < costList.size(); idx++) {\n            mTotalCost += costList.get(idx).getAmount();\n        }\n    }\n\n    public List<Cost> getCostList() {\n        return mCostList;\n    }\n\n    public Date getDate() {\n        return mDate;\n    }\n\n    public double getTotalCost() {\n        return mTotalCost;\n    }\n\n    @Override\n    public String toString() {\n        return \"DailyTotalCost{\" +\n                \"mCostList=\" + mCostList +\n                \", mDate=\" + mDate +\n                \", mTotalCost=\" + mTotalCost +\n                '}';\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/presentation/presenters/AbstractPresenter.java",
    "content": "package com.kodelabs.mycosts.presentation.presenters;\n\nimport com.kodelabs.mycosts.domain.executor.Executor;\nimport com.kodelabs.mycosts.domain.executor.MainThread;\n\n/**\n * Created by dmilicic on 12/23/15.\n */\npublic abstract class AbstractPresenter {\n    protected Executor   mExecutor;\n    protected MainThread mMainThread;\n\n    public AbstractPresenter(Executor executor, MainThread mainThread) {\n        mExecutor = executor;\n        mMainThread = mainThread;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/presentation/presenters/AddCostPresenter.java",
    "content": "package com.kodelabs.mycosts.presentation.presenters;\n\nimport com.kodelabs.mycosts.presentation.ui.BaseView;\n\nimport java.util.Date;\n\n/**\n * Created by dmilicic on 12/21/15.\n */\npublic interface AddCostPresenter extends BasePresenter {\n\n\n    interface View extends BaseView {\n\n        void onCostAdded();\n    }\n\n    void addNewCost(Date date, double amount, String description, String category);\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/presentation/presenters/BasePresenter.java",
    "content": "package com.kodelabs.mycosts.presentation.presenters;\n\n/**\n * Created by dmilicic on 7/28/15.\n */\npublic interface BasePresenter {\n    /**\n     * Method that control the lifecycle of the view. It should be called in the view's\n     * (Activity or Fragment) onResume() method.\n     */\n    void resume();\n\n    /**\n     * Method that controls the lifecycle of the view. It should be called in the view's\n     * (Activity or Fragment) onPause() method.\n     */\n    void pause();\n\n    /**\n     * Method that controls the lifecycle of the view. It should be called in the view's\n     * (Activity or Fragment) onStop() method.\n     */\n    void stop();\n\n    /**\n     * Method that control the lifecycle of the view. It should be called in the view's\n     * (Activity or Fragment) onDestroy() method.\n     */\n    void destroy();\n\n\n    /**\n     * Method that should signal the appropriate view to show the appropriate error with the provided message.\n     */\n    void onError(String message);\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/presentation/presenters/EditCostPresenter.java",
    "content": "package com.kodelabs.mycosts.presentation.presenters;\n\nimport com.kodelabs.mycosts.domain.model.Cost;\nimport com.kodelabs.mycosts.presentation.ui.BaseView;\n\nimport java.util.Date;\n\n/**\n * Created by dmilicic on 12/27/15.\n */\npublic interface EditCostPresenter {\n\n    interface View extends BaseView {\n\n        void onCostRetrieved(Cost cost);\n\n        void onCostUpdated(Cost cost);\n    }\n\n    void getCostById(long id);\n\n    void editCost(Cost cost, Date date, double amount, String description, String category);\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/presentation/presenters/MainPresenter.java",
    "content": "package com.kodelabs.mycosts.presentation.presenters;\n\nimport com.kodelabs.mycosts.domain.model.Cost;\nimport com.kodelabs.mycosts.presentation.model.DailyTotalCost;\nimport com.kodelabs.mycosts.presentation.ui.BaseView;\n\nimport java.util.List;\n\n/**\n * Created by dmilicic on 12/10/15.\n */\npublic interface MainPresenter extends BasePresenter {\n\n    interface View extends BaseView {\n\n        void showCosts(List<DailyTotalCost> costs);\n\n        void onClickDeleteCost(long costId);\n\n        void onClickEditCost(long costId, int position);\n\n        void onCostDeleted(Cost cost);\n    }\n\n    void getAllCosts();\n\n    void deleteCost(long costId);\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/presentation/presenters/impl/AddCostPresenterImpl.java",
    "content": "package com.kodelabs.mycosts.presentation.presenters.impl;\n\nimport com.kodelabs.mycosts.domain.executor.Executor;\nimport com.kodelabs.mycosts.domain.executor.MainThread;\nimport com.kodelabs.mycosts.domain.interactors.AddCostInteractor;\nimport com.kodelabs.mycosts.domain.interactors.impl.AddCostInteractorImpl;\nimport com.kodelabs.mycosts.domain.repository.CostRepository;\nimport com.kodelabs.mycosts.presentation.presenters.AbstractPresenter;\nimport com.kodelabs.mycosts.presentation.presenters.AddCostPresenter;\n\nimport java.util.Date;\n\n/**\n * Created by dmilicic on 12/23/15.\n */\npublic class AddCostPresenterImpl extends AbstractPresenter implements AddCostPresenter,\n        AddCostInteractor.Callback {\n\n    private AddCostPresenter.View mView;\n    private CostRepository        mCostRepository;\n\n    public AddCostPresenterImpl(Executor executor, MainThread mainThread,\n                                View view, CostRepository costRepository) {\n        super(executor, mainThread);\n        mView = view;\n        mCostRepository = costRepository;\n    }\n\n    @Override\n    public void addNewCost(Date date, double amount, String description, String category) {\n        AddCostInteractor addCostInteractor = new AddCostInteractorImpl(mExecutor,\n                mMainThread,\n                this,\n                mCostRepository,\n                category,\n                description,\n                date,\n                amount);\n        addCostInteractor.execute();\n    }\n\n    @Override\n    public void onCostAdded() {\n        mView.onCostAdded();\n    }\n\n\n    @Override\n    public void resume() {\n\n    }\n\n    @Override\n    public void pause() {\n\n    }\n\n    @Override\n    public void stop() {\n\n    }\n\n    @Override\n    public void destroy() {\n\n    }\n\n    @Override\n    public void onError(String message) {\n\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/presentation/presenters/impl/EditCostPresenterImpl.java",
    "content": "package com.kodelabs.mycosts.presentation.presenters.impl;\n\nimport com.kodelabs.mycosts.domain.executor.Executor;\nimport com.kodelabs.mycosts.domain.executor.MainThread;\nimport com.kodelabs.mycosts.domain.interactors.EditCostInteractor;\nimport com.kodelabs.mycosts.domain.interactors.GetCostByIdInteractor;\nimport com.kodelabs.mycosts.domain.interactors.impl.EditCostInteractorImpl;\nimport com.kodelabs.mycosts.domain.interactors.impl.GetCostByIdInteractorImpl;\nimport com.kodelabs.mycosts.domain.model.Cost;\nimport com.kodelabs.mycosts.domain.repository.CostRepository;\nimport com.kodelabs.mycosts.presentation.presenters.AbstractPresenter;\nimport com.kodelabs.mycosts.presentation.presenters.EditCostPresenter;\n\nimport java.util.Date;\n\n/**\n * Created by dmilicic on 12/27/15.\n */\npublic class EditCostPresenterImpl extends AbstractPresenter\n        implements EditCostPresenter, GetCostByIdInteractor.Callback, EditCostInteractor.Callback {\n\n    private EditCostPresenter.View mView;\n    private CostRepository         mCostRepository;\n\n    public EditCostPresenterImpl(Executor executor, MainThread mainThread,\n                                 View view, CostRepository costRepository) {\n        super(executor, mainThread);\n        mView = view;\n        mCostRepository = costRepository;\n    }\n\n    @Override\n    public void getCostById(long id) {\n        GetCostByIdInteractor getCostByIdInteractor = new GetCostByIdInteractorImpl(\n                mExecutor,\n                mMainThread,\n                id,\n                mCostRepository,\n                this\n        );\n        getCostByIdInteractor.execute();\n    }\n\n\n    @Override\n    public void onCostRetrieved(Cost cost) {\n        mView.onCostRetrieved(cost);\n    }\n\n    @Override\n    public void noCostFound() {\n        mView.showError(\"No cost found :(\");\n    }\n\n    @Override\n    public void editCost(Cost cost, Date date, double amount, String description, String category) {\n        EditCostInteractor editCostInteractor = new EditCostInteractorImpl(\n                mExecutor,\n                mMainThread,\n                this,\n                mCostRepository,\n                cost,\n                category, description, date, amount\n        );\n        editCostInteractor.execute();\n    }\n\n    @Override\n    public void onCostUpdated(Cost cost) {\n        mView.onCostUpdated(cost);\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/presentation/presenters/impl/MainPresenterImpl.java",
    "content": "package com.kodelabs.mycosts.presentation.presenters.impl;\n\nimport com.kodelabs.mycosts.domain.executor.Executor;\nimport com.kodelabs.mycosts.domain.executor.MainThread;\nimport com.kodelabs.mycosts.domain.interactors.DeleteCostInteractor;\nimport com.kodelabs.mycosts.domain.interactors.GetAllCostsInteractor;\nimport com.kodelabs.mycosts.domain.interactors.impl.DeleteCostInteractorImpl;\nimport com.kodelabs.mycosts.domain.interactors.impl.GetAllCostsInteractorImpl;\nimport com.kodelabs.mycosts.domain.model.Cost;\nimport com.kodelabs.mycosts.domain.repository.CostRepository;\nimport com.kodelabs.mycosts.presentation.converter.DailyTotalCostConverter;\nimport com.kodelabs.mycosts.presentation.model.DailyTotalCost;\nimport com.kodelabs.mycosts.presentation.presenters.AbstractPresenter;\nimport com.kodelabs.mycosts.presentation.presenters.MainPresenter;\n\nimport java.util.List;\n\n/**\n * Created by dmilicic on 12/13/15.\n */\npublic class MainPresenterImpl extends AbstractPresenter implements MainPresenter,\n        GetAllCostsInteractor.Callback,\n        DeleteCostInteractor.Callback {\n\n    private MainPresenter.View mView;\n    private CostRepository     mCostRepository;\n\n    public MainPresenterImpl(Executor executor, MainThread mainThread,\n                             View view, CostRepository costRepository) {\n        super(executor, mainThread);\n        mView = view;\n        mCostRepository = costRepository;\n    }\n\n    @Override\n    public void resume() {\n        getAllCosts();\n    }\n\n    @Override\n    public void pause() {\n\n    }\n\n    @Override\n    public void stop() {\n\n    }\n\n    @Override\n    public void destroy() {\n\n    }\n\n    @Override\n    public void onError(String message) {\n\n    }\n\n    @Override\n    public void getAllCosts() {\n        // get all costs\n        GetAllCostsInteractor getCostsInteractor = new GetAllCostsInteractorImpl(\n                mExecutor,\n                mMainThread,\n                mCostRepository,\n                this\n        );\n        getCostsInteractor.execute();\n    }\n\n    @Override\n    public void onCostsRetrieved(List<Cost> costList) {\n        List<DailyTotalCost> dailyTotalCosts = DailyTotalCostConverter.convertCostsToDailyCosts(costList);\n        mView.showCosts(dailyTotalCosts);\n    }\n\n    @Override\n    public void deleteCost(long costId) {\n\n        // delete this cost item in a background thread\n        DeleteCostInteractor deleteCostInteractor = new DeleteCostInteractorImpl(\n                mExecutor,\n                mMainThread,\n                costId,\n                this,\n                mCostRepository\n        );\n        deleteCostInteractor.execute();\n    }\n\n\n    @Override\n    public void onCostDeleted(Cost cost) {\n        mView.onCostDeleted(cost);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/presentation/ui/BaseView.java",
    "content": "package com.kodelabs.mycosts.presentation.ui;\n\n/**\n * Created by dmilicic on 7/28/15.\n * <p/>\n * This interface represents a basic view. All views should implement these common methods.\n */\npublic interface BaseView {\n\n    /**\n     * This is a general method used for showing some kind of progress during a background task. For example, this\n     * method should show a progress bar and/or disable buttons before some background work starts.\n     */\n    void showProgress();\n\n    /**\n     * This is a general method used for hiding progress information after a background task finishes.\n     */\n    void hideProgress();\n\n    /**\n     * This method is used for showing error messages on the UI.\n     *\n     * @param message The error message to be dislayed.\n     */\n    void showError(String message);\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/presentation/ui/activities/AboutActivity.java",
    "content": "package com.kodelabs.mycosts.presentation.ui.activities;\n\nimport android.content.Intent;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.support.design.widget.FloatingActionButton;\nimport android.support.v7.app.AppCompatActivity;\nimport android.support.v7.widget.Toolbar;\nimport android.view.View;\n\nimport com.kodelabs.mycosts.R;\n\npublic class AboutActivity extends AppCompatActivity {\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_about);\n        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);\n        setSupportActionBar(toolbar);\n\n        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);\n        fab.setOnClickListener(new View.OnClickListener() {\n            @Override\n            public void onClick(View view) {\n                // let the user choose his email client\n                Intent emailIntent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts(\n                        \"mailto\", \"dario.milicic@gmail.com\", null));\n                startActivity(Intent.createChooser(emailIntent, \"Send email...\"));\n            }\n        });\n        getSupportActionBar().setDisplayHomeAsUpEnabled(true);\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/presentation/ui/activities/AbstractCostActivity.java",
    "content": "package com.kodelabs.mycosts.presentation.ui.activities;\n\nimport android.app.DatePickerDialog;\nimport android.os.Bundle;\nimport android.support.v7.app.AppCompatActivity;\nimport android.support.v7.widget.Toolbar;\nimport android.view.Menu;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.DatePicker;\nimport android.widget.EditText;\nimport android.widget.Spinner;\nimport android.widget.TextView;\n\nimport com.kodelabs.mycosts.R;\nimport com.kodelabs.mycosts.presentation.ui.fragments.DatePickerFragment;\nimport com.kodelabs.mycosts.utils.DateUtils;\n\nimport java.util.Date;\n\nimport butterknife.Bind;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\nimport io.codetail.widget.RevealFrameLayout;\n\n/**\n * Created by dmilicic on 12/26/15.\n */\npublic abstract class AbstractCostActivity extends AppCompatActivity\n        implements DatePickerDialog.OnDateSetListener {\n\n\n    @Bind(R.id.reveal_layout)\n    RevealFrameLayout mRevealLayout;\n\n    @Bind(R.id.toolbar)\n    Toolbar mToolbar;\n\n    @Bind(R.id.input_date)\n    TextView mDateTextView;\n\n    @Bind(R.id.input_amount)\n    EditText mAmountEditText;\n\n    @Bind(R.id.input_description)\n    EditText mDescriptionEditText;\n\n    @Bind(R.id.input_cost_category)\n    Spinner mCategorySpinner;\n\n    protected Date   mSelectedDate;\n    protected String mDescription;\n    protected String mCategory;\n    protected double mAmount;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_add_cost);\n        ButterKnife.bind(this);\n\n        mToolbar.setNavigationIcon(R.drawable.ic_close_white_24dp);\n        setSupportActionBar(mToolbar);\n        mToolbar.setNavigationOnClickListener(new OnClickListener() {\n            @Override\n            public void onClick(View v) {\n                onBackPressed();\n            }\n        });\n\n\n        mRevealLayout.setVisibility(View.VISIBLE);\n    }\n\n    @OnClick(R.id.input_date)\n    public void showDatePickerDialog(View v) {\n        DatePickerFragment newFragment = new DatePickerFragment();\n        newFragment.setListener(this);\n        newFragment.show(getFragmentManager(), \"datePicker\");\n    }\n\n\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        // Inflate the menu; this adds items to the action bar if it is present.\n        getMenuInflater().inflate(R.menu.menu_add_cost, menu);\n        return true;\n    }\n\n    protected void extractFormData() {\n        // extract data from the form\n        try {\n            mAmount = Double.valueOf(mAmountEditText.getText().toString());\n        } catch (NumberFormatException e) {\n            mAmount = 0.0;\n        }\n\n        // extract description and category\n        mDescription = mDescriptionEditText.getText().toString();\n        mCategory = mCategorySpinner.getSelectedItem().toString();\n    }\n\n    @Override\n    public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth) {\n        mSelectedDate = DateUtils.createDate(year, monthOfYear, dayOfMonth);\n        mDateTextView.setText(DateUtils.formatDate(mSelectedDate));\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/presentation/ui/activities/AddCostActivity.java",
    "content": "package com.kodelabs.mycosts.presentation.ui.activities;\n\nimport android.os.Bundle;\nimport android.view.MenuItem;\nimport android.widget.Toast;\n\nimport com.kodelabs.mycosts.R;\nimport com.kodelabs.mycosts.domain.executor.impl.ThreadExecutor;\nimport com.kodelabs.mycosts.presentation.presenters.AddCostPresenter;\nimport com.kodelabs.mycosts.presentation.presenters.impl.AddCostPresenterImpl;\nimport com.kodelabs.mycosts.storage.CostRepositoryImpl;\nimport com.kodelabs.mycosts.threading.MainThreadImpl;\nimport com.kodelabs.mycosts.utils.DateUtils;\n\npublic class AddCostActivity extends AbstractCostActivity\n        implements AddCostPresenter.View {\n\n    private AddCostPresenter mPresenter;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        // setup the presenter\n        mPresenter = new AddCostPresenterImpl(\n                ThreadExecutor.getInstance(),\n                MainThreadImpl.getInstance(),\n                this,\n                new CostRepositoryImpl(this)\n        );\n    }\n\n    @Override\n    protected void onResume() {\n        super.onResume();\n\n        // default day should be today\n        mSelectedDate = DateUtils.getToday();\n        mDateTextView.setText(DateUtils.formatDate(mSelectedDate));\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        // Handle action bar item clicks here. The action bar will\n        // automatically handle clicks on the Home/Up button, so long\n        // as you specify a parent activity in AndroidManifest.xml.\n        int id = item.getItemId();\n\n        //noinspection SimplifiableIfStatement\n        if (id == R.id.action_save) {\n\n            extractFormData();\n\n            // pass the data onto the presenter\n            mPresenter.addNewCost(mSelectedDate, mAmount, mDescription, mCategory);\n            return true;\n        }\n\n        return super.onOptionsItemSelected(item);\n\n    }\n\n    @Override\n    public void onCostAdded() {\n        Toast.makeText(this, \"Saved!\", Toast.LENGTH_LONG).show();\n        onBackPressed();\n    }\n\n    @Override\n    public void showProgress() {\n\n    }\n\n    @Override\n    public void hideProgress() {\n\n    }\n\n    @Override\n    public void showError(String message) {\n\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/presentation/ui/activities/EditCostActivity.java",
    "content": "package com.kodelabs.mycosts.presentation.ui.activities;\n\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.annotation.NonNull;\nimport android.view.MenuItem;\nimport android.widget.Toast;\n\nimport com.kodelabs.mycosts.R;\nimport com.kodelabs.mycosts.domain.executor.impl.ThreadExecutor;\nimport com.kodelabs.mycosts.domain.model.Cost;\nimport com.kodelabs.mycosts.presentation.presenters.EditCostPresenter;\nimport com.kodelabs.mycosts.presentation.presenters.impl.EditCostPresenterImpl;\nimport com.kodelabs.mycosts.storage.CostRepositoryImpl;\nimport com.kodelabs.mycosts.threading.MainThreadImpl;\nimport com.kodelabs.mycosts.utils.DateUtils;\n\n/**\n * Created by dmilicic on 12/27/15.\n */\npublic class EditCostActivity extends AbstractCostActivity implements EditCostPresenter.View {\n\n    private EditCostPresenter mPresenter;\n    private Cost              mEditedCost;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n        // create a presenter for this screen\n        mPresenter = new EditCostPresenterImpl(\n                ThreadExecutor.getInstance(),\n                MainThreadImpl.getInstance(),\n                this,\n                new CostRepositoryImpl(this)\n        );\n\n        // extract the cost id of the item we want to edit\n        long costId = getIntent().getLongExtra(MainActivity.EXTRA_COST_ID, -1);\n\n        // in case cost id is not sent\n        if (costId == -1) {\n            Toast.makeText(this, \"Cost not found!\", Toast.LENGTH_SHORT).show();\n            finish();\n            return;\n        }\n\n        // first get the old cost from the database\n        mPresenter.getCostById(costId);\n    }\n\n    @Override\n    public void onCostRetrieved(@NonNull Cost cost) {\n\n        mEditedCost = cost;\n\n        // populate the member variables\n        mAmount = cost.getAmount();\n        mSelectedDate = cost.getDate();\n        mCategory = cost.getCategory();\n        mDescription = cost.getDescription();\n\n        prepopulateFields();\n    }\n\n\n    /**\n     * Finds the position of the given category inside the spinner.\n     *\n     * @param category The provided category for which we are finding the position.\n     * @return Returns an int which represents a position of the provided category in the spinner.\n     */\n    private int findCategoryPosition(String category) {\n        int result = -1;\n        String[] categoryArray = getResources().getStringArray(R.array.category_array);\n        for (int i = 0; i < categoryArray.length; i++) {\n            if (category.equals(categoryArray[i])) result = i;\n        }\n\n        return result;\n    }\n\n    private void prepopulateFields() {\n\n        // prepopulate all text views\n        mAmountEditText.setText(String.valueOf(mAmount));\n        mDateTextView.setText(DateUtils.formatDate(mSelectedDate));\n        mDescriptionEditText.setText(mDescription);\n\n        // find the position of the category item to select\n        int position = findCategoryPosition(mCategory);\n        mCategorySpinner.setSelection(position);\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        // Handle action bar item clicks here. The action bar will\n        // automatically handle clicks on the Home/Up button, so long\n        // as you specify a parent activity in AndroidManifest.xml.\n        int id = item.getItemId();\n\n        //noinspection SimplifiableIfStatement\n        if (id == R.id.action_save) {\n\n            extractFormData();\n\n            // pass the data onto the presenter\n            mPresenter.editCost(mEditedCost, mSelectedDate, mAmount, mDescription, mCategory);\n            return true;\n        }\n\n        return super.onOptionsItemSelected(item);\n    }\n\n\n    @Override\n    public void onCostUpdated(Cost cost) {\n\n        // build the data to send\n        Intent data = new Intent();\n        data.putExtra(MainActivity.EXTRA_COST_ID, cost.getId());\n\n        // mark that this was a success\n        setResult(RESULT_OK, data);\n        finish();\n    }\n\n    @Override\n    public void showProgress() {\n\n    }\n\n    @Override\n    public void hideProgress() {\n\n    }\n\n    @Override\n    public void showError(String message) {\n        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/presentation/ui/activities/MainActivity.java",
    "content": "package com.kodelabs.mycosts.presentation.ui.activities;\n\nimport android.app.AlertDialog;\nimport android.content.DialogInterface;\nimport android.content.Intent;\nimport android.os.Bundle;\nimport android.support.v7.app.AppCompatActivity;\nimport android.support.v7.widget.LinearLayoutManager;\nimport android.support.v7.widget.RecyclerView;\nimport android.support.v7.widget.Toolbar;\nimport android.view.Menu;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.widget.Toast;\n\nimport com.kodelabs.mycosts.R;\nimport com.kodelabs.mycosts.domain.executor.impl.ThreadExecutor;\nimport com.kodelabs.mycosts.domain.model.Cost;\nimport com.kodelabs.mycosts.presentation.animation.AnimatorFactory;\nimport com.kodelabs.mycosts.presentation.model.DailyTotalCost;\nimport com.kodelabs.mycosts.presentation.presenters.MainPresenter;\nimport com.kodelabs.mycosts.presentation.presenters.impl.MainPresenterImpl;\nimport com.kodelabs.mycosts.presentation.ui.adapters.CostItemAdapter;\nimport com.kodelabs.mycosts.storage.CostRepositoryImpl;\nimport com.kodelabs.mycosts.sync.auth.DummyAccountProvider;\nimport com.kodelabs.mycosts.threading.MainThreadImpl;\n\nimport java.util.List;\n\nimport butterknife.Bind;\nimport butterknife.ButterKnife;\nimport io.codetail.widget.RevealFrameLayout;\nimport timber.log.Timber;\n\npublic class MainActivity extends AppCompatActivity implements MainPresenter.View {\n\n    public static final String EXTRA_COST_ID = \"extra_cost_id_key\";\n\n    public static final int EDIT_COST_REQUEST = 0;\n\n    @Bind(R.id.expenses_list)\n    RecyclerView mRecyclerView;\n\n    @Bind(R.id.reveal_layout)\n    RevealFrameLayout mRevealLayout;\n\n    private MainPresenter mMainPresenter;\n\n    private CostItemAdapter mAdapter;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n        ButterKnife.bind(this);\n        Timber.w(\"ONCREATE\");\n\n        init();\n    }\n\n    private void init() {\n\n        // setup recycler view adapter\n        mAdapter = new CostItemAdapter(this, this);\n\n        // setup recycler view\n        mRecyclerView.setLayoutManager(new LinearLayoutManager(this));\n        mRecyclerView.setAdapter(mAdapter);\n\n        // setup toolbar\n        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);\n        setSupportActionBar(toolbar);\n\n        // instantiate the presenter\n        mMainPresenter = new MainPresenterImpl(\n                ThreadExecutor.getInstance(),\n                MainThreadImpl.getInstance(),\n                this,\n                new CostRepositoryImpl(this)\n        );\n\n\n        // create a dummy account if it doesn't yet exist\n        DummyAccountProvider.CreateSyncAccount(this);\n    }\n\n    @Override\n    protected void onResume() {\n        super.onResume();\n        mMainPresenter.resume();\n\n        // reset the layout\n        mRevealLayout.setVisibility(View.INVISIBLE);\n\n        Timber.w(\"ONRESUME\");\n    }\n\n\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        super.onCreateOptionsMenu(menu);\n\n        // Inflate the menu; this adds items to the action bar if it is present.\n        getMenuInflater().inflate(R.menu.menu_main, menu);\n        return true;\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        // Handle action bar item clicks here. The action bar will\n        // automatically handle clicks on the Home/Up button, so long\n        // as you specify a parent activity in AndroidManifest.xml.\n        int id = item.getItemId();\n\n        switch (id) {\n            case R.id.action_add_cost:\n                // intent to start another activity\n                final Intent intent = new Intent(MainActivity.this, AddCostActivity.class);\n\n                // do the animation\n                AnimatorFactory.enterReveal(mRevealLayout, intent, MainActivity.this);\n\n                break;\n            case R.id.action_about:\n                final Intent aboutIntent = new Intent(MainActivity.this, AboutActivity.class);\n                startActivity(aboutIntent);\n                break;\n            default:\n                break;\n        }\n\n        return super.onOptionsItemSelected(item);\n    }\n\n    @Override\n    protected void onActivityResult(int requestCode, int resultCode, Intent data) {\n        super.onActivityResult(requestCode, resultCode, data);\n\n        // check if everything is ok\n        if (requestCode == EDIT_COST_REQUEST && resultCode == RESULT_OK) {\n\n            // let the user know the edit succeded\n            Toast.makeText(this, \"Updated!\", Toast.LENGTH_LONG).show();\n        }\n    }\n\n    @Override\n    public void showCosts(List<DailyTotalCost> costs) {\n        // signal the adapter that it has data to show\n        mAdapter.addNewCosts(costs);\n    }\n\n    @Override\n    public void onClickDeleteCost(final long costId) {\n        DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {\n            @Override\n            public void onClick(DialogInterface dialog, int which) {\n                switch (which) {\n                    case DialogInterface.BUTTON_POSITIVE:\n                        mMainPresenter.deleteCost(costId);\n                        break;\n\n                    case DialogInterface.BUTTON_NEGATIVE:\n                        //No button clicked\n                        break;\n                }\n            }\n        };\n\n        AlertDialog.Builder builder = new AlertDialog.Builder(this);\n        builder.setMessage(\"Delete this cost?\")\n                .setPositiveButton(\"Yes\", dialogClickListener)\n                .setNegativeButton(\"No\", dialogClickListener)\n                .show();\n    }\n\n    @Override\n    public void onCostDeleted(Cost cost) {\n        // we deleted some data, RELOAD ALL THE THINGS!\n        mMainPresenter.getAllCosts();\n    }\n\n    @Override\n    public void onClickEditCost(long costId, int position) {\n\n        // intent to start another activity\n        final Intent intent = new Intent(MainActivity.this, EditCostActivity.class);\n        intent.putExtra(EXTRA_COST_ID, costId);\n\n        startActivityForResult(intent, EDIT_COST_REQUEST);\n    }\n\n    @Override\n    public void showProgress() {\n\n    }\n\n    @Override\n    public void hideProgress() {\n\n    }\n\n    @Override\n    public void showError(String message) {\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/presentation/ui/adapters/CostItemAdapter.java",
    "content": "package com.kodelabs.mycosts.presentation.ui.adapters;\n\nimport android.content.Context;\nimport android.support.annotation.NonNull;\nimport android.support.v7.widget.RecyclerView;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.view.ViewGroup;\nimport android.widget.TextView;\n\nimport com.kodelabs.mycosts.R;\nimport com.kodelabs.mycosts.presentation.model.DailyTotalCost;\nimport com.kodelabs.mycosts.presentation.presenters.MainPresenter;\nimport com.kodelabs.mycosts.presentation.ui.customviews.ExpandedCostView;\nimport com.kodelabs.mycosts.presentation.ui.listeners.IndividualCostViewClickListener;\nimport com.kodelabs.mycosts.presentation.ui.listeners.RecyclerViewClickListener;\nimport com.kodelabs.mycosts.utils.DateUtils;\n\nimport java.util.ArrayList;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport butterknife.Bind;\nimport butterknife.ButterKnife;\n\n/**\n * Created by dmilicic on 12/13/15.\n */\npublic class CostItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements RecyclerViewClickListener {\n\n\n    private enum ViewType {\n        CONTRACTED_CARD, EXPANDED_CARD\n    }\n\n    private List<DailyTotalCost> mCostList;\n    private Context              mContext;\n\n    private Set<Integer> mSelectedItems;\n\n    public final MainPresenter.View mView;\n\n    public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {\n\n        @Bind(R.id.cost_item_title)\n        public TextView mTitle;\n\n        @Bind(R.id.cost_item_total_value)\n        public TextView mTotalCost;\n\n        private RecyclerViewClickListener mListener;\n\n        public void setup(DailyTotalCost dailyTotalCost) {\n            Context context = mTitle.getContext();\n\n            final String dateText = DateUtils.dateToText(context, dailyTotalCost.getDate());\n            final String title = String.format(context.getString(R.string.total_expenses), dateText);\n            mTitle.setText(title);\n            mTotalCost.setText(String.valueOf(dailyTotalCost.getTotalCost()) + \"$\");\n        }\n\n        @Override\n        public void onClick(View v) {\n            mListener.onClickView(getAdapterPosition());\n        }\n\n        public ViewHolder(View v, final RecyclerViewClickListener listener) {\n            super(v);\n            ButterKnife.bind(this, v);\n            v.setOnClickListener(this);\n            mListener = listener;\n        }\n    }\n\n    public static class ExpandedViewHolder extends RecyclerView.ViewHolder\n            implements View.OnClickListener, IndividualCostViewClickListener {\n\n        @Bind(R.id.card_expanded_costview)\n        public ExpandedCostView mExpandedCostView;\n\n        private RecyclerViewClickListener mListener;\n\n        @Override\n        public void onClickDelete(long costId) {\n            mListener.onClickDelete(getAdapterPosition(), costId);\n        }\n\n        @Override\n        public void onClickEdit(long costId) {\n            mListener.onClickEdit(getAdapterPosition(), costId);\n        }\n\n        @Override\n        public void onClick(View v) {\n            mListener.onClickView(getAdapterPosition());\n        }\n\n        public ExpandedViewHolder(View v, final RecyclerViewClickListener listener) {\n            super(v);\n            ButterKnife.bind(this, v);\n            v.setOnClickListener(this);\n\n            // this listener is our adapter\n            mListener = listener;\n\n            // set a listener for edit, delete calls\n            mExpandedCostView.setIndividualCostViewClickListener(this);\n        }\n    }\n\n\n    public CostItemAdapter(MainPresenter.View view, Context context) {\n        mCostList = new ArrayList<>();\n        mView = view;\n        mContext = context;\n        mSelectedItems = new HashSet<>();\n    }\n\n    @Override\n    public int getItemViewType(int position) {\n        // check to see if a view at this position should be expanded or normal/contracted\n        if (mSelectedItems.contains(position))\n            return ViewType.EXPANDED_CARD.ordinal();\n\n        return ViewType.CONTRACTED_CARD.ordinal();\n    }\n\n    @Override\n    public void onClickView(int position) {\n\n        // If clicked on for the first time the view should be counted as selected, if clicked again the view\n        // should be considered unselected.\n        // Selected views will be shown as expanded cards while unselected will be shown as normal/contracted cards\n        if (!mSelectedItems.contains(position))\n            mSelectedItems.add(position);\n        else\n            mSelectedItems.remove(position);\n\n        notifyItemChanged(position);\n    }\n\n    @Override\n    public void onClickDelete(int position, long costId) {\n\n        // in case we are deleting the last element from a day, mark that day as unselected, no point in showing an empty day\n        if (mSelectedItems.contains(position) && mCostList.get(position).getCostList().size() == 1)\n            mSelectedItems.remove(position);\n\n\n        mView.onClickDeleteCost(costId);\n    }\n\n    @Override\n    public void onClickEdit(int position, long costId) {\n        mView.onClickEditCost(costId, position);\n    }\n\n    public void addNewCosts(@NonNull List<DailyTotalCost> costList) {\n        // clean up old data\n        if (mCostList != null) {\n            mCostList.clear();\n        }\n        mCostList = costList;\n\n        notifyDataSetChanged();\n    }\n\n    @Override\n    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {\n        Context context = parent.getContext();\n        LayoutInflater inflater = LayoutInflater.from(context);\n\n        // check if this should be an expanded card\n        if (viewType == ViewType.EXPANDED_CARD.ordinal()) {\n            View view = inflater.inflate(R.layout.card_expanded_daily_cost_item, parent, false);\n            return new ExpandedViewHolder(view, this);\n        }\n\n        // this is a normal/contracted card\n        View view = inflater.inflate(R.layout.card_daily_cost_item, parent, false);\n        return new ViewHolder(view, this);\n    }\n\n    @Override\n    public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {\n        DailyTotalCost cost = mCostList.get(position);\n\n        // setup the views depending on the viewholder type\n        if (viewHolder instanceof ViewHolder) {\n            ((ViewHolder) viewHolder).setup(cost);\n        } else if (viewHolder instanceof ExpandedViewHolder) {\n            ((ExpandedViewHolder) viewHolder).mExpandedCostView.setDailyCost(cost);\n        }\n    }\n\n    @Override\n    public int getItemCount() {\n        return mCostList.size();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/presentation/ui/customviews/CostItemView.java",
    "content": "package com.kodelabs.mycosts.presentation.ui.customviews;\n\nimport android.content.Context;\nimport android.view.LayoutInflater;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.widget.ImageButton;\nimport android.widget.PopupMenu;\nimport android.widget.PopupMenu.OnMenuItemClickListener;\nimport android.widget.RelativeLayout;\nimport android.widget.TextView;\n\nimport com.kodelabs.mycosts.R;\nimport com.kodelabs.mycosts.domain.model.Cost;\nimport com.kodelabs.mycosts.presentation.ui.listeners.IndividualCostViewClickListener;\n\nimport butterknife.Bind;\nimport butterknife.ButterKnife;\nimport butterknife.OnClick;\n\n/**\n * Created by dmilicic on 1/6/16.\n */\npublic class CostItemView extends RelativeLayout implements OnMenuItemClickListener {\n\n    @Bind(R.id.cost_item_title)\n    TextView mCategoryView;\n\n    @Bind(R.id.cost_item_total_value)\n    TextView mValueView;\n\n    @Bind(R.id.cost_item_description)\n    TextView mDescriptionView;\n\n    @Bind(R.id.button_menu)\n    ImageButton mMenuButton;\n\n    private IndividualCostViewClickListener mCostViewClickListener;\n\n    private Cost mCost;\n\n    public CostItemView(Context context,\n                        IndividualCostViewClickListener costViewClickListener, Cost cost) {\n        super(context);\n        mCostViewClickListener = costViewClickListener;\n        mCost = cost;\n        init(context);\n    }\n\n    private void init(Context context) {\n        LayoutInflater inflater = (LayoutInflater) context\n                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);\n        View view = inflater.inflate(R.layout.individual_cost_item, this);\n\n        ButterKnife.bind(this, view);\n\n        setCategory(mCost.getCategory());\n        setDescription(mCost.getDescription());\n        setValue(mCost.getAmount());\n    }\n\n\n    @Override\n    public boolean onMenuItemClick(MenuItem item) {\n\n        // since the listener is set after this object is created it is possible that it can be null, avoid that :)\n        if (mCostViewClickListener == null)\n            return false;\n\n        switch (item.getItemId()) {\n            case R.id.item_edit:\n                mCostViewClickListener.onClickEdit(mCost.getId());\n                return true;\n            case R.id.item_delete:\n                mCostViewClickListener.onClickDelete(mCost.getId());\n                return true;\n            default:\n                return false;\n        }\n    }\n\n\n    @OnClick(R.id.button_menu)\n    void onClickMenu() {\n        PopupMenu popupMenu = new PopupMenu(getContext(), mMenuButton);\n        popupMenu.setOnMenuItemClickListener(this);\n        popupMenu.inflate(R.menu.menu_cost_item);\n        popupMenu.show();\n    }\n\n    private void setCategory(String category) {\n        mCategoryView.setText(category);\n    }\n\n    private void setValue(double value) {\n        String val = String.format(\"%.2f$\", value);\n        mValueView.setText(val);\n    }\n\n    private void setDescription(String description) {\n        mDescriptionView.setText(description);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/presentation/ui/customviews/ExpandedCostView.java",
    "content": "package com.kodelabs.mycosts.presentation.ui.customviews;\n\nimport android.content.Context;\nimport android.support.annotation.Nullable;\nimport android.support.v4.content.ContextCompat;\nimport android.support.v7.widget.CardView;\nimport android.util.AttributeSet;\nimport android.view.LayoutInflater;\nimport android.view.View;\nimport android.widget.LinearLayout;\nimport android.widget.TextView;\n\nimport com.kodelabs.mycosts.R;\nimport com.kodelabs.mycosts.domain.model.Cost;\nimport com.kodelabs.mycosts.presentation.model.DailyTotalCost;\nimport com.kodelabs.mycosts.presentation.ui.listeners.IndividualCostViewClickListener;\nimport com.kodelabs.mycosts.utils.DateUtils;\n\nimport java.util.Date;\nimport java.util.List;\n\nimport butterknife.Bind;\nimport butterknife.ButterKnife;\n\n/**\n * Created by dmilicic on 1/6/16.\n */\npublic class ExpandedCostView extends CardView {\n\n    @Bind(R.id.cost_item_title)\n    TextView mTitle;\n\n    @Bind(R.id.cost_item_total_value)\n    TextView mValue;\n\n    @Bind(R.id.layout_individual_cost_items)\n    LinearLayout mLinearLayout;\n\n    @Nullable\n    private IndividualCostViewClickListener mIndividualCostViewClickListener;\n\n    public ExpandedCostView(Context context) {\n        super(context);\n        init(context);\n    }\n\n    public ExpandedCostView(Context context, AttributeSet attrs) {\n        super(context, attrs);\n        init(context);\n    }\n\n    public ExpandedCostView(Context context, AttributeSet attrs, int defStyleAttr) {\n        super(context, attrs, defStyleAttr);\n        init(context);\n    }\n\n    private void init(Context context) {\n        LayoutInflater inflater = (LayoutInflater) context\n                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);\n        View view = inflater.inflate(R.layout.expanded_cost_item, this);\n\n        ButterKnife.bind(this, view);\n    }\n\n    public void setIndividualCostViewClickListener(\n            @Nullable IndividualCostViewClickListener individualCostViewClickListener) {\n        mIndividualCostViewClickListener = individualCostViewClickListener;\n    }\n\n    private void addCostItem(Cost cost, int position) {\n        CostItemView costView = new CostItemView(getContext(), mIndividualCostViewClickListener, cost);\n\n        // every other cost item will have a different background so its easier on the eyes\n        if (position % 2 == 0) {\n            costView.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.colorBrightGray));\n        }\n\n        mLinearLayout.addView(costView);\n    }\n\n    private void setTitle(Date date) {\n        final String dateText = DateUtils.dateToText(getContext(), date);\n        final String title = String.format(getContext().getString(R.string.total_expenses), dateText);\n        mTitle.setText(title);\n    }\n\n    private void setTotalValue(double value) {\n        String val = String.format(\"%.2f$\", value);\n        mValue.setText(val);\n    }\n\n    public void setDailyCost(DailyTotalCost dailyCost) {\n\n        // reset the individual cost items\n        mLinearLayout.removeAllViews();\n\n        // convert date to text\n        setTitle(dailyCost.getDate());\n\n        setTotalValue(dailyCost.getTotalCost());\n\n        // add the individual cost items\n        List<Cost> costList = dailyCost.getCostList();\n        Cost cost;\n        for (int idx = 0; idx < costList.size(); idx++) {\n            cost = costList.get(idx);\n            addCostItem(cost, idx);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/presentation/ui/fragments/DatePickerFragment.java",
    "content": "package com.kodelabs.mycosts.presentation.ui.fragments;\n\nimport android.app.DatePickerDialog;\nimport android.app.DatePickerDialog.OnDateSetListener;\nimport android.app.Dialog;\nimport android.app.DialogFragment;\nimport android.os.Bundle;\n\nimport java.util.Calendar;\n\n/**\n * Created by dmilicic on 12/20/15.\n */\npublic class DatePickerFragment extends DialogFragment {\n\n    public DatePickerFragment() {\n        // empty\n    }\n\n    private DatePickerDialog.OnDateSetListener mListener;\n\n    public void setListener(OnDateSetListener listener) {\n        mListener = listener;\n    }\n\n    @Override\n    public Dialog onCreateDialog(Bundle savedInstanceState) {\n        // Use the current date as the default date in the picker\n        final Calendar c = Calendar.getInstance();\n        int year = c.get(Calendar.YEAR);\n        int month = c.get(Calendar.MONTH);\n        int day = c.get(Calendar.DAY_OF_MONTH);\n\n        // Create a new instance of DatePickerDialog and return it\n        return new DatePickerDialog(getActivity(), mListener, year, month, day);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/presentation/ui/listeners/IndividualCostViewClickListener.java",
    "content": "package com.kodelabs.mycosts.presentation.ui.listeners;\n\n/**\n * Created by dmilicic on 1/6/16.\n */\npublic interface IndividualCostViewClickListener {\n\n    void onClickDelete(long costId);\n\n    void onClickEdit(long costId);\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/presentation/ui/listeners/RecyclerViewClickListener.java",
    "content": "package com.kodelabs.mycosts.presentation.ui.listeners;\n\n/**\n * Created by dmilicic on 12/26/15.\n */\npublic interface RecyclerViewClickListener {\n\n    void onClickView(int position);\n\n    void onClickEdit(int position, long costId);\n\n    void onClickDelete(int position, long costId);\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/storage/CostRepositoryImpl.java",
    "content": "package com.kodelabs.mycosts.storage;\n\nimport android.content.Context;\n\nimport com.kodelabs.mycosts.domain.model.Cost;\nimport com.kodelabs.mycosts.domain.repository.CostRepository;\nimport com.kodelabs.mycosts.storage.converters.StorageModelConverter;\nimport com.kodelabs.mycosts.storage.model.Cost_Table;\nimport com.kodelabs.mycosts.sync.SyncAdapter;\nimport com.kodelabs.mycosts.utils.DateUtils;\nimport com.raizlabs.android.dbflow.sql.language.SQLite;\n\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * Created by dmilicic on 12/13/15.\n */\npublic class CostRepositoryImpl implements CostRepository {\n\n    private Context mContext;\n\n    // let's generate some dummy data\n    static {\n\n        List<com.kodelabs.mycosts.storage.model.Cost> costs = SQLite.select()\n                .from(com.kodelabs.mycosts.storage.model.Cost.class)\n                .queryList();\n\n        // if the database is empty, let's add some dummies\n        if (costs.size() == 0) {\n\n            // get the today's date for some sample cost items\n            Calendar calendar = Calendar.getInstance();\n            Date today = calendar.getTime();\n            today = DateUtils.truncateHours(today); // set hours, minutes and seconds to 0 for simplicity\n\n            // get yesterday as well\n            calendar.add(Calendar.DATE, -1);\n            Date yesterday = calendar.getTime();\n            yesterday = DateUtils.truncateHours(yesterday); // set hours, minutes and seconds to 0 for simplicity\n\n            // Since each cost is uniquely identified by a timestamp, we should make sure that the sample costs are\n            // not created in the same millisecond, we simply pause a bit after each cost creation.\n            try {\n                com.kodelabs.mycosts.storage.model.Cost cost = new com.kodelabs.mycosts.storage.model.Cost(\"Groceries\", \"Bought some X and some Y\", today, 100.0);\n                cost.insert();\n                Thread.sleep(100);\n                cost = new com.kodelabs.mycosts.storage.model.Cost(\"Bills\", \"Bill for electricity\", today, 50.0);\n                cost.insert();\n                Thread.sleep(100);\n\n\n                Thread.sleep(100);\n                cost = new com.kodelabs.mycosts.storage.model.Cost(\"Transportation\", \"I took an Uber ride\", yesterday, 10.0);\n                cost.insert();\n                Thread.sleep(100);\n                cost = new com.kodelabs.mycosts.storage.model.Cost(\"Entertainment\", \"I went to see Star Wars!\", yesterday, 50.0);\n                cost.insert();\n\n\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n        }\n    }\n\n    public CostRepositoryImpl(Context context) {\n        mContext = context;\n    }\n\n\n    @Override\n    public void insert(Cost item) {\n        com.kodelabs.mycosts.storage.model.Cost dbItem = StorageModelConverter.convertToStorageModel(item);\n\n        // mark as unsynced\n        dbItem.synced = false;\n        dbItem.insert();\n\n        SyncAdapter.triggerSync(mContext);\n    }\n\n    @Override\n    public void update(Cost cost) {\n        com.kodelabs.mycosts.storage.model.Cost dbItem = StorageModelConverter.convertToStorageModel(cost);\n\n        // mark as unsynced\n        dbItem.synced = false;\n        dbItem.update();\n\n        SyncAdapter.triggerSync(mContext);\n    }\n\n    @Override\n    public Cost getCostById(long id) {\n        com.kodelabs.mycosts.storage.model.Cost cost = SQLite\n                .select()\n                .from(com.kodelabs.mycosts.storage.model.Cost.class)\n                .where(Cost_Table.id.eq(id))\n                .querySingle();\n\n        return StorageModelConverter.convertToDomainModel(cost);\n    }\n\n    @Override\n    public List<Cost> getAllCosts() {\n\n        List<com.kodelabs.mycosts.storage.model.Cost> costs = SQLite\n                .select()\n                .from(com.kodelabs.mycosts.storage.model.Cost.class)\n                .queryList();\n\n        return StorageModelConverter.convertListToDomainModel(costs);\n    }\n\n\n    @Override\n    public List<Cost> getAllUnsyncedCosts() {\n\n        List<com.kodelabs.mycosts.storage.model.Cost> costs = SQLite\n                .select()\n                .from(com.kodelabs.mycosts.storage.model.Cost.class)\n                .where(Cost_Table.synced.eq(false))\n                .queryList();\n\n        return StorageModelConverter.convertListToDomainModel(costs);\n    }\n\n    @Override\n    public void markSynced(List<Cost> costs) {\n        // we have to convert it to the database model before storing\n        List<com.kodelabs.mycosts.storage.model.Cost> unsyncedCosts =\n                StorageModelConverter.convertListToStorageModel(costs);\n\n        for (com.kodelabs.mycosts.storage.model.Cost cost : unsyncedCosts) {\n            cost.synced = true;\n            cost.update();\n        }\n    }\n\n    @Override\n    public void delete(Cost cost) {\n        com.kodelabs.mycosts.storage.model.Cost dbItem = StorageModelConverter.convertToStorageModel(cost);\n        dbItem.delete();\n\n        SyncAdapter.triggerSync(mContext);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/storage/contentprovider/StubProvider.java",
    "content": "package com.kodelabs.mycosts.storage.contentprovider;\n\nimport android.content.ContentProvider;\nimport android.content.ContentValues;\nimport android.database.Cursor;\nimport android.net.Uri;\n\n/**\n * Created by dmilicic on 2/11/16.\n * Define an implementation of ContentProvider that stubs out\n * all methods\n */\npublic class StubProvider extends ContentProvider {\n    /*\n     * Always return true, indicating that the\n     * provider loaded correctly.\n     */\n    @Override\n    public boolean onCreate() {\n        return true;\n    }\n\n    /*\n     * Return no type for MIME type\n     */\n    @Override\n    public String getType(Uri uri) {\n        return null;\n    }\n\n    /*\n     * query() always returns no results\n     *\n     */\n    @Override\n    public Cursor query(\n            Uri uri,\n            String[] projection,\n            String selection,\n            String[] selectionArgs,\n            String sortOrder) {\n        return null;\n    }\n\n    /*\n     * insert() always returns null (no URI)\n     */\n    @Override\n    public Uri insert(Uri uri, ContentValues values) {\n        return null;\n    }\n\n    /*\n     * delete() always returns \"no rows affected\" (0)\n     */\n    @Override\n    public int delete(Uri uri, String selection, String[] selectionArgs) {\n        return 0;\n    }\n\n    /*\n     * update() always returns \"no rows affected\" (0)\n     */\n    public int update(\n            Uri uri,\n            ContentValues values,\n            String selection,\n            String[] selectionArgs) {\n        return 0;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/storage/converters/StorageModelConverter.java",
    "content": "package com.kodelabs.mycosts.storage.converters;\n\nimport com.kodelabs.mycosts.storage.model.Cost;\n\nimport java.util.ArrayList;\nimport java.util.Date;\nimport java.util.List;\n\n/**\n * Created by dmilicic on 2/11/16.\n */\npublic class StorageModelConverter {\n\n    public static Cost convertToStorageModel(com.kodelabs.mycosts.domain.model.Cost cost) {\n        Cost result = new Cost();\n        result.setDescription(cost.getDescription());\n        result.setAmount(cost.getAmount());\n        result.setCategory(cost.getCategory());\n        result.setDate(cost.getDate());\n        result.setId(cost.getId());\n\n        return result;\n    }\n\n    public static com.kodelabs.mycosts.domain.model.Cost convertToDomainModel(Cost cost) {\n\n        String desc = cost.getDescription();\n        double amount = cost.getAmount();\n        String category = cost.getCategory();\n        Date date = cost.getDate();\n        long id = cost.getId();\n\n        com.kodelabs.mycosts.domain.model.Cost result = new com.kodelabs.mycosts.domain.model.Cost(\n                category,\n                desc,\n                date,\n                amount,\n                id\n        );\n\n        return result;\n    }\n\n\n    public static List<com.kodelabs.mycosts.domain.model.Cost> convertListToDomainModel(List<Cost> costs) {\n        List<com.kodelabs.mycosts.domain.model.Cost> convertedCosts = new ArrayList<>();\n\n        for (Cost cost : costs) {\n            convertedCosts.add(convertToDomainModel(cost));\n        }\n\n        // cleanup\n        costs.clear();\n        costs = null;\n\n        return convertedCosts;\n    }\n\n\n    public static List<Cost> convertListToStorageModel(List<com.kodelabs.mycosts.domain.model.Cost> costs) {\n        List<Cost> convertedCosts = new ArrayList<>();\n\n        for (com.kodelabs.mycosts.domain.model.Cost cost : costs) {\n            convertedCosts.add(convertToStorageModel(cost));\n        }\n\n        // cleanup\n        costs.clear();\n        costs = null;\n\n        return convertedCosts;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/storage/database/CostDatabase.java",
    "content": "package com.kodelabs.mycosts.storage.database;\n\nimport com.raizlabs.android.dbflow.annotation.Database;\n\n/**\n * Created by dmilicic on 2/11/16.\n */\n@Database(name = CostDatabase.NAME, version = CostDatabase.VERSION)\npublic class CostDatabase {\n    public static final String NAME = \"Costs_db\";\n\n    public static final int VERSION = 1;\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/storage/model/Cost.java",
    "content": "package com.kodelabs.mycosts.storage.model;\n\nimport com.kodelabs.mycosts.storage.database.CostDatabase;\nimport com.raizlabs.android.dbflow.annotation.Column;\nimport com.raizlabs.android.dbflow.annotation.PrimaryKey;\nimport com.raizlabs.android.dbflow.annotation.Table;\nimport com.raizlabs.android.dbflow.structure.BaseModel;\n\nimport java.util.Date;\n\n/**\n * Created by dmilicic on 2/11/16.\n */\n@Table(database = CostDatabase.class)\npublic class Cost extends BaseModel {\n\n\n    @PrimaryKey\n    private long id; // our base model already has an id, let's use it as a primary key\n\n    @Column\n    private String category;\n\n    @Column\n    private String description;\n\n    @Column\n    private Date date;\n\n    @Column\n    private double amount;\n\n    @Column\n    public boolean synced;\n\n    public Cost() {\n    }\n\n    /**\n     * This constructor is only used to create some dummy objects when the app starts.\n     */\n    public Cost(String category, String description, Date date, double amount) {\n        // cost will be \"uniquely\" identified by the current timestamp\n        this.id = new Date().getTime();\n        this.category = category;\n        this.description = description;\n        this.date = date;\n        this.amount = amount;\n        this.synced = false;\n    }\n\n    public long getId() {\n        return id;\n    }\n\n    public void setId(long id) {\n        this.id = id;\n    }\n\n    public String getCategory() {\n        return category;\n    }\n\n    public void setCategory(String category) {\n        this.category = category;\n    }\n\n    public String getDescription() {\n        return description;\n    }\n\n    public void setDescription(String description) {\n        this.description = description;\n    }\n\n    public Date getDate() {\n        return date;\n    }\n\n    public void setDate(Date date) {\n        this.date = date;\n    }\n\n    public double getAmount() {\n        return amount;\n    }\n\n    public void setAmount(double amount) {\n        this.amount = amount;\n    }\n\n\n    @Override\n    public String toString() {\n        return \"Cost{\" +\n                \"id=\" + id +\n                \", category='\" + category + '\\'' +\n                \", description='\" + description + '\\'' +\n                \", date=\" + date +\n                \", amount=\" + amount +\n                '}';\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/sync/SyncAdapter.java",
    "content": "package com.kodelabs.mycosts.sync;\n\nimport android.accounts.Account;\nimport android.content.AbstractThreadedSyncAdapter;\nimport android.content.ContentProviderClient;\nimport android.content.ContentResolver;\nimport android.content.Context;\nimport android.content.SyncResult;\nimport android.os.Bundle;\n\nimport com.kodelabs.mycosts.R;\nimport com.kodelabs.mycosts.domain.model.Cost;\nimport com.kodelabs.mycosts.domain.repository.CostRepository;\nimport com.kodelabs.mycosts.network.RestClient;\nimport com.kodelabs.mycosts.network.converters.RESTModelConverter;\nimport com.kodelabs.mycosts.network.model.Payload;\nimport com.kodelabs.mycosts.network.model.RESTCost;\nimport com.kodelabs.mycosts.network.services.SyncService;\nimport com.kodelabs.mycosts.storage.CostRepositoryImpl;\nimport com.kodelabs.mycosts.utils.AuthUtils;\n\nimport java.io.IOException;\nimport java.util.List;\n\nimport retrofit2.Call;\nimport retrofit2.Callback;\nimport retrofit2.Response;\nimport timber.log.Timber;\n\n/**\n *  * Handle the transfer of data between a server and an\n *  * app, using the Android sync adapter framework.\n *  \n */\npublic class SyncAdapter extends AbstractThreadedSyncAdapter {\n\n    private Context mContext;\n\n    private CostRepository mCostRepository;\n\n    private List<Cost> mUnsyncedCosts;\n\n    public SyncAdapter(Context context, boolean autoInitialize) {\n        super(context, autoInitialize);\n        mContext = context;\n        mCostRepository = new CostRepositoryImpl(mContext);\n    }\n\n    public SyncAdapter(Context context, boolean autoInitialize, boolean allowParallelSyncs) {\n        super(context, autoInitialize, allowParallelSyncs);\n        mContext = context;\n        mCostRepository = new CostRepositoryImpl(mContext);\n    }\n\n    /**\n     * This method will start a sync adapter that will upload data to the server.\n     */\n    public static void triggerSync(Context context) {\n        // TODO sync adapter is forced for debugging purposes, remove this in production\n        // Pass the settings flags by inserting them in a bundle\n        Bundle settingsBundle = new Bundle();\n        settingsBundle.putBoolean(\n                ContentResolver.SYNC_EXTRAS_MANUAL, true);\n        settingsBundle.putBoolean(\n                ContentResolver.SYNC_EXTRAS_EXPEDITED, true);\n\n        // request a sync using sync adapter\n        Account account = AuthUtils.getAccount(context);\n        ContentResolver.requestSync(account, context.getString(R.string.stub_content_authority), settingsBundle);\n    }\n\n\n    private Callback<Void> mResponseCallback = new Callback<Void>() {\n        @Override\n        public void onResponse(Call<Void> call, Response<Void> response) {\n            Timber.i(\"UPLOAD SUCCESS: %d\", response.code());\n\n            if (response.isSuccess()) {\n                mCostRepository.markSynced(mUnsyncedCosts);\n            }\n        }\n\n        @Override\n        public void onFailure(Call<Void> call, Throwable t) {\n            Timber.e(\"UPLOAD FAIL\");\n            t.printStackTrace();\n\n            // try to sync again\n            SyncResult syncResult = new SyncResult();\n        }\n    };\n\n    @Override\n    public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider,\n                              SyncResult syncResult) {\n        Timber.i(\"STARTING SYNC...\");\n\n        // initialize the services we will use\n        SyncService syncService = RestClient.getService(SyncService.class);\n\n        // TODO: get the real user's name\n        Payload payload = new Payload(\"default\");\n\n        // get all unsynced data\n        mUnsyncedCosts = mCostRepository.getAllUnsyncedCosts();\n        for (Cost cost : mUnsyncedCosts) {\n\n            // convert to models suitable for transferring over network\n            RESTCost restCost = RESTModelConverter.convertToRestModel(cost);\n            payload.addCost(restCost);\n        }\n\n        // run the upload\n        try {\n            Response<Void> response = syncService.uploadData(payload).execute();\n\n            Timber.i(\"UPLOAD SUCCESS: %d\", response.code());\n\n            // everything went well, mark local cost items as synced\n            if (response.isSuccess()) {\n                mCostRepository.markSynced(mUnsyncedCosts);\n            }\n\n        } catch (IOException e) { // something went wrong\n            Timber.e(\"UPLOAD FAIL\");\n            // make it a soft error so the framework does the exponential backoff\n            syncResult.stats.numIoExceptions += 1;\n\n            Timber.d(\"Restarting sync in %d seconds\", syncResult.delayUntil);\n        }\n    }\n}"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/sync/SyncService.java",
    "content": "package com.kodelabs.mycosts.sync;\n\nimport android.app.Service;\nimport android.content.Intent;\nimport android.os.IBinder;\nimport android.support.annotation.Nullable;\n\n/**\n * Created by dmilicic on 2/11/16.\n * This service is the component that connects our SyncAdapter to our application as it is in another process.\n */\npublic class SyncService extends Service {\n\n    // Storage for an instance of the sync adapter\n    private static       SyncAdapter sSyncAdapter     = null;\n    // Object to use as a thread-safe lock\n    private static final Object      sSyncAdapterLock = new Object();\n\n    /*\n     * Instantiate the sync adapter object.\n     */\n    @Override\n    public void onCreate() {\n        /*\n         * Create the sync adapter as a singleton.\n         * Set the sync adapter as syncable\n         * Disallow parallel syncs\n         */\n        synchronized (sSyncAdapterLock) {\n            if (sSyncAdapter == null) {\n                sSyncAdapter = new SyncAdapter(getApplicationContext(), true);\n            }\n        }\n    }\n\n    /**\n     * Return an object that allows the system to invoke\n     * the sync adapter.\n     */\n    @Nullable\n    @Override\n    public IBinder onBind(Intent intent) {\n        /*\n         * Get the object that allows external processes\n         * to call onPerformSync(). The object is created\n         * in the base class code when the SyncAdapter\n         * constructors call super()\n         */\n        return sSyncAdapter.getSyncAdapterBinder();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/sync/auth/Authenticator.java",
    "content": "package com.kodelabs.mycosts.sync.auth;\n\nimport android.accounts.AbstractAccountAuthenticator;\nimport android.accounts.Account;\nimport android.accounts.AccountAuthenticatorResponse;\nimport android.accounts.NetworkErrorException;\nimport android.content.Context;\nimport android.os.Bundle;\n\n/**\n * Implement AbstractAccountAuthenticator and stub out all\n * of its methods\n * <p/>\n * We need at least a stub Android authentication so we can use the Android Sync adapter mechanism.\n */\npublic class Authenticator extends AbstractAccountAuthenticator {\n    // Simple constructor\n    public Authenticator(Context context) {\n        super(context);\n    }\n\n    // Editing properties is not supported\n    @Override\n    public Bundle editProperties(\n            AccountAuthenticatorResponse r, String s) {\n        throw new UnsupportedOperationException();\n    }\n\n    // Don't add additional accounts\n    @Override\n    public Bundle addAccount(\n            AccountAuthenticatorResponse r,\n            String s,\n            String s2,\n            String[] strings,\n            Bundle bundle) throws NetworkErrorException {\n        return null;\n    }\n\n    // Ignore attempts to confirm credentials\n    @Override\n    public Bundle confirmCredentials(\n            AccountAuthenticatorResponse r,\n            Account account,\n            Bundle bundle) throws NetworkErrorException {\n        return null;\n    }\n\n    // Getting an authentication token is not supported\n    @Override\n    public Bundle getAuthToken(\n            AccountAuthenticatorResponse r,\n            Account account,\n            String s,\n            Bundle bundle) throws NetworkErrorException {\n        throw new UnsupportedOperationException();\n    }\n\n    // Getting a label for the auth token is not supported\n    @Override\n    public String getAuthTokenLabel(String s) {\n        throw new UnsupportedOperationException();\n    }\n\n    // Updating user credentials is not supported\n    @Override\n    public Bundle updateCredentials(\n            AccountAuthenticatorResponse r,\n            Account account,\n            String s, Bundle bundle) throws NetworkErrorException {\n        throw new UnsupportedOperationException();\n    }\n\n    // Checking features for the account is not supported\n    @Override\n    public Bundle hasFeatures(\n            AccountAuthenticatorResponse r,\n            Account account, String[] strings) throws NetworkErrorException {\n        throw new UnsupportedOperationException();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/sync/auth/AuthenticatorService.java",
    "content": "package com.kodelabs.mycosts.sync.auth;\n\nimport android.app.Service;\nimport android.content.Intent;\nimport android.os.IBinder;\n\n/**\n * A bound Service that instantiates the authenticator\n * when started.\n */\npublic class AuthenticatorService extends Service {\n    /**\n     * Instance field that stores the authenticator object\n     */\n    private Authenticator mAuthenticator;\n\n    @Override\n    public void onCreate() {\n        // Create a new authenticator object\n        mAuthenticator = new Authenticator(this);\n    }\n\n    /**\n     * When the system binds to this Service to make the RPC call\n     * return the authenticator's IBinder.\n     */\n    @Override\n    public IBinder onBind(Intent intent) {\n        return mAuthenticator.getIBinder();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/sync/auth/DummyAccountProvider.java",
    "content": "package com.kodelabs.mycosts.sync.auth;\n\nimport android.accounts.Account;\nimport android.accounts.AccountManager;\nimport android.content.Context;\n\nimport com.kodelabs.mycosts.R;\n\n/**\n * Created by dmilicic on 2/11/16.\n */\npublic class DummyAccountProvider {\n\n    public static Account getDummyAccount(Context context) {\n        final String ACCOUNT = \"dummyaccount\";\n        final String ACCOUNT_TYPE = context.getString(R.string.account_type);\n        final String AUTHORITY = context.getString(R.string.stub_content_authority);\n\n        // Create the account type and default account\n        return new Account(ACCOUNT, ACCOUNT_TYPE);\n    }\n\n    /**\n     * Create a new dummy account for the sync adapter\n     *\n     * @param context The application context\n     */\n    public static boolean CreateSyncAccount(Context context) {\n\n        Account newAccount = getDummyAccount(context);\n\n        // Get an instance of the Android account manager\n        AccountManager accountManager =\n                (AccountManager) context.getSystemService(\n                        Context.ACCOUNT_SERVICE);\n        /*\n         * Add the account and account type, no password or user data\n         * If successful, return the Account object, otherwise report an error.\n         */\n        return accountManager.addAccountExplicitly(newAccount, null, null);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/threading/MainThreadImpl.java",
    "content": "package com.kodelabs.mycosts.threading;\n\nimport android.os.Handler;\nimport android.os.Looper;\n\nimport com.kodelabs.mycosts.domain.executor.MainThread;\n\n/**\n * This class makes sure that the runnable we provide will be run on the main UI thread.\n * <p/>\n * Created by dmilicic on 7/29/15.\n */\npublic class MainThreadImpl implements MainThread {\n\n    private static MainThread sMainThread;\n\n    private Handler mHandler;\n\n    private MainThreadImpl() {\n        mHandler = new Handler(Looper.getMainLooper());\n    }\n\n    @Override\n    public void post(Runnable runnable) {\n        mHandler.post(runnable);\n    }\n\n    public static MainThread getInstance() {\n        if (sMainThread == null) {\n            sMainThread = new MainThreadImpl();\n        }\n\n        return sMainThread;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/utils/AuthUtils.java",
    "content": "package com.kodelabs.mycosts.utils;\n\nimport android.accounts.Account;\nimport android.accounts.AccountManager;\nimport android.content.Context;\n\nimport com.kodelabs.mycosts.R;\n\nimport timber.log.Timber;\n\n/**\n * Created by dmilicic on 8/11/15.\n * <p/>\n * This class will store useful utility methods for managing accounts on Android.\n */\npublic class AuthUtils {\n\n    /**\n     * Create a new dummy account needed by the sync adapter.\n     */\n    public static Account createDummyAccount(Context context) {\n\n        String accountName = \"DummyAccount\";\n        String accountType = context.getString(R.string.account_type);\n\n        // Create the account type and default account\n        Account newAccount = new Account(accountName, accountType);\n\n        // Get an instance of the Android account manager\n        AccountManager accountManager =\n                (AccountManager) context.getSystemService(\n                        Context.ACCOUNT_SERVICE);\n        /*\n         * Add the account and account type, no password or user data\n         * If successful, return the Account object, otherwise report an error.\n         */\n        if (accountManager.addAccountExplicitly(newAccount, null, null)) {\n            Timber.i(\"Account created!\");\n        } else {\n            /*\n             * The account exists or some other error occurred.\n             */\n            Timber.e(\"Account could not be created!\");\n            return null;\n        }\n\n        return newAccount;\n    }\n\n\n    /**\n     * Retrieves an account from the Android system if it exists.\n     *\n     * @param context The context of the application.\n     * @return Returns an existing account or throws an exception if no accounts exist.\n     */\n    public static Account getAccount(Context context) {\n\n        if (context == null)\n            throw new IllegalArgumentException(\"Context is null!\");\n\n        // Get an instance of the Android account manager\n        AccountManager accountManager =\n                (AccountManager) context.getSystemService(\n                        Context.ACCOUNT_SERVICE);\n\n        String accountType = context.getString(R.string.account_type);\n        Account[] accounts = accountManager.getAccountsByType(accountType);\n\n        if (accounts.length == 0)\n            throw new IllegalStateException(\"There are is no account at all!\");\n\n        // return the one and only account\n        return accounts[0];\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/com/kodelabs/mycosts/utils/DateUtils.java",
    "content": "package com.kodelabs.mycosts.utils;\n\nimport android.content.Context;\n\nimport com.kodelabs.mycosts.R;\n\nimport java.text.SimpleDateFormat;\nimport java.util.Calendar;\nimport java.util.Date;\nimport java.util.Locale;\n\n/**\n * Created by dmilicic on 9/20/15.\n */\npublic class DateUtils {\n\n    /**\n     * Converts a date to the textual representation of dates used by people.\n     *\n     * @param date\n     * @return If the date is of today, then this method will return 'Today's'. If its yesterday then 'Yesterday' is returned.\n     * Otherwise it returns the date in the form of dd.mm\n     */\n    public static String dateToText(Context context, Date date) {\n        String textDate;\n\n        // clear hours, minutes and smaller time units from the date\n        date = truncateHours(date);\n\n        Calendar c = Calendar.getInstance();\n\n        // set the calendar to start of today\n        c.set(Calendar.HOUR_OF_DAY, 0);\n        c.set(Calendar.MINUTE, 0);\n        c.set(Calendar.SECOND, 0);\n        c.set(Calendar.MILLISECOND, 0);\n\n        // and get that as a Date\n        Date today = c.getTime();\n\n        // get yesterday\n        c.add(Calendar.DATE, -1);\n        Date yesterday = c.getTime();\n\n\n        if (date.equals(today)) { // test if today\n            textDate = context.getString(R.string.today_s);\n        } else if (date.equals(yesterday)) {  // test if yesterday\n            textDate = context.getString(R.string.yesterday_s);\n        } else {\n            textDate = formatDate(date, new SimpleDateFormat(\"dd.MM\"));\n        }\n\n        return textDate;\n    }\n\n    public static Date createDate(int year, int monthOfYear, int dayOfMonth) {\n        Calendar c = Calendar.getInstance();\n\n        // set the calendar to start of today\n        c.set(Calendar.HOUR_OF_DAY, 0);\n        c.set(Calendar.MINUTE, 0);\n        c.set(Calendar.SECOND, 0);\n        c.set(Calendar.MILLISECOND, 0);\n\n        // setup the date\n        c.set(Calendar.YEAR, year);\n        c.set(Calendar.MONTH, monthOfYear);\n        c.set(Calendar.DAY_OF_MONTH, dayOfMonth);\n\n        // and get that as a Date\n        Date resultDate = c.getTime();\n        return resultDate;\n    }\n\n    public static String formatDate(Date date) {\n        SimpleDateFormat sdf = new SimpleDateFormat(\"dd/MM/yy\", Locale.US);\n        return sdf.format(date);\n    }\n\n    public static String formatDate(Date date, SimpleDateFormat sdf) {\n        return sdf.format(date);\n    }\n\n    public static Date getToday() {\n\n        Calendar c = Calendar.getInstance();\n\n        // set the calendar to start of today\n        c.set(Calendar.HOUR_OF_DAY, 0);\n        c.set(Calendar.MINUTE, 0);\n        c.set(Calendar.SECOND, 0);\n        c.set(Calendar.MILLISECOND, 0);\n\n        // and get that as a Date\n        Date today = c.getTime();\n\n        return today;\n    }\n\n    public static Date truncateHours(Date date) {\n        Calendar c = Calendar.getInstance();\n\n        // set the calendar to start of today\n        c.setTime(date);\n        c.set(Calendar.HOUR_OF_DAY, 0);\n        c.set(Calendar.MINUTE, 0);\n        c.set(Calendar.SECOND, 0);\n        c.set(Calendar.MILLISECOND, 0);\n\n        // and get that as a Date\n        return c.getTime();\n    }\n}\n"
  },
  {
    "path": "app/src/main/res/anim/hold.xml",
    "content": "<set xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:shareInterpolator=\"false\">\n\n    <translate\n        android:fromXDelta=\"0%\"\n        android:fromYDelta=\"0%\"\n        android:toXDelta=\"0%\"\n        android:toYDelta=\"0%\" />\n\n</set>\n"
  },
  {
    "path": "app/src/main/res/drawable/rounded_corner.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<shape xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <solid android:color=\"@color/colorPrimary\" />\n\n    <corners\n        android:bottomLeftRadius=\"0dp\"\n        android:bottomRightRadius=\"0dp\"\n        android:topLeftRadius=\"3dp\"\n        android:topRightRadius=\"3dp\" />\n    <padding\n        android:bottom=\"0dip\"\n        android:left=\"0dip\"\n        android:right=\"0dip\"\n        android:top=\"0dip\" />\n</shape>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_about.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<android.support.design.widget.CoordinatorLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:fitsSystemWindows=\"true\"\n    tools:context=\"com.kodelabs.mycosts.presentation.ui.activities.AboutActivity\">\n\n    <android.support.design.widget.AppBarLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:theme=\"@style/AppTheme.AppBarOverlay\">\n\n        <android.support.v7.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"?attr/colorPrimary\"\n            app:popupTheme=\"@style/AppTheme.PopupOverlay\" />\n\n    </android.support.design.widget.AppBarLayout>\n\n    <include layout=\"@layout/content_about\" />\n\n    <android.support.design.widget.FloatingActionButton\n        android:id=\"@+id/fab\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_gravity=\"bottom|end\"\n        android:layout_margin=\"@dimen/fab_margin\"\n        android:src=\"@android:drawable/ic_dialog_email\" />\n\n</android.support.design.widget.CoordinatorLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_add_cost.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:fitsSystemWindows=\"true\"\n    tools:context=\"com.kodelabs.mycosts.presentation.ui.activities.AddCostActivity\">\n\n    <android.support.design.widget.AppBarLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"wrap_content\"\n        android:theme=\"@style/AppTheme.AppBarOverlay\">\n\n        <android.support.v7.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"?attr/colorPrimary\"\n            app:popupTheme=\"@style/AppTheme.PopupOverlay\" />\n\n    </android.support.design.widget.AppBarLayout>\n\n\n    <include\n        layout=\"@layout/content_add_cost\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/actionBarSize\" />\n\n</FrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<io.codetail.widget.RevealFrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    tools:context=\".presentation.ui.activities.MainActivity\">\n\n    <android.support.v7.widget.Toolbar\n        android:id=\"@+id/toolbar\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"?attr/actionBarSize\"\n        android:background=\"?attr/colorPrimary\"\n        android:theme=\"@style/AppTheme.AppBarOverlay\" />\n\n    <android.support.v7.widget.RecyclerView\n        android:id=\"@+id/expenses_list\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/actionBarSize\">\n\n    </android.support.v7.widget.RecyclerView>\n\n    <!-- This layout will be revealed when FAB is clicked -->\n    <include\n        layout=\"@layout/content_add_cost\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginTop=\"?attr/actionBarSize\" />\n\n</io.codetail.widget.RevealFrameLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/card_daily_cost_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n<android.support.v7.widget.CardView\n    android:id=\"@+id/cost_item_card\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"124dp\"\n    android:layout_marginBottom=\"10dp\"\n    android:layout_marginLeft=\"20dp\"\n    android:layout_marginRight=\"20dp\"\n    android:layout_marginTop=\"20dp\">\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <TextView\n            android:id=\"@+id/cost_item_title\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_margin=\"20dp\"\n            android:text=\"Today's total expenses\"\n            android:textSize=\"15sp\" />\n\n        <TextView\n            android:id=\"@+id/cost_item_total_value\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentBottom=\"true\"\n            android:layout_marginBottom=\"16dp\"\n            android:layout_marginLeft=\"16dp\"\n            android:layout_marginStart=\"16dp\"\n            android:text=\"127$\"\n            android:textSize=\"40sp\" />\n\n\n    </RelativeLayout>\n\n</android.support.v7.widget.CardView>\n"
  },
  {
    "path": "app/src/main/res/layout/card_expanded_daily_cost_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<com.kodelabs.mycosts.presentation.ui.customviews.ExpandedCostView\n    android:id=\"@+id/card_expanded_costview\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:layout_marginBottom=\"10dp\"\n    android:layout_marginLeft=\"20dp\"\n    android:layout_marginRight=\"20dp\"\n    android:layout_marginTop=\"20dp\"\n    app:cardPreventCornerOverlap=\"false\" />"
  },
  {
    "path": "app/src/main/res/layout/content_about.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    app:layout_behavior=\"@string/appbar_scrolling_view_behavior\"\n    tools:context=\"com.kodelabs.mycosts.presentation.ui.activities.AboutActivity\"\n    tools:showIn=\"@layout/activity_about\">\n\n    <TextView\n        android:id=\"@+id/made_by_textview\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_centerInParent=\"true\"\n        android:text=\"@string/made_by\"\n        android:textSize=\"20sp\" />\n\n\n    <TextView\n        android:id=\"@+id/website_textview\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/made_by_textview\"\n        android:layout_centerHorizontal=\"true\"\n        android:autoLink=\"web\"\n        android:text=\"@string/kodelabs_website\"\n        android:textSize=\"20sp\"\n        android:textStyle=\"bold\" />\n\n</RelativeLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/content_add_cost.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<io.codetail.widget.RevealFrameLayout\n    android:id=\"@+id/reveal_layout\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"#fff\"\n    android:visibility=\"visible\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"horizontal\">\n\n            <ImageView\n                android:layout_width=\"24dp\"\n                android:layout_height=\"24dp\"\n                android:layout_column=\"0\"\n                android:layout_gravity=\"center\"\n                android:layout_margin=\"16dp\"\n                android:src=\"@drawable/ic_date_range_black_24dp\" />\n\n\n            <TextView\n                android:id=\"@+id/input_date\"\n                style=\"@android:style/Widget.DeviceDefault.Light.Spinner\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:ems=\"10\"\n                android:hint=\"Enter date\"\n                android:padding=\"20dp\" />\n\n        </LinearLayout>\n\n\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\">\n\n\n            <EditText\n                android:id=\"@+id/input_amount\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:ems=\"10\"\n                android:hint=\"0.00\"\n                android:inputType=\"numberDecimal\"\n                android:padding=\"16dp\" />\n\n            <TextView\n                android:id=\"@+id/textview_amount\"\n                android:layout_width=\"80dp\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentRight=\"true\"\n                android:ems=\"10\"\n                android:hint=\"USD\"\n                android:padding=\"20dp\" />\n\n\n        </RelativeLayout>\n\n\n        <LinearLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:orientation=\"horizontal\">\n\n            <TextView\n                android:id=\"@+id/textview_category\"\n                android:layout_width=\"110dp\"\n                android:layout_height=\"wrap_content\"\n                android:ems=\"10\"\n                android:hint=\"Category:\"\n                android:padding=\"20dp\" />\n\n\n            <Spinner\n                android:id=\"@+id/input_cost_category\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:entries=\"@array/category_array\"\n                android:padding=\"16dp\" />\n\n        </LinearLayout>\n\n\n        <EditText\n            android:id=\"@+id/input_description\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:ems=\"10\"\n            android:hint=\"Enter description\"\n            android:inputType=\"text\"\n            android:padding=\"16dp\" />\n\n\n    </LinearLayout>\n\n\n</io.codetail.widget.RevealFrameLayout>"
  },
  {
    "path": "app/src/main/res/layout/expanded_cost_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<merge xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"130dp\"\n    android:layout_marginBottom=\"10dp\"\n    android:layout_marginLeft=\"20dp\"\n    android:layout_marginRight=\"20dp\"\n    android:layout_marginTop=\"20dp\">\n\n    <LinearLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:orientation=\"vertical\">\n\n        <RelativeLayout\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"65dp\"\n            android:background=\"@drawable/rounded_corner\">\n\n            <TextView\n                android:id=\"@+id/cost_item_title\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_centerVertical=\"true\"\n                android:layout_margin=\"20dp\"\n                android:text=\"Today's total expenses\"\n                android:textColor=\"#fff\"\n                android:textSize=\"17sp\" />\n\n            <TextView\n                android:id=\"@+id/cost_item_total_value\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_alignParentEnd=\"true\"\n                android:layout_alignParentRight=\"true\"\n                android:layout_centerVertical=\"true\"\n                android:layout_marginEnd=\"20dp\"\n                android:layout_marginRight=\"20dp\"\n                android:paddingTop=\"5dp\"\n                android:text=\"127$\"\n                android:textColor=\"#fff\"\n                android:textSize=\"17sp\" />\n\n        </RelativeLayout>\n\n\n        <LinearLayout\n            android:id=\"@+id/layout_individual_cost_items\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_below=\"@id/cost_item_total_value\"\n            android:orientation=\"vertical\">\n\n\n        </LinearLayout>\n\n    </LinearLayout>\n\n\n</merge>"
  },
  {
    "path": "app/src/main/res/layout/individual_cost_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<RelativeLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingBottom=\"16dp\">\n\n    <TextView\n        android:id=\"@+id/cost_item_title\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginLeft=\"16dp\"\n        android:layout_marginStart=\"16dp\"\n        android:layout_marginTop=\"16dp\"\n        android:text=\"Groceries\"\n        android:textSize=\"15sp\" />\n\n\n    <TextView\n        android:id=\"@+id/cost_item_description\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_below=\"@id/cost_item_title\"\n        android:layout_margin=\"16dp\"\n        android:text=\"This description describes a describable item\"\n        android:textSize=\"12sp\" />\n\n    <ImageButton\n        android:id=\"@+id/button_menu\"\n        android:layout_width=\"24dp\"\n        android:layout_height=\"24dp\"\n        android:layout_alignParentEnd=\"true\"\n        android:layout_alignParentRight=\"true\"\n        android:layout_marginEnd=\"16dp\"\n        android:layout_marginRight=\"16dp\"\n        android:layout_marginTop=\"16dp\"\n        android:background=\"@null\"\n        android:src=\"@drawable/ic_vertical_dots\" />\n\n    <TextView\n        android:id=\"@+id/cost_item_total_value\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_alignBaseline=\"@id/cost_item_title\"\n        android:layout_marginEnd=\"25dp\"\n        android:layout_marginRight=\"25dp\"\n        android:layout_toLeftOf=\"@id/button_menu\"\n        android:layout_toStartOf=\"@id/button_menu\"\n        android:text=\"127$\"\n        android:textSize=\"15sp\" />\n\n\n</RelativeLayout>"
  },
  {
    "path": "app/src/main/res/menu/menu_add_cost.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n    <item\n        android:id=\"@+id/action_save\"\n        android:orderInCategory=\"1\"\n        android:title=\"Save\"\n        app:showAsAction=\"always\" />\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/menu_cost_item.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\">\n\n\n    <item\n        android:id=\"@+id/item_edit\"\n        android:title=\"Edit\"\n        android:visible=\"true\"\n        app:showAsAction=\"ifRoom|withText\" />\n\n    <item\n        android:id=\"@+id/item_delete\"\n        android:title=\"Delete\"\n        android:visible=\"true\"\n        app:showAsAction=\"ifRoom|withText\" />\n\n\n</menu>"
  },
  {
    "path": "app/src/main/res/menu/menu_main.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:context=\".presentation.ui.activities.MainActivity\">\n\n    <item\n        android:id=\"@+id/action_add_cost\"\n        android:actionViewClass=\"android.widget.ImageButton\"\n        android:icon=\"@drawable/ic_add_white_24dp\"\n        android:orderInCategory=\"1\"\n        android:title=\"@string/action_add_cost\"\n        app:showAsAction=\"ifRoom\" />\n\n    <item\n        android:id=\"@+id/action_about\"\n        android:orderInCategory=\"100\"\n        android:title=\"@string/action_about\"\n        app:showAsAction=\"never\" />\n</menu>\n"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"colorPrimaryDark\">#303F9F</color>\n    <color name=\"colorAccent\">#FF4081</color>\n    <color name=\"colorBrightGray\">#f1f1f1</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n    <dimen name=\"fab_margin\">16dp</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">My Costs</string>\n\n    <!-- Actions -->\n    <string name=\"action_about\">About</string>\n    <string name=\"action_add_cost\">Add new cost</string>\n\n    <!-- General -->\n    <string name=\"today_s\">Today\\'s</string>\n    <string name=\"yesterday_s\">Yesterday\\'s</string>\n    <string name=\"total_expenses\">%1$s total costs</string>\n    <string name=\"cost\">%1$s cost</string>\n\n    <!-- About -->\n    <string name=\"made_by\">Made by Dario Milicic</string>\n    <string name=\"kodelabs_website\">www.kodelabs.co</string>\n\n    <!-- Titles -->\n    <string name=\"title_activity_add_cost\">Add cost</string>\n    <string name=\"title_activity_about\">About</string>\n\n    <string name=\"account_type\">com.kodelabs.mycosts.account</string>\n    <string name=\"stub_content_authority\">com.kodelabs.mycosts.provider</string>\n\n    <string-array name=\"category_array\">\n        <item>Entertainment</item>\n        <item>Equipment</item>\n        <item>Gifts</item>\n        <item>Groceries</item>\n        <item>Insurance</item>\n        <item>Medical</item>\n        <item>Payment</item>\n        <item>Rent</item>\n        <item>Salary</item>\n        <item>Shopping</item>\n        <item>Tickets</item>\n        <item>Transfer</item>\n        <item>Transportation</item>\n        <item>Utilities</item>\n        <item>Bills</item>\n        <item>Other</item>\n    </string-array>\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n\n        <item name=\"windowActionBar\">false</item>\n        <item name=\"windowNoTitle\">true</item>\n    </style>\n\n    <style name=\"AppTheme.AppBarOverlay\" parent=\"ThemeOverlay.AppCompat.Dark.ActionBar\" />\n\n    <style name=\"AppTheme.PopupOverlay\" parent=\"ThemeOverlay.AppCompat.Light\" />\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-v21/styles.xml",
    "content": "<resources>>\n\n    <style name=\"AppTheme.NoActionBar\">\n        <item name=\"windowActionBar\">false</item>\n        <item name=\"windowNoTitle\">true</item>\n        <item name=\"android:windowDrawsSystemBarBackgrounds\">true</item>\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-w820dp/dimens.xml",
    "content": "<resources>\n    <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n         (such as screen margins) for screens with more than 820dp of available width. This\n         would include 7\" and 10\" devices in landscape (~960dp and ~1280dp respectively). -->\n    <dimen name=\"activity_horizontal_margin\">64dp</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/xml/authenticator.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<account-authenticator xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:accountType=\"@string/account_type\"\n    android:icon=\"@android:drawable/ic_dialog_info\"\n    android:label=\"@string/app_name\"\n    android:smallIcon=\"@android:drawable/ic_dialog_info\" />"
  },
  {
    "path": "app/src/main/res/xml/syncadapter.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<sync-adapter xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:accountType=\"@string/account_type\"\n    android:allowParallelSyncs=\"false\"\n    android:contentAuthority=\"@string/stub_content_authority\"\n    android:isAlwaysSyncable=\"true\"\n    android:supportsUploading=\"true\"\n    android:userVisible=\"false\" />"
  },
  {
    "path": "app/src/test/java/com/kodelabs/mycosts/domain/interactors/GetCostByIdTest.java",
    "content": "package com.kodelabs.mycosts.domain.interactors;\n\nimport com.kodelabs.mycosts.domain.executor.Executor;\nimport com.kodelabs.mycosts.domain.executor.MainThread;\nimport com.kodelabs.mycosts.domain.interactors.impl.GetCostByIdInteractorImpl;\nimport com.kodelabs.mycosts.domain.model.Cost;\nimport com.kodelabs.mycosts.domain.repository.CostRepository;\nimport com.kodelabs.mycosts.threading.TestMainThread;\n\nimport org.junit.Before;\nimport org.junit.Test;\nimport org.mockito.Mock;\nimport org.mockito.Mockito;\nimport org.mockito.MockitoAnnotations;\n\nimport java.util.Date;\n\nimport static org.mockito.Mockito.when;\n\n\n/**\n * Created by dmilicic on 1/8/16.\n */\npublic class GetCostByIdTest {\n\n    private       MainThread                     mMainThread;\n    @Mock private Executor                       mExecutor;\n    @Mock private CostRepository                 mCostRepository;\n    @Mock private GetCostByIdInteractor.Callback mMockedCallback;\n\n    private long mCostId;\n\n    @Before\n    public void setUp() throws Exception {\n        MockitoAnnotations.initMocks(this);\n        mMainThread = new TestMainThread();\n        mCostId = 100; // any number will do as cost ID\n    }\n\n    @Test\n    public void testCostNotFound() throws Exception {\n        GetCostByIdInteractorImpl interactor = new GetCostByIdInteractorImpl(mExecutor, mMainThread, mCostId, mCostRepository, mMockedCallback);\n        interactor.run();\n\n        Mockito.verify(mCostRepository).getCostById(mCostId);\n        Mockito.verifyNoMoreInteractions(mCostRepository);\n        Mockito.verify(mMockedCallback).noCostFound();\n    }\n\n    @Test\n    public void testCostFound() throws Exception {\n\n        Cost dummyCost = new Cost(\"Category\", \"description\", new Date(), 100.0);\n        when(mCostRepository.getCostById(mCostId))\n                .thenReturn(dummyCost);\n\n        GetCostByIdInteractorImpl interactor = new GetCostByIdInteractorImpl(mExecutor, mMainThread, mCostId, mCostRepository, mMockedCallback);\n        interactor.run();\n\n        Mockito.verify(mCostRepository).getCostById(mCostId);\n        Mockito.verifyNoMoreInteractions(mCostRepository);\n        Mockito.verify(mMockedCallback).onCostRetrieved(dummyCost);\n    }\n}\n"
  },
  {
    "path": "app/src/test/java/com/kodelabs/mycosts/presentation/converter/DailyTotalCostConverterTest.java",
    "content": "package com.kodelabs.mycosts.presentation.converter;\n\nimport com.kodelabs.mycosts.domain.model.Cost;\nimport com.kodelabs.mycosts.presentation.model.DailyTotalCost;\nimport com.kodelabs.mycosts.util.TestDateUtil;\n\nimport org.junit.Test;\n\nimport java.util.ArrayList;\nimport java.util.Calendar;\nimport java.util.List;\n\nimport static org.junit.Assert.assertEquals;\n\n/**\n * Created by dmilicic on 1/5/16.\n */\npublic class DailyTotalCostConverterTest {\n\n    private static List<Cost> mCosts;\n\n    @Test\n    public void testDailyCostConversion() throws Exception {\n\n        // init test\n        mCosts = new ArrayList<>();\n        mCosts.add(new Cost(\"Transportation\", \"ZET\", TestDateUtil.getDate(2016, Calendar.JANUARY, 4), 100.0));\n        mCosts.add(new Cost(\"Groceries\", \"ZET\", TestDateUtil.getDate(2016, Calendar.JANUARY, 4), 200.0));\n        mCosts.add(new Cost(\"Entertainment\", \"ZET\", TestDateUtil.getDate(2016, Calendar.JANUARY, 4), 300.0));\n        mCosts.add(new Cost(\"Bills\", \"HEP struja\", TestDateUtil.getDate(2016, Calendar.JANUARY, 4), 400.0));\n\n        mCosts.add(new Cost(\"Transportation\", \"Description\", TestDateUtil.getDate(2016, Calendar.JANUARY, 2), 150.0));\n        mCosts.add(new Cost(\"Transportation\", \"Description\", TestDateUtil.getDate(2016, Calendar.JANUARY, 2), 110.0));\n        mCosts.add(new Cost(\"Transportation\", \"Description\", TestDateUtil.getDate(2016, Calendar.JANUARY, 2), 240.0));\n\n        mCosts.add(new Cost(\"Transportation\", \"Description\", TestDateUtil.getDate(2016, Calendar.JANUARY, 1), 130.0));\n        mCosts.add(new Cost(\"Transportation\", \"Description\", TestDateUtil.getDate(2016, Calendar.JANUARY, 1), 230.0));\n\n        List<DailyTotalCost> dailyTotalCosts = DailyTotalCostConverter.convertCostsToDailyCosts(mCosts);\n\n        // there should be 3 daily cost objects created for 3 different days\n        assertEquals(3, dailyTotalCosts.size());\n\n        // first day should have 4 cost items and a total sum of 1000\n        assertEquals(4, dailyTotalCosts.get(0).getCostList().size());\n        assertEquals(1000.0, dailyTotalCosts.get(0).getTotalCost(), 0.00001);\n\n        // second day should have 3 cost items and a total sum of 500\n        assertEquals(3, dailyTotalCosts.get(1).getCostList().size());\n        assertEquals(500.0, dailyTotalCosts.get(1).getTotalCost(), 0.00001);\n\n        // third day should have 2 cost items and a total sum of 360\n        assertEquals(2, dailyTotalCosts.get(2).getCostList().size());\n        assertEquals(360.0, dailyTotalCosts.get(2).getTotalCost(), 0.00001);\n    }\n\n    @Test\n    public void testDailyCostConversion2() throws Exception {\n\n        // init test\n        mCosts = new ArrayList<>();\n        mCosts.add(new Cost(\"Transportation\", \"ZET\", TestDateUtil.getDate(2016, Calendar.JANUARY, 4), 100.0));\n\n        mCosts.add(new Cost(\"Transportation\", \"Description\", TestDateUtil.getDate(2016, Calendar.JANUARY, 2), 150.0));\n        mCosts.add(new Cost(\"Transportation\", \"Description\", TestDateUtil.getDate(2016, Calendar.JANUARY, 2), 110.0));\n        mCosts.add(new Cost(\"Transportation\", \"Description\", TestDateUtil.getDate(2016, Calendar.JANUARY, 2), 240.0));\n\n        mCosts.add(new Cost(\"Transportation\", \"Description\", TestDateUtil.getDate(2016, Calendar.JANUARY, 1), 130.0));\n        mCosts.add(new Cost(\"Transportation\", \"Description\", TestDateUtil.getDate(2016, Calendar.JANUARY, 1), 230.0));\n\n        List<DailyTotalCost> dailyTotalCosts = DailyTotalCostConverter.convertCostsToDailyCosts(mCosts);\n\n        // there should be 3 daily cost objects created for 3 different days\n        assertEquals(3, dailyTotalCosts.size());\n\n        assertEquals(1, dailyTotalCosts.get(0).getCostList().size());\n        assertEquals(100.0, dailyTotalCosts.get(0).getTotalCost(), 0.00001);\n\n        assertEquals(3, dailyTotalCosts.get(1).getCostList().size());\n        assertEquals(500.0, dailyTotalCosts.get(1).getTotalCost(), 0.00001);\n\n        // third day should have 2 cost items and a total sum of 360\n        assertEquals(2, dailyTotalCosts.get(2).getCostList().size());\n        assertEquals(360.0, dailyTotalCosts.get(2).getTotalCost(), 0.00001);\n    }\n}\n"
  },
  {
    "path": "app/src/test/java/com/kodelabs/mycosts/threading/TestMainThread.java",
    "content": "package com.kodelabs.mycosts.threading;\n\nimport com.kodelabs.mycosts.domain.executor.MainThread;\n\n/**\n * Created by dmilicic on 1/8/16.\n */\npublic class TestMainThread implements MainThread {\n\n    @Override\n    public void post(Runnable runnable) {\n        // tests can run on this thread, no need to invoke other threads\n        runnable.run();\n    }\n}\n"
  },
  {
    "path": "app/src/test/java/com/kodelabs/mycosts/util/TestDateUtil.java",
    "content": "package com.kodelabs.mycosts.util;\n\nimport java.util.Calendar;\nimport java.util.Date;\n\n/**\n * Created by dmilicic on 1/9/16.\n */\npublic class TestDateUtil {\n\n    public static Date getDate(int year, int month, int day) {\n        Calendar calendar = Calendar.getInstance();\n        // set the calendar to start of today\n        calendar.set(Calendar.HOUR_OF_DAY, 0);\n        calendar.set(Calendar.MINUTE, 0);\n        calendar.set(Calendar.SECOND, 0);\n        calendar.set(Calendar.MILLISECOND, 0);\n        calendar.set(year, month, day);\n\n        return calendar.getTime();\n    }\n}\n"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    repositories {\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:1.5.0'\n        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'\n\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        jcenter()\n        maven {\n            url \"https://jitpack.io\"\n        }\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Wed Oct 21 11:34:03 PDT 2015\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-2.8-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\n# Default value: -Xmx10248m -XX:MaxPermSize=256m\n# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windowz variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\ngoto execute\r\n\r\n:4NT_args\r\n@rem Get arguments from the 4NT Shell from JP Software\r\nset CMD_LINE_ARGS=%$\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':app'\n"
  }
]