Use Crashlytics for error handling and fix up navigation

This commit is contained in:
Billy Brawner 2019-01-26 22:10:03 -06:00
parent f3e456ac28
commit ba92bd850b
11 changed files with 95 additions and 58 deletions

2
.gitignore vendored
View file

@ -11,4 +11,4 @@
*.log *.log
.idea/ .idea/
*~ *~
acra.properties */google-services.json

View file

@ -2,30 +2,24 @@ apply plugin: 'com.android.application'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android-extensions'
apply plugin: 'io.fabric'
Properties acra = new Properties()
acra.load(new FileInputStream("app/acra.properties"))
android { android {
compileSdkVersion 27 compileSdkVersion 28
compileOptions { compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
defaultConfig { defaultConfig {
applicationId "com.wbrawner.budget" applicationId "com.wbrawner.budget"
minSdkVersion 24 minSdkVersion 21
targetSdkVersion 27 targetSdkVersion 28
versionCode 1 versionCode 1
versionName "1.0" versionName "1.0"
vectorDrawables { vectorDrawables {
useSupportLibrary = true useSupportLibrary = true
} }
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
buildConfigField "String", "ACRA_URL", "\"${acra.getProperty("url")}\""
buildConfigField "String", "ACRA_USER", "\"${acra.getProperty("user")}\""
buildConfigField "String", "ACRA_PASS", "\"${acra.getProperty("pass")}\""
javaCompileOptions { javaCompileOptions {
annotationProcessorOptions { annotationProcessorOptions {
arguments = ["room.schemaLocation": "$projectDir/schemas".toString()] arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
@ -46,10 +40,13 @@ android {
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:27.1.1' implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:support-compat:28.0.0'
implementation 'com.android.support:support-media-compat:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3' implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'com.android.support:design:27.1.1' implementation 'com.android.support:design:28.0.0'
implementation "com.android.support:support-emoji-bundled:27.1.1" implementation "com.android.support:support-emoji-bundled:28.0.0"
implementation 'com.github.BlacKCaT27:CurrencyEditText:2.0.2' implementation 'com.github.BlacKCaT27:CurrencyEditText:2.0.2'
def room_version = "1.1.1" def room_version = "1.1.1"
def lifecycle_version = "1.1.1" def lifecycle_version = "1.1.1"
@ -60,9 +57,11 @@ dependencies {
androidTestImplementation "android.arch.persistence.room:testing:$room_version" androidTestImplementation "android.arch.persistence.room:testing:$room_version"
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
// ACRA Crash reporting
def acraVersion = '5.1.3'
implementation "ch.acra:acra-http:$acraVersion"
androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.google.firebase:firebase-core:16.0.6'
implementation 'com.crashlytics.sdk.android:crashlytics:2.9.8'
} }
apply plugin: 'com.google.gms.google-services'

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.wbrawner.budget"> package="com.wbrawner.budget">
<application <application
@ -9,7 +10,8 @@
android:label="@string/app_name" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme"> android:theme="@style/AppTheme"
tools:ignore="GoogleAppIndexingWarning">
<activity android:name="com.wbrawner.budget.ui.MainActivity"> <activity android:name="com.wbrawner.budget.ui.MainActivity">
<intent-filter android:label="@string/app_name_short"> <intent-filter android:label="@string/app_name_short">
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
@ -20,8 +22,20 @@
android:name="android.app.shortcuts" android:name="android.app.shortcuts"
android:resource="@xml/shortcuts" /> android:resource="@xml/shortcuts" />
</activity> </activity>
<activity android:name="com.wbrawner.budget.ui.transactions.AddEditTransactionActivity" /> <activity
<activity android:name="com.wbrawner.budget.ui.categories.AddEditCategoryActivity" /> android:name="com.wbrawner.budget.ui.transactions.AddEditTransactionActivity"
android:parentActivityName=".ui.MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.MainActivity" />
</activity>
<activity
android:name="com.wbrawner.budget.ui.categories.AddEditCategoryActivity"
android:parentActivityName=".ui.MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".ui.MainActivity" />
</activity>
</application> </application>
</manifest> </manifest>

View file

@ -2,32 +2,21 @@ package com.wbrawner.budget
import android.app.Application import android.app.Application
import android.arch.persistence.room.Room import android.arch.persistence.room.Room
import android.content.Context
import com.wbrawner.budget.data.dao.TransactionDao
import com.wbrawner.budget.data.BudgetDatabase import com.wbrawner.budget.data.BudgetDatabase
import com.wbrawner.budget.data.dao.CategoryDao import com.wbrawner.budget.data.dao.CategoryDao
import com.wbrawner.budget.data.dao.TransactionDao
import com.wbrawner.budget.data.migrations.MIGRATION_1_2 import com.wbrawner.budget.data.migrations.MIGRATION_1_2
import com.wbrawner.budget.data.migrations.MIGRATION_2_3 import com.wbrawner.budget.data.migrations.MIGRATION_2_3
import org.acra.ACRA
import org.acra.annotation.AcraCore
import org.acra.annotation.AcraHttpSender
import org.acra.data.StringFormat
import org.acra.sender.HttpSender
@AcraCore(reportFormat = StringFormat.JSON) class AllowanceApplication : Application() {
@AcraHttpSender(uri = BuildConfig.ACRA_URL,
basicAuthLogin = BuildConfig.ACRA_USER,
basicAuthPassword = BuildConfig.ACRA_PASS,
httpMethod = HttpSender.Method.POST)
class AllowanceApplication: Application() {
lateinit var database: BudgetDatabase lateinit var database: BudgetDatabase
private set private set
lateinit var transactionDao: TransactionDao lateinit var transactionDao: TransactionDao
private set private set
lateinit var categoryDao: CategoryDao lateinit var categoryDao: CategoryDao
private set private set
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
@ -39,10 +28,4 @@ class AllowanceApplication: Application() {
transactionDao = database.transactionDao() transactionDao = database.transactionDao()
categoryDao = database.categoryDao() categoryDao = database.categoryDao()
} }
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
if (!BuildConfig.DEBUG) ACRA.init(this)
}
} }

View file

@ -18,7 +18,7 @@ interface TransactionDao {
@Query("SELECT * FROM `Transaction` WHERE id = :id") @Query("SELECT * FROM `Transaction` WHERE id = :id")
fun load(id: Int): LiveData<TransactionWithCategory> fun load(id: Int): LiveData<TransactionWithCategory>
@Query("SELECT * FROM `Transaction` LIMIT :count") @Query("SELECT * FROM `Transaction` ORDER BY date DESC LIMIT :count")
fun loadMultiple(count: Int): LiveData<List<TransactionWithCategory>> fun loadMultiple(count: Int): LiveData<List<TransactionWithCategory>>
@Query("SELECT (SELECT TOTAL(amount) from `Transaction` WHERE isExpense = 0) - (SELECT TOTAL(amount) from `Transaction` WHERE isExpense = 1)") @Query("SELECT (SELECT TOTAL(amount) from `Transaction` WHERE isExpense = 0) - (SELECT TOTAL(amount) from `Transaction` WHERE isExpense = 1)")

View file

@ -2,12 +2,14 @@ package com.wbrawner.budget.ui.categories
import android.arch.lifecycle.Observer import android.arch.lifecycle.Observer
import android.arch.lifecycle.ViewModelProviders import android.arch.lifecycle.ViewModelProviders
import android.content.Intent
import android.graphics.Color import android.graphics.Color
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.NavUtils
import android.support.v4.app.TaskStackBuilder
import android.support.v7.app.AppCompatActivity import android.support.v7.app.AppCompatActivity
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.View
import com.wbrawner.budget.R import com.wbrawner.budget.R
import com.wbrawner.budget.data.model.Category import com.wbrawner.budget.data.model.Category
import kotlinx.android.synthetic.main.activity_add_edit_category.* import kotlinx.android.synthetic.main.activity_add_edit_category.*
@ -55,7 +57,21 @@ class AddEditCategoryActivity : AppCompatActivity() {
override fun onOptionsItemSelected(item: MenuItem?): Boolean { override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when (item?.itemId) { when (item?.itemId) {
android.R.id.home -> onBackPressed() android.R.id.home -> {
val upIntent: Intent? = NavUtils.getParentActivityIntent(this)
when {
upIntent == null -> throw IllegalStateException("No Parent Activity Intent")
NavUtils.shouldUpRecreateTask(this, upIntent) || isTaskRoot -> {
TaskStackBuilder.create(this)
.addNextIntentWithParentStack(upIntent)
.startActivities()
}
else -> {
NavUtils.navigateUpTo(this, upIntent)
}
}
}
R.id.action_save -> { R.id.action_save -> {
if (!validateFields()) return true if (!validateFields()) return true
viewModel.saveCategory(Category( viewModel.saveCategory(Category(
@ -79,7 +95,7 @@ class AddEditCategoryActivity : AppCompatActivity() {
private fun validateFields(): Boolean { private fun validateFields(): Boolean {
var errors = false var errors = false
if (edit_category_name.text.isEmpty()) { if (edit_category_name.text?.isEmpty() == true) {
edit_category_name.error = getString(R.string.required_field_name) edit_category_name.error = getString(R.string.required_field_name)
errors = true errors = true
} }

View file

@ -51,7 +51,7 @@ class CategoryListFragment : Fragment() {
} }
}) })
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
if (dy > 0) fab.hide() else fab.show() if (dy > 0) fab.hide() else fab.show()
} }
}) })

View file

@ -2,7 +2,10 @@ package com.wbrawner.budget.ui.transactions
import android.arch.lifecycle.Observer import android.arch.lifecycle.Observer
import android.arch.lifecycle.ViewModelProviders import android.arch.lifecycle.ViewModelProviders
import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.support.v4.app.NavUtils
import android.support.v4.app.TaskStackBuilder
import android.support.v7.app.AppCompatActivity import android.support.v7.app.AppCompatActivity
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
@ -95,7 +98,7 @@ class AddEditTransactionActivity : AppCompatActivity() {
override fun onOptionsItemSelected(item: MenuItem?): Boolean { override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when (item?.itemId) { when (item?.itemId) {
android.R.id.home -> onBackPressed() android.R.id.home -> onNavigateUp()
R.id.action_save -> { R.id.action_save -> {
val field = edit_transaction_date val field = edit_transaction_date
val cal = Calendar.getInstance() val cal = Calendar.getInstance()
@ -111,16 +114,33 @@ class AddEditTransactionActivity : AppCompatActivity() {
categoryId = (edit_transaction_category.selectedItem as TransactionCategory).id, categoryId = (edit_transaction_category.selectedItem as TransactionCategory).id,
remoteId = null remoteId = null
)) ))
finish() onNavigateUp()
} }
R.id.action_delete -> { R.id.action_delete -> {
viewModel.deleteTransactionById(this@AddEditTransactionActivity.id!!) viewModel.deleteTransactionById(this@AddEditTransactionActivity.id!!)
finish() onNavigateUp()
} }
} }
return true return true
} }
override fun onNavigateUp(): Boolean {
val upIntent: Intent? = NavUtils.getParentActivityIntent(this)
when {
upIntent == null -> throw IllegalStateException("No Parent Activity Intent")
NavUtils.shouldUpRecreateTask(this, upIntent) || isTaskRoot -> {
TaskStackBuilder.create(this)
.addNextIntentWithParentStack(upIntent)
.startActivities()
}
else -> {
NavUtils.navigateUpTo(this, upIntent)
}
}
return true
}
companion object { companion object {
const val EXTRA_TRANSACTION_ID = "EXTRA_TRANSACTION_ID" const val EXTRA_TRANSACTION_ID = "EXTRA_TRANSACTION_ID"

View file

@ -51,7 +51,7 @@ class TransactionListFragment : Fragment() {
} }
}) })
recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
if (dy > 0) fab.hide() else fab.show() if (dy > 0) fab.hide() else fab.show()
} }
}) })

View file

@ -1,17 +1,19 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { buildscript {
ext.kotlin_version = '1.3.0' ext.kotlin_version = '1.3.11'
repositories { repositories {
google() google()
jcenter() jcenter()
maven {
url 'https://maven.fabric.io/public'
}
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.2.1' classpath 'com.android.tools.build:gradle:3.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.google.gms:google-services:4.2.0'
// NOTE: Do not place your application dependencies here; they belong classpath 'io.fabric.tools:gradle:1.26.1'
// in the individual module build.gradle files
} }
} }
@ -20,6 +22,9 @@ allprojects {
google() google()
jcenter() jcenter()
maven { url "https://jitpack.io" } maven { url "https://jitpack.io" }
maven {
url 'https://maven.google.com/'
}
} }
} }

View file

@ -1,6 +1,6 @@
#Mon Jul 30 21:54:00 CDT 2018 #Sat Jan 26 20:48:01 CST 2019
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.1-all.zip