Skip to content

Commit

Permalink
Merge branch 'release/2.3.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
benoitletondor committed Oct 1, 2020
2 parents 3f423f6 + bf6ac7f commit 9762d28
Show file tree
Hide file tree
Showing 31 changed files with 386 additions and 161 deletions.
109 changes: 55 additions & 54 deletions Android/EasyBudget/app/app.iml

Large diffs are not rendered by default.

49 changes: 29 additions & 20 deletions Android/EasyBudget/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,23 @@ repositories {
}

android {
compileSdkVersion 29
compileSdkVersion 30

defaultConfig {
applicationId "com.benoitletondor.easybudgetapp"
minSdkVersion 21
targetSdkVersion 29
versionCode 72
versionName "2.2.0"
targetSdkVersion 30
versionCode 73
versionName "2.3.0"
vectorDrawables.useSupportLibrary = true

javaCompileOptions {
annotationProcessorOptions {
arguments += [
"room.incremental":"true"
]
}
}
}
buildTypes {
debug {
Expand Down Expand Up @@ -77,36 +85,37 @@ android {
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.2.0'
implementation 'com.google.android.material:material:1.1.0'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.core:core-ktx:1.3.1'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.recyclerview:recyclerview:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
implementation 'androidx.preference:preference-ktx:1.1.1'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
implementation 'androidx.work:work-runtime-ktx:2.3.4'
implementation 'androidx.work:work-gcm:2.3.4'
implementation 'androidx.work:work-runtime-ktx:2.4.0'
implementation 'androidx.work:work-gcm:2.4.0'
implementation 'com.google.android.play:core:1.8.0'

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.6'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.6'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'

implementation 'com.google.firebase:firebase-core:17.4.1'
implementation 'com.google.firebase:firebase-messaging:20.1.7'
implementation 'com.google.firebase:firebase-storage:19.1.1'
implementation 'com.google.firebase:firebase-crashlytics:17.0.0'
implementation 'com.google.firebase:firebase-core:17.5.0'
implementation 'com.google.firebase:firebase-messaging:20.2.4'
implementation 'com.google.firebase:firebase-storage:19.2.0'
implementation 'com.google.firebase:firebase-crashlytics:17.2.1'
implementation 'com.firebaseui:firebase-ui-auth:6.2.0'

implementation 'com.android.billingclient:billing:2.2.0'
implementation 'com.android.billingclient:billing:3.0.0'

implementation project(':caldroid')
implementation 'com.getbase:floatingactionbutton:1.10.1'
implementation 'me.relex:circleindicator:2.1.4@aar'
implementation 'com.batch.android:batch-sdk:1.15.1'

implementation 'org.koin:koin-core:2.1.5'
implementation 'org.koin:koin-android:2.1.5'
implementation 'org.koin:koin-android-viewmodel:2.1.5'
implementation 'org.koin:koin-core:2.2.0-beta-1'
implementation 'org.koin:koin-android:2.2.0-beta-1'
implementation 'org.koin:koin-android-viewmodel:2.2.0-beta-1'

kapt "androidx.room:room-compiler:2.2.5"
implementation "androidx.room:room-runtime:2.2.5"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ private fun Expense.toExpenseEntity() = ExpenseEntity(
title,
amount.getDBValue(),
date,
checked,
associatedRecurringExpense?.id
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import java.util.*
const val DB_NAME = "easybudget.db"

@Database(exportSchema = false,
version = 4,
version = 5,
entities = [
ExpenseEntity::class,
RecurringExpenseEntity::class
Expand All @@ -41,7 +41,7 @@ abstract class RoomDB : RoomDatabase() {
companion object {
fun create(context: Context): RoomDB = Room
.databaseBuilder(context, RoomDB::class.java, DB_NAME)
.addMigrations(migrationFrom1To2, migrationFrom2To3, migrationToRoom)
.addMigrations(migrationFrom1To2, migrationFrom2To3, migrationToRoom, addCheckedField)
.build()
}
}
Expand All @@ -58,6 +58,12 @@ private class TimestampConverters {
}
}

private val addCheckedField = object : Migration(4, 5) {
override fun migrate(database: SupportSQLiteDatabase) {
database.execSQL("ALTER TABLE expense ADD COLUMN checked INTEGER NOT NULL DEFAULT 0")
}
}

private val migrationToRoom = object : Migration(3, 4) {
override fun migrate(database: SupportSQLiteDatabase) {
// No-op, simple migration from SQLite to Room
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ class ExpenseEntity(@PrimaryKey(autoGenerate = true)
val amount: Long,
@ColumnInfo(name = "date")
val date: Date,
@ColumnInfo(name = "checked")
val checked: Boolean,
@ColumnInfo(name = "monthly_id")
val associatedRecurringExpenseId: Long?) {

Expand All @@ -43,6 +45,7 @@ class ExpenseEntity(@PrimaryKey(autoGenerate = true)
title,
amount / 100.0,
date,
checked,
associatedRecurringExpense
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ class IabImpl(context: Context,

private suspend fun querySkuDetails(params: SkuDetailsParams): SkuDetailsResponse = suspendCoroutine { continuation ->
billingClient.querySkuDetailsAsync(params) { billingResult, skuDetailsList ->
continuation.resumeWith(Result.success(SkuDetailsResponse(billingResult, skuDetailsList)))
continuation.resumeWith(Result.success(SkuDetailsResponse(billingResult, skuDetailsList ?: emptyList())))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,27 +24,32 @@ data class Expense(val id: Long?,
val title: String,
val amount: Double,
val date: Date,
val checked: Boolean,
val associatedRecurringExpense: RecurringExpense?) : Parcelable {

constructor(title: String,
amount: Double,
date: Date) : this(null, title, amount, date, null)
date: Date,
checked: Boolean) : this(null, title, amount, date, checked, null)

constructor(id: Long,
title: String,
amount: Double,
date: Date) : this(id, title, amount, date, null)
date: Date,
checked: Boolean) : this(id, title, amount, date, checked, null)

constructor(title: String,
amount: Double,
date: Date,
associatedRecurringExpense: RecurringExpense) : this(null, title, amount, date, associatedRecurringExpense)
checked: Boolean,
associatedRecurringExpense: RecurringExpense) : this(null, title, amount, date, checked, associatedRecurringExpense)

private constructor(parcel: Parcel) : this(
parcel.readValue(Long::class.java.classLoader) as? Long,
parcel.readString()!!,
parcel.readDouble(),
Date(parcel.readLong()),
parcel.readInt() == 1,
parcel.readParcelable(RecurringExpense::class.java.classLoader)
)

Expand All @@ -63,6 +68,7 @@ data class Expense(val id: Long?,
parcel.writeString(title)
parcel.writeDouble(amount)
parcel.writeLong(date.time)
parcel.writeInt(if( checked ) { 1 } else { 0 })
parcel.writeParcelable(associatedRecurringExpense, flags)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

package com.benoitletondor.easybudgetapp.view

import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
Expand All @@ -27,15 +28,18 @@ import com.benoitletondor.easybudgetapp.helper.*
import com.benoitletondor.easybudgetapp.parameters.Parameters
import com.benoitletondor.easybudgetapp.parameters.hasUserCompleteRating
import com.benoitletondor.easybudgetapp.parameters.setUserHasCompleteRating
import com.google.android.play.core.review.ReviewManagerFactory

/**
* Rating popup that ask user for feedback and redirect them to the PlayStore
*
* @author Benoit LETONDOR
*/
class RatingPopup(private val context: Context,
class RatingPopup(private val activity: Activity,
private val parameters: Parameters) {

private val reviewManager = ReviewManagerFactory.create(activity.applicationContext)

/**
* Show the rating popup to the user
*
Expand Down Expand Up @@ -70,7 +74,7 @@ class RatingPopup(private val context: Context,
* @return A ready to be shown [AlertDialog]
*/
private fun buildStep1(includeDontAskMeAgainButton: Boolean): AlertDialog {
val builder = AlertDialog.Builder(context)
val builder = AlertDialog.Builder(activity)
.setTitle(R.string.rating_popup_question_title)
.setMessage(R.string.rating_popup_question_message)
.setNegativeButton(R.string.rating_popup_question_cta_negative) { _, _ ->
Expand Down Expand Up @@ -99,7 +103,7 @@ class RatingPopup(private val context: Context,
* @return A ready to be shown [AlertDialog]
*/
private fun buildNegativeStep(): AlertDialog {
return AlertDialog.Builder(context)
return AlertDialog.Builder(activity)
.setTitle(R.string.rating_popup_negative_title)
.setMessage(R.string.rating_popup_negative_message)
.setNegativeButton(R.string.rating_popup_negative_cta_negative) { _, _ ->
Expand All @@ -113,14 +117,14 @@ class RatingPopup(private val context: Context,
val sendIntent = Intent()
sendIntent.action = Intent.ACTION_SENDTO
sendIntent.data = Uri.parse("mailto:") // only email apps should handle this
sendIntent.putExtra(Intent.EXTRA_EMAIL, arrayOf(context.resources.getString(R.string.rating_feedback_email)))
sendIntent.putExtra(Intent.EXTRA_TEXT, context.resources.getString(R.string.rating_feedback_send_text))
sendIntent.putExtra(Intent.EXTRA_SUBJECT, context.resources.getString(R.string.rating_feedback_send_subject))
sendIntent.putExtra(Intent.EXTRA_EMAIL, arrayOf(activity.resources.getString(R.string.rating_feedback_email)))
sendIntent.putExtra(Intent.EXTRA_TEXT, activity.resources.getString(R.string.rating_feedback_send_text))
sendIntent.putExtra(Intent.EXTRA_SUBJECT, activity.resources.getString(R.string.rating_feedback_send_subject))

if (sendIntent.resolveActivity(context.packageManager) != null) {
context.startActivity(sendIntent)
if (sendIntent.resolveActivity(activity.packageManager) != null) {
activity.startActivity(sendIntent)
} else {
Toast.makeText(context, context.resources.getString(R.string.rating_feedback_send_error), Toast.LENGTH_SHORT).show()
Toast.makeText(activity, activity.resources.getString(R.string.rating_feedback_send_error), Toast.LENGTH_SHORT).show()
}
}
.create()
Expand All @@ -132,7 +136,7 @@ class RatingPopup(private val context: Context,
* @return A ready to be shown [AlertDialog]
*/
private fun buildPositiveStep(): AlertDialog {
return AlertDialog.Builder(context)
return AlertDialog.Builder(activity)
.setTitle(R.string.rating_popup_positive_title)
.setMessage(R.string.rating_popup_positive_message)
.setNegativeButton(R.string.rating_popup_positive_cta_negative) { _, _ ->
Expand All @@ -143,19 +147,36 @@ class RatingPopup(private val context: Context,
parameters.setRatingPopupStep(RatingPopupStep.STEP_LIKE_RATED)
parameters.setUserHasCompleteRating()

val appPackageName = context.packageName
reviewManager.requestReviewFlow()
.addOnCompleteListener { request ->
if (request.isSuccessful) {
val reviewInfo = request.result
reviewManager.launchReviewFlow(activity, reviewInfo)
.addOnCompleteListener { result ->
if( !result.isSuccessful ) {
redirectToPlayStoreForRating()
}
}
} else {
redirectToPlayStoreForRating()
}
}
}
.create()
}

private fun redirectToPlayStoreForRating() {
val appPackageName = activity.packageName

try {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$appPackageName"))
try {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$appPackageName"))

context.startActivity(intent)
} catch (e: ActivityNotFoundException) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=$appPackageName"))
activity.startActivity(intent)
} catch (e: ActivityNotFoundException) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=$appPackageName"))

context.startActivity(intent)
}
}
.create()
activity.startActivity(intent)
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class ExpenseEditViewModel(private val db: DB,
title = description,
amount = if (isRevenue) -value else value,
date = date
) ?: Expense(description, if (isRevenue) -value else value, date)
) ?: Expense(description, if (isRevenue) -value else value, date, false)

db.persistExpense(expense)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import android.content.Intent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.CheckBox
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AlertDialog
Expand All @@ -29,7 +30,9 @@ import androidx.core.content.ContextCompat
import androidx.localbroadcastmanager.content.LocalBroadcastManager
import androidx.recyclerview.widget.RecyclerView
import com.benoitletondor.easybudgetapp.R
import com.benoitletondor.easybudgetapp.db.DB
import com.benoitletondor.easybudgetapp.helper.CurrencyHelper
import com.benoitletondor.easybudgetapp.iab.Iab
import com.benoitletondor.easybudgetapp.model.Expense
import com.benoitletondor.easybudgetapp.model.RecurringExpenseDeleteType
import com.benoitletondor.easybudgetapp.model.RecurringExpenseType
Expand All @@ -45,7 +48,9 @@ import java.util.*
*/
class ExpensesRecyclerViewAdapter(private val activity: Activity,
private val parameters: Parameters,
private var date: Date) : RecyclerView.Adapter<ExpensesRecyclerViewAdapter.ViewHolder>() {
private val iab: Iab,
private var date: Date,
private val onExpenseCheckedListener: (Expense, Boolean) -> Unit) : RecyclerView.Adapter<ExpensesRecyclerViewAdapter.ViewHolder>() {

private var expenses = mutableListOf<Expense>()

Expand Down Expand Up @@ -99,6 +104,13 @@ class ExpensesRecyclerViewAdapter(private val activity: Activity,
viewHolder.expenseAmountTextView.setTextColor(ContextCompat.getColor(viewHolder.view.context, if (expense.isRevenue()) R.color.budget_green else R.color.budget_red))
viewHolder.recurringIndicator.visibility = if (expense.isRecurring()) View.VISIBLE else View.GONE
viewHolder.positiveIndicator.setImageResource(if (expense.isRevenue()) R.drawable.ic_label_green else R.drawable.ic_label_red)
viewHolder.checkedCheckBox.visibility = if( iab.isUserPremium() ) { View.VISIBLE } else { View.GONE }
viewHolder.checkedCheckBox.setOnCheckedChangeListener { _, checked ->
if( checked != expense.checked ) {
onExpenseCheckedListener(expense, checked)
}
}
viewHolder.checkedCheckBox.isChecked = expense.checked

if (expense.isRecurring()) {
when (expense.associatedRecurringExpense!!.type) {
Expand Down Expand Up @@ -216,5 +228,6 @@ class ExpensesRecyclerViewAdapter(private val activity: Activity,
val recurringIndicator: ViewGroup = view.findViewById(R.id.recurring_indicator)
val recurringIndicatorTextview: TextView = view.findViewById(R.id.recurring_indicator_textview)
val positiveIndicator: ImageView = view.findViewById(R.id.positive_indicator)
val checkedCheckBox: CheckBox = view.findViewById(R.id.expense_checked)
}
}
Loading

0 comments on commit 9762d28

Please sign in to comment.