realmasync
Laurent 3 years ago
commit c0b022a553
  1. 7
      app/build.gradle
  2. 4
      app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt
  3. 61
      app/src/main/java/net/pokeranalytics/android/api/CurrencyConverterApi.kt
  4. 46
      app/src/main/java/net/pokeranalytics/android/api/FreeConverterApi.kt
  5. 1
      app/src/main/java/net/pokeranalytics/android/model/filter/Query.kt
  6. 1
      app/src/main/java/net/pokeranalytics/android/model/filter/QueryCondition.kt
  7. 15
      app/src/main/java/net/pokeranalytics/android/model/migrations/Patcher.kt
  8. 6
      app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt
  9. 1
      app/src/main/java/net/pokeranalytics/android/model/realm/FilterCondition.kt
  10. 8
      app/src/main/java/net/pokeranalytics/android/model/realm/Transaction.kt
  11. 2
      app/src/main/java/net/pokeranalytics/android/ui/activity/ImportActivity.kt
  12. 314
      app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt
  13. 7
      app/src/main/java/net/pokeranalytics/android/ui/fragment/CurrenciesFragment.kt
  14. 61
      app/src/main/java/net/pokeranalytics/android/ui/fragment/SettingsFragment.kt
  15. 11
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/BaseFragment.kt
  16. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/LoaderDialogFragment.kt
  17. 8
      app/src/main/java/net/pokeranalytics/android/ui/fragment/report/AbstractReportFragment.kt
  18. 1
      app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/CalendarDetailsFragment.kt
  19. 2
      app/src/main/java/net/pokeranalytics/android/ui/modules/calendar/CalendarFragment.kt
  20. 28
      app/src/main/java/net/pokeranalytics/android/ui/modules/data/BankrollDataFragment.kt
  21. 2
      app/src/main/java/net/pokeranalytics/android/ui/modules/data/PlayerDataFragment.kt
  22. 1
      app/src/main/java/net/pokeranalytics/android/ui/view/rows/FilterSectionRow.kt
  23. 25
      app/src/main/java/net/pokeranalytics/android/util/Preferences.kt
  24. 2
      app/src/main/res/values-de/strings.xml
  25. 2
      app/src/main/res/values-es/strings.xml
  26. 2
      app/src/main/res/values-fr/strings.xml
  27. 2
      app/src/main/res/values-hi/strings.xml
  28. 2
      app/src/main/res/values-it/strings.xml
  29. 2
      app/src/main/res/values-ja/strings.xml
  30. 2
      app/src/main/res/values-pt/strings.xml
  31. 2
      app/src/main/res/values-ru/strings.xml
  32. 2
      app/src/main/res/values-zh/strings.xml
  33. 2
      app/src/main/res/values/strings.xml
  34. 5
      build.gradle

@ -6,7 +6,9 @@ apply plugin: 'realm-android'
// Crashlytics
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'
//////////////
// Serialization
apply plugin: "kotlinx-serialization"
repositories {
maven { url 'https://jitpack.io' } // required for MPAndroidChart
@ -99,7 +101,8 @@ dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.6'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.6"
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.20.0") // JVM dependency
// implementation("org.jetbrains.kotlinx:kotlinx-serialization-runtime:0.20.0") // JVM dependency
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.4.1"
// Android
implementation 'androidx.appcompat:appcompat:1.1.0'

@ -39,7 +39,9 @@ class PokerAnalyticsApplication : Application() {
override fun onCreate() {
super.onCreate()
FirebaseApp.initializeApp(this)
if (!BuildConfig.DEBUG) {
FirebaseApp.initializeApp(this)
}
UserDefaults.init(this)

@ -0,0 +1,61 @@
package net.pokeranalytics.android.api
import android.content.Context
import com.android.volley.VolleyError
import com.android.volley.toolbox.StringRequest
import com.android.volley.toolbox.Volley
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import timber.log.Timber
@Serializable
data class RateResponse(var info: RateInfo)
@Serializable
data class RateInfo(var rate: Double)
class CurrencyConverterApi {
companion object {
val json = Json { ignoreUnknownKeys = true }
fun currencyRate(fromCurrency: String, toCurrency: String, context: Context, callback: (Double?, VolleyError?) -> (Unit)) {
val queue = Volley.newRequestQueue(context)
// val url = "https://free.currconv.com/api/v7/convert?q=${pair}&compact=ultra&apiKey=9b56e742a75392c8aeb7"
val url = "https://api.apilayer.com/exchangerates_data/convert?to=$toCurrency&from=$fromCurrency&amount=1"
// https://free.currconv.com/api/v7/convert?q=GBP_USD&compact=ultra&apiKey=5ba8d38995282fe8b1c8
// { "USD_PHP": 44.1105, "PHP_USD": 0.0227 }
Timber.d("Api call = $url")
val stringRequest = object : StringRequest(
Method.GET, url,
{ response ->
val o = json.decodeFromString<RateResponse>(response)
Timber.d("rate = ${o.info.rate}")
callback(o.info.rate, null)
},
{
Timber.d("Api call failed: ${it.message}")
callback(null, it)
}) {
override fun getHeaders(): MutableMap<String, String> {
val headers = HashMap<String, String>()
headers["apikey"] = "XnfeyID3PMKd3k4zTPW0XmZAbcZlZgqH"
return headers
}
}
queue.add(stringRequest)
}
}
}

@ -1,46 +0,0 @@
package net.pokeranalytics.android.api
import android.content.Context
import com.android.volley.Request
import com.android.volley.toolbox.StringRequest
import com.android.volley.toolbox.Volley
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration
import timber.log.Timber
class FreeConverterApi {
companion object {
fun currencyRate(pair: String, context: Context, callback: (Double) -> (Unit)) {
val queue = Volley.newRequestQueue(context)
val url = "https://free.currconv.com/api/v7/convert?q=${pair}&compact=ultra&apiKey=9b56e742a75392c8aeb7"
// https://free.currconv.com/api/v7/convert?q=GBP_USD&compact=ultra&apiKey=5ba8d38995282fe8b1c8
// { "USD_PHP": 44.1105, "PHP_USD": 0.0227 }
val stringRequest = StringRequest(
Request.Method.GET, url,
{ response ->
val json = Json(JsonConfiguration.Stable)
val f = json.parseJson(response)
f.jsonObject[pair]?.primitive?.double?.let { rate ->
callback(rate)
} ?: run {
Timber.d("no rate: $response")
}
},
{
Timber.d("Api call failed: ${it.message}")
})
queue.add(stringRequest)
}
}
}

@ -119,6 +119,7 @@ class Query {
is QueryCondition.QueryDataCondition<*> -> {
c.objectId?.let { return it }
}
else -> {}
}
}
return null

@ -848,6 +848,7 @@ sealed class QueryCondition : RowRepresentable {
}
return realmQuery
}
else -> {}
}
if (this is CustomFieldRelated) {

@ -57,6 +57,10 @@ class Patcher {
patchZeroTable()
}
Preferences.executeOnce(Preferences.Keys.PATCH_RATED_AMOUNT, context) {
patchRatedAmounts()
}
patchPerformances(application)
}
@ -215,5 +219,16 @@ class Patcher {
realm.close()
}
private fun patchRatedAmounts() {
val realm = Realm.getDefaultInstance()
val transactions = realm.where<Transaction>().findAll()
realm.executeTransaction {
transactions.forEach { t ->
t.computeRatedAmount()
}
}
realm.close()
}
}
}

@ -326,6 +326,12 @@ class PokerAnalyticsMigration : RealmMigration {
ucs.addField("transactionTypeIds", String::class.java).setRequired("transactionTypeIds", true)
} ?: throw PAIllegalStateException("UserConfig schema not found")
schema.get("Performance")?.let { ps ->
if (!ps.isPrimaryKey("id")) {
ps.addPrimaryKey("id")
}
}
currentVersion++
}

@ -29,6 +29,7 @@ open class FilterCondition() : RealmObject() {
is QueryCondition.ListOfDouble -> this.setValues(filterElementRows.flatMap { (it as QueryCondition.ListOfDouble).listOfValues })
is QueryCondition.ListOfInt -> this.setValues(filterElementRows.flatMap { (it as QueryCondition.ListOfInt).listOfValues })
is QueryCondition.ListOfString -> this.setValues(filterElementRows.flatMap { (it as QueryCondition.ListOfString).listOfValues })
else -> {}
}
}

@ -75,8 +75,7 @@ open class Transaction : RealmObject(), RowRepresentable, RowUpdatable, Manageab
override var amount: Double = 0.0
set(value) {
field = value
val rate = this.bankroll?.currency?.rate ?: 1.0
this.ratedAmount = rate * value
computeRatedAmount()
}
// The amount of the transaction
@ -114,6 +113,11 @@ open class Transaction : RealmObject(), RowRepresentable, RowUpdatable, Manageab
RATED_AMOUNT("ratedAmount")
}
fun computeRatedAmount() {
val rate = this.bankroll?.currency?.rate ?: 1.0
this.ratedAmount = rate * this.amount
}
val displayAmount: Double
get() { // for transfers we want to show a positive value (in the feed for instance)
return if (this.destination == null) { this.amount } else { abs(this.amount) }

@ -84,7 +84,7 @@ class ImportActivity : BaseActivity() {
when (requestCode) {
RequestCode.IMPORT.value -> {
if (resultCode == ResultCode.IMPORT_UNRECOGNIZED_FORMAT.value) {
showAlertDialog(context = this, message = R.string.unknown_import_format_popup_message, positiveAction = {
showAlertDialog(context = this, messageResId = R.string.unknown_import_format_popup_message, positiveAction = {
finish()
})
}

@ -3,20 +3,17 @@ package net.pokeranalytics.android.ui.extensions
import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.DialogInterface
import android.content.Intent
import android.content.res.Resources
import android.graphics.Bitmap
import android.graphics.Canvas
import android.graphics.Color
import android.net.Uri
import android.text.SpannableStringBuilder
import android.util.TypedValue
import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import android.widget.*
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.SearchView
import androidx.core.content.ContextCompat
@ -37,212 +34,245 @@ import java.io.File
// Sizes
val Int.dp: Int
get() = (this / Resources.getSystem().displayMetrics.density).toInt()
get() = (this / Resources.getSystem().displayMetrics.density).toInt()
val Int.px: Int
get() = (this * Resources.getSystem().displayMetrics.density).toInt()
get() = (this * Resources.getSystem().displayMetrics.density).toInt()
val Float.dp: Float
get() = (this / Resources.getSystem().displayMetrics.density)
get() = (this / Resources.getSystem().displayMetrics.density)
val Float.px: Float
get() = (this * Resources.getSystem().displayMetrics.density)
get() = (this * Resources.getSystem().displayMetrics.density)
// Toast
fun Activity.toast(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}
fun Fragment.toast(message: String) {
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
Toast.makeText(requireContext(), message, Toast.LENGTH_SHORT).show()
}
// Open Play Store for rating
fun Activity.openPlayStorePage() {
val uri = Uri.parse("market://details?id=$packageName")
val goToMarket = Intent(Intent.ACTION_VIEW, uri)
goToMarket.addFlags(
Intent.FLAG_ACTIVITY_NO_HISTORY or Intent.FLAG_ACTIVITY_NEW_DOCUMENT or
Intent.FLAG_ACTIVITY_MULTIPLE_TASK
)
try {
startActivity(goToMarket)
} catch (e: ActivityNotFoundException) {
startActivity(
Intent(
Intent.ACTION_VIEW, Uri.parse(
"http://play.google.com/store/apps/details?id=$packageName"
)
)
)
}
val uri = Uri.parse("market://details?id=$packageName")
val goToMarket = Intent(Intent.ACTION_VIEW, uri)
goToMarket.addFlags(
Intent.FLAG_ACTIVITY_NO_HISTORY or Intent.FLAG_ACTIVITY_NEW_DOCUMENT or
Intent.FLAG_ACTIVITY_MULTIPLE_TASK
)
try {
startActivity(goToMarket)
} catch (e: ActivityNotFoundException) {
startActivity(
Intent(
Intent.ACTION_VIEW, Uri.parse(
"http://play.google.com/store/apps/details?id=$packageName"
)
)
)
}
}
// Open email for "Contact us"
fun BaseActivity.openContactMail(subjectStringRes: Int, filePath: String? = null) {
val info =
"v${BuildConfig.VERSION_NAME}(${BuildConfig.VERSION_CODE}) - ${AppGuard.isProUser}, Android ${android.os.Build.VERSION.SDK_INT}, ${DeviceUtils.getDeviceName()}"
val emailIntent = Intent(Intent.ACTION_SEND)
filePath?.let {
val databaseFile = File(it)
val contentUri = FileProvider.getUriForFile(this, "net.pokeranalytics.android.fileprovider", databaseFile)
if (contentUri != null) {
emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
emailIntent.setDataAndType(contentUri, contentResolver.getType(contentUri))
emailIntent.putExtra(Intent.EXTRA_STREAM, contentUri)
}
}
emailIntent.type = "message/rfc822"
emailIntent.putExtra(Intent.EXTRA_SUBJECT, getString(subjectStringRes))
emailIntent.putExtra(Intent.EXTRA_TEXT, "\n\n$info")
emailIntent.putExtra(Intent.EXTRA_EMAIL, arrayOf(URL.SUPPORT_EMAIL.value))
startActivity(Intent.createChooser(emailIntent, getString(R.string.contact)))
val info =
"v${BuildConfig.VERSION_NAME}(${BuildConfig.VERSION_CODE}) - ${AppGuard.isProUser}, Android ${android.os.Build.VERSION.SDK_INT}, ${DeviceUtils.getDeviceName()}"
val emailIntent = Intent(Intent.ACTION_SEND)
filePath?.let {
val databaseFile = File(it)
val contentUri = FileProvider.getUriForFile(
this,
"net.pokeranalytics.android.fileprovider",
databaseFile
)
if (contentUri != null) {
emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
emailIntent.setDataAndType(contentUri, contentResolver.getType(contentUri))
emailIntent.putExtra(Intent.EXTRA_STREAM, contentUri)
}
}
emailIntent.type = "message/rfc822"
emailIntent.putExtra(Intent.EXTRA_SUBJECT, getString(subjectStringRes))
emailIntent.putExtra(Intent.EXTRA_TEXT, "\n\n$info")
emailIntent.putExtra(Intent.EXTRA_EMAIL, arrayOf(URL.SUPPORT_EMAIL.value))
startActivity(Intent.createChooser(emailIntent, getString(R.string.contact)))
}
// Open custom tab
fun Context.openUrl(url: String) {
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
ContextCompat.startActivity(this, browserIntent, null)
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
ContextCompat.startActivity(this, browserIntent, null)
}
// Open custom tab
fun Context.areYouSure(title: Int? = null, message: Int? = null, positiveTitle: Int? = null, proceed: () -> Unit) {
val builder: android.app.AlertDialog.Builder = android.app.AlertDialog.Builder(this)
val messageResource = message ?: R.string.are_you_sure_you_want_to_do_this
builder.setMessage(messageResource)
fun Context.areYouSure(
title: Int? = null,
message: Int? = null,
positiveTitle: Int? = null,
proceed: () -> Unit
) {
title?.let { builder.setTitle(it) }
val builder: android.app.AlertDialog.Builder = android.app.AlertDialog.Builder(this)
val positiveButtonTitle = positiveTitle ?: R.string.yes
builder.setPositiveButton(positiveButtonTitle) { _, _ ->
proceed()
}
builder.setNegativeButton(R.string.cancel) { _, _ ->
// nothing
}
val messageResource = message ?: R.string.are_you_sure_you_want_to_do_this
builder.setMessage(messageResource)
// builder.setItems(
// arrayOf<CharSequence>(
// getString(R.string.yes),
// getString(R.string.cancel)
// )
// ) { _, index ->
// // The 'which' argument contains the index position
// // of the selected item
// when (index) {
// 0 -> proceed()
// 1 -> {} // nothing
// }
// }
builder.create().show()
title?.let { builder.setTitle(it) }
val positiveButtonTitle = positiveTitle ?: R.string.yes
builder.setPositiveButton(positiveButtonTitle) { _, _ ->
proceed()
}
builder.setNegativeButton(R.string.cancel) { _, _ ->
// nothing
}
builder.create().show()
}
// Display Alert Dialog
fun Activity.showAlertDialog(title: Int? = null, message: Int? = null) {
showAlertDialog(this, title, message)
showAlertDialog(this, title, message)
}
fun Fragment.showAlertDialog(title: Int? = null, message: Int? = null) {
context?.let {
showAlertDialog(it, title, message)
}
fun Fragment.showAlertDialog(
title: Int? = null,
messageResId: Int? = null,
message: String? = null
) {
context?.let {
showAlertDialog(it, title, messageResId, message)
}
}
fun showEditTextAlertDialog(
context: Context, inputType: Int, title: Int? = null, messageResId: Int? = null, message: String? = null,
editTextText: String? = null, positiveAction: ((String) -> Unit)? = null
) {
val builder = AlertDialog.Builder(context)
title?.let {
builder.setTitle(title)
}
messageResId?.let {
builder.setMessage(messageResId)
}
message?.let {
builder.setMessage(it)
}
val editText = EditText(context)
editTextText?.let {
editText.text = SpannableStringBuilder(it)
}
editText.setTextColor(ContextCompat.getColor(context, R.color.white))
editText.inputType = inputType
builder.setView(editText)
builder.setPositiveButton(net.pokeranalytics.android.R.string.ok) { _, _ ->
positiveAction?.invoke(editText.text.toString())
}
builder.show()
}
/**
* Create and show an alert dialog
*/
fun showAlertDialog(
context: Context, title: Int? = null, message: Int? = null, cancelButtonTitle: Int? = null, showCancelButton: Boolean = false,
positiveAction: (() -> Unit)? = null, negativeAction: (() -> Unit)? = null
context: Context, title: Int? = null, messageResId: Int? = null, message: String? = null,
cancelButtonTitle: Int? = null, showCancelButton: Boolean = false,
positiveAction: (() -> Unit)? = null, negativeAction: (() -> Unit)? = null
) {
val builder = AlertDialog.Builder(context)
title?.let {
builder.setTitle(title)
}
message?.let {
builder.setMessage(message)
}
builder.setPositiveButton(net.pokeranalytics.android.R.string.ok) { _, _ ->
positiveAction?.invoke()
}
if (cancelButtonTitle != null) {
builder.setNegativeButton(cancelButtonTitle) { _, _ ->
negativeAction?.invoke()
}
} else if (showCancelButton) {
builder.setNegativeButton(R.string.cancel) { _, _ ->
negativeAction?.invoke()
}
}
builder.show()
val builder = AlertDialog.Builder(context)
title?.let {
builder.setTitle(title)
}
messageResId?.let {
builder.setMessage(messageResId)
}
message?.let {
builder.setMessage(it)
}
builder.setPositiveButton(net.pokeranalytics.android.R.string.ok) { _, _ ->
positiveAction?.invoke()
}
if (cancelButtonTitle != null) {
builder.setNegativeButton(cancelButtonTitle) { _, _ ->
negativeAction?.invoke()
}
} else if (showCancelButton) {
builder.setNegativeButton(R.string.cancel) { _, _ ->
negativeAction?.invoke()
}
}
builder.show()
}
fun TextView.setTextFormat(textFormat: TextFormat, context: Context) {
this.setTextColor(textFormat.getColor(context))
this.text = textFormat.text
this.setTextColor(textFormat.getColor(context))
this.text = textFormat.text
}
fun View.hideWithAnimation() {
isVisible = true
animate().cancel()
animate().alpha(0f).withEndAction { isVisible = false }.start()
isVisible = true
animate().cancel()
animate().alpha(0f).withEndAction { isVisible = false }.start()
}
fun View.showWithAnimation() {
isVisible = true
animate().cancel()
animate().alpha(1f).start()
isVisible = true
animate().cancel()
animate().alpha(1f).start()
}
fun View.addCircleRipple() = with(TypedValue()) {
context.theme.resolveAttribute(android.R.attr.selectableItemBackgroundBorderless, this, true)
setBackgroundResource(resourceId)
context.theme.resolveAttribute(android.R.attr.selectableItemBackgroundBorderless, this, true)
setBackgroundResource(resourceId)
}
fun SearchView.removeMargins() {
val searchEditFrame = findViewById<LinearLayout?>(R.id.search_edit_frame)
val layoutParams = searchEditFrame?.layoutParams as LinearLayout.LayoutParams?
layoutParams?.leftMargin = 0
layoutParams?.rightMargin = 0
searchEditFrame?.layoutParams = layoutParams
val searchEditFrame = findViewById<LinearLayout?>(R.id.search_edit_frame)
val layoutParams = searchEditFrame?.layoutParams as LinearLayout.LayoutParams?
layoutParams?.leftMargin = 0
layoutParams?.rightMargin = 0
searchEditFrame?.layoutParams = layoutParams
}
fun View.toByteArray() : ByteArray {
return this.convertToBitmap().toByteArray()
fun View.toByteArray(): ByteArray {
return this.convertToBitmap().toByteArray()
}
fun View.convertToBitmap(): Bitmap {
val b = Bitmap.createBitmap(this.width, this.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(b)
val background = this.background
this.background?.let {
background.draw(canvas)
} ?: run {
canvas.drawColor(Color.WHITE)
}
this.draw(canvas)
return b
val b = Bitmap.createBitmap(this.width, this.height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(b)
val background = this.background
this.background?.let {
background.draw(canvas)
} ?: run {
canvas.drawColor(Color.WHITE)
}
this.draw(canvas)
return b
}
fun ImageView.toByteArray() : ByteArray {
return this.drawable.toBitmap().toByteArray()
fun ImageView.toByteArray(): ByteArray {
return this.drawable.toBitmap().toByteArray()
}
fun Bitmap.toByteArray() : ByteArray {
val baos = ByteArrayOutputStream()
this.compress(Bitmap.CompressFormat.PNG, 100, baos)
return baos.toByteArray()
fun Bitmap.toByteArray(): ByteArray {
val baos = ByteArrayOutputStream()
this.compress(Bitmap.CompressFormat.PNG, 100, baos)
return baos.toByteArray()
}
fun Context.hideKeyboard(v: View) {
val imm = v.context.getSystemService(InputMethodManager::class.java)
imm?.hideSoftInputFromWindow(v.windowToken, 0)
val imm = v.context.getSystemService(InputMethodManager::class.java)
imm?.hideSoftInputFromWindow(v.windowToken, 0)
}
//fun Context.showKeyboard(view: View) {

@ -65,8 +65,8 @@ class CurrenciesFragment : BaseFragment(), StaticRowRepresentableDataSource, Row
}
var currencyCode: String = currency.currencyCode
var currencySymbole: String = currency.getSymbol(Locale.getDefault())
var currencyCodeAndSymbol: String = "${this.currencyCode} (${this.currencySymbole})"
var currencySymbol: String = currency.getSymbol(Locale.getDefault())
var currencyCodeAndSymbol: String = "${this.currencyCode} (${this.currencySymbol})"
override val viewType: Int = RowViewType.TITLE_VALUE.ordinal
}
@ -110,6 +110,9 @@ class CurrenciesFragment : BaseFragment(), StaticRowRepresentableDataSource, Row
// RowRepresentableDelegate
override fun onRowSelected(position: Int, row: RowRepresentable, tag: Int) {
val intent = Intent()
intent.putExtra(INTENT_CURRENCY_CODE, (row as CurrencyRow).currency.currencyCode)
this.activity?.setResult(Activity.RESULT_OK, intent)

@ -6,6 +6,7 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.text.InputType
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@ -17,6 +18,7 @@ import com.google.android.play.core.review.ReviewManagerFactory
import io.realm.Realm
import net.pokeranalytics.android.BuildConfig
import net.pokeranalytics.android.R
import net.pokeranalytics.android.api.CurrencyConverterApi
import net.pokeranalytics.android.databinding.FragmentSettingsBinding
import net.pokeranalytics.android.exceptions.PAIllegalStateException
import net.pokeranalytics.android.model.LiveData
@ -33,6 +35,7 @@ import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.extensions.openContactMail
import net.pokeranalytics.android.ui.extensions.openUrl
import net.pokeranalytics.android.ui.extensions.showEditTextAlertDialog
import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.ui.modules.bankroll.BankrollActivity
import net.pokeranalytics.android.ui.modules.datalist.DataListActivity
@ -141,19 +144,7 @@ class SettingsFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRep
if (resultCode == Activity.RESULT_OK) {
data?.let {
val currencyCode = data.getStringExtra(CurrenciesFragment.INTENT_CURRENCY_CODE) ?: throw PAIllegalStateException("Missing currency code")
Preferences.setCurrencyCode(currencyCode, requireContext())
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
realm.where(Currency::class.java).isNull("code").or().equalTo("code", UserDefaults.currency.currencyCode).findAll().forEach { currency ->
currency.rate = Currency.DEFAULT_RATE
}
realm.where(Session::class.java).isNull("bankroll.currency.code").findAll().forEach { session ->
session.bankrollHasBeenUpdated()
}
}
realm.close()
settingsAdapterRow.refreshRow(SettingsRow.CURRENCY)
updateMainCurrency(currencyCode)
}
}
}
@ -163,6 +154,50 @@ class SettingsFragment : RealmFragment(), RowRepresentableDelegate, StaticRowRep
}
}
private fun updateMainCurrency(currencyCode: String) {
Preferences.getDefaultCurrency(requireContext())?.currencyCode?.let { mainCurrencyCode ->
if (mainCurrencyCode == currencyCode) {
return
}
showLoader(R.string.please_wait)
CurrencyConverterApi.currencyRate(mainCurrencyCode, currencyCode, requireContext()) { apiRate, _ ->
hideLoader()
val message = requireContext().getString(R.string.currency_rate_confirmation, mainCurrencyCode, currencyCode)
// val message = "Please enter the $mainCurrencyCode to $currencyCode rate to apply to all your bankrolls"
showEditTextAlertDialog(requireContext(), InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL,
message = message, editTextText = apiRate?.toString()) { value ->
value.toDoubleOrNull()?.let { rate ->
updateMainCurrency(currencyCode, rate)
}
}
}
}
}
private fun updateMainCurrency(currencyCode: String, rate: Double) {
Preferences.setCurrencyCode(currencyCode, requireContext())
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
realm.where(Currency::class.java).findAll().forEach { currency ->
currency.rate = (currency.rate ?: 1.0) * rate
}
realm.where(Session::class.java).findAll().forEach { session ->
session.bankrollHasBeenUpdated()
}
}
realm.close()
settingsAdapterRow.refreshRow(SettingsRow.CURRENCY)
}
override fun adapterRows(): List<RowRepresentable> {
return rowRepresentation
}

@ -7,6 +7,7 @@ import android.os.Bundle
import android.view.View
import androidx.appcompat.widget.Toolbar
import androidx.fragment.app.Fragment
import com.google.android.material.snackbar.Snackbar
import net.pokeranalytics.android.PokerAnalyticsApplication
import net.pokeranalytics.android.R
import net.pokeranalytics.android.ui.activity.components.BaseActivity
@ -15,6 +16,7 @@ import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheet
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
import net.pokeranalytics.android.util.CrashLogging
import timber.log.Timber
import java.io.File
import java.util.*
@ -183,4 +185,13 @@ abstract class BaseFragment : Fragment() {
return capability?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) ?: false
}
fun showSnackBar(message: String) {
this.view?.let { view ->
val snackBar = Snackbar.make(view, message, Snackbar.LENGTH_INDEFINITE)
snackBar.show()
} ?: run {
Timber.d("No parent view for snackbar")
}
}
}

@ -47,7 +47,7 @@ class LoaderDialogFragment: DialogFragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
arguments?.let {bundle ->
arguments?.let { bundle ->
if (bundle.containsKey(ARGUMENT_MESSAGE_RES_ID)) {
binding.loadingMessage.text = getString(bundle.getInt(ARGUMENT_MESSAGE_RES_ID))
}

@ -68,9 +68,9 @@ abstract class AbstractReportFragment : DataManagerFragment() {
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
val view = inflater.inflate(net.pokeranalytics.android.R.layout.dialog_edit_text, null)
val view = inflater.inflate(R.layout.dialog_edit_text, null)
val nameEditText =
view.findViewById<EditText>(net.pokeranalytics.android.R.id.reportName)
view.findViewById<EditText>(R.id.reportName)
nameEditText.inputType = InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
this.model.primaryKey?.let { id ->
@ -81,7 +81,7 @@ abstract class AbstractReportFragment : DataManagerFragment() {
builder.setView(view)
// Add action buttons
.setPositiveButton(net.pokeranalytics.android.R.string.save) { dialog, _ ->
.setPositiveButton(R.string.save) { dialog, _ ->
try {
saveReport(nameEditText.text.toString())
dialog.dismiss()
@ -89,7 +89,7 @@ abstract class AbstractReportFragment : DataManagerFragment() {
Toast.makeText(requireContext(), e.localizedMessage, Toast.LENGTH_SHORT).show()
}
}
.setNegativeButton(net.pokeranalytics.android.R.string.cancel) { dialog, _ ->
.setNegativeButton(R.string.cancel) { dialog, _ ->
dialog.cancel()
}

@ -177,6 +177,7 @@ class CalendarDetailsFragment : BaseFragment(), StaticRowRepresentableDataSource
when (model.sessionTypeCondition) {
QueryCondition.IsCash -> query.add(QueryCondition.IsCash)
QueryCondition.IsTournament -> query.add(QueryCondition.IsTournament)
else -> {}
}
val requiredStats: List<Stat> = listOf(Stat.LOCATIONS_PLAYED, Stat.LONGEST_STREAKS, Stat.DAYS_PLAYED, Stat.STANDARD_DEVIATION_HOURLY)

@ -431,6 +431,7 @@ class CalendarFragment : RealmFragment(), CoroutineScope, StaticRowRepresentable
Calendar.MONTH,
condition.listOfValues.first()
)
else -> {}
}
}
@ -478,6 +479,7 @@ class CalendarFragment : RealmFragment(), CoroutineScope, StaticRowRepresentable
Calendar.YEAR,
condition.listOfValues.first()
)
else -> {}
}
}
yearlyReports[calendar.time] = computedResults

@ -8,13 +8,14 @@ import android.view.View
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModelProvider
import net.pokeranalytics.android.R
import net.pokeranalytics.android.api.FreeConverterApi
import net.pokeranalytics.android.api.CurrencyConverterApi
import net.pokeranalytics.android.model.realm.Bankroll
import net.pokeranalytics.android.model.realm.ResultCaptureType
import net.pokeranalytics.android.ui.activity.CurrenciesActivity
import net.pokeranalytics.android.ui.activity.components.RequestCode
import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.extensions.toast
import net.pokeranalytics.android.ui.fragment.CurrenciesFragment
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
@ -137,7 +138,7 @@ class BankrollDataFragment : EditableDataFragment(), StaticRowRepresentableDataS
override fun charSequenceForRow(row: RowRepresentable, context: Context, tag: Int): CharSequence {
return when (row) {
SimpleRow.NAME -> if (bankroll.name.isNotEmpty()) bankroll.name else NULL_TEXT
SimpleRow.NAME -> bankroll.name.ifEmpty { NULL_TEXT }
BankrollPropertiesRow.CURRENCY -> {
bankroll.currency?.code?.let { code ->
Currency.getInstance(code).currencyCode
@ -263,12 +264,24 @@ class BankrollDataFragment : EditableDataFragment(), StaticRowRepresentableDataS
}
this.lastRefreshRateCall = System.currentTimeMillis()
val currenciesConverterValue = "${bankroll.currency?.code}_${defaultCurrency.currencyCode}"
// val currenciesConverterValue = "${bankroll.currency?.code}_${defaultCurrency.currencyCode}"
FreeConverterApi.currencyRate(currenciesConverterValue, requireContext()) { rate ->
onRowValueChanged(rate, BankrollPropertiesRow.RATE)
isRefreshingRate = false
rowRepresentableAdapter.refreshRow(BankrollPropertiesRow.REFRESH_RATE)
bankroll.currency?.code?.let { from ->
val to = defaultCurrency.currencyCode
CurrencyConverterApi.currencyRate(from, to, requireContext()) { rate, error ->
rate?.let {
onRowValueChanged(rate, BankrollPropertiesRow.RATE)
}
error?.localizedMessage?.let { message ->
toast(message)
}
// onRowValueChanged(rate, BankrollPropertiesRow.RATE)
isRefreshingRate = false
rowRepresentableAdapter.refreshRow(BankrollPropertiesRow.REFRESH_RATE)
}
}
this.isRefreshingRate = true
@ -280,7 +293,6 @@ class BankrollDataFragment : EditableDataFragment(), StaticRowRepresentableDataS
this.bankrollModel.selectedCaptureType.value?.let {
Preferences.setResultCaptureType(this.bankroll, it, requireContext())
}
}
}

@ -172,7 +172,7 @@ class PlayerDataFragment : EditableDataFragment(), StaticRowRepresentableDataSou
if (row.isValidForDelete(getRealm())) {
GlobalScope.launch(Dispatchers.Main) {
delay(300)
showAlertDialog(requireContext(), message = R.string.are_you_sure_you_want_to_delete, showCancelButton = true, positiveAction = {
showAlertDialog(requireContext(), messageResId = R.string.are_you_sure_you_want_to_delete, showCancelButton = true, positiveAction = {
player.deleteComment(row)
rowRepresentableAdapter.notifyDataSetChanged()
})

@ -63,6 +63,7 @@ sealed class FilterSectionRow(override val resId: Int?) : RowRepresentable {
is CustomField -> {
return customField.name
}
else -> {}
}
return name
}

@ -48,7 +48,8 @@ class Preferences {
PATCH_STAKES("patchStakes"),
CLEAN_BLINDS_FILTERS("deleteBlindsFilters"),
SHOW_IN_APP_BADGES("showInAppBadges"),
LAST_CALENDAR_BADGE_DATE("lastCalendarBadgeDate")
LAST_CALENDAR_BADGE_DATE("lastCalendarBadgeDate"),
PATCH_RATED_AMOUNT("patchRatedAmount[new field]")
}
enum class FeedMessage {
@ -101,17 +102,17 @@ class Preferences {
companion object {
fun setStringSet(key: PreferenceKey, value: MutableSet<String>, context: Context) {
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
val editor = preferences.edit()
editor.putStringSet(key.identifier, value)
editor.apply()
}
fun getStringSet(key: Keys, context: Context): MutableSet<String>? {
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
return preferences.getStringSet(key.identifier, null)
}
// fun setStringSet(key: PreferenceKey, value: MutableSet<String>, context: Context) {
// val preferences = PreferenceManager.getDefaultSharedPreferences(context)
// val editor = preferences.edit()
// editor.putStringSet(key.identifier, value)
// editor.apply()
// }
//
// fun getStringSet(key: Keys, context: Context): MutableSet<String>? {
// val preferences = PreferenceManager.getDefaultSharedPreferences(context)
// return preferences.getStringSet(key.identifier, null)
// }
fun setString(key: PreferenceKey, value: String, context: Context) {
val preferences = PreferenceManager.getDefaultSharedPreferences(context)

@ -780,5 +780,7 @@
<string name="less_or_equal_sign"><![CDATA[≤]]></string>
<string name="less_sign"><![CDATA[<]]></string>
<string name="expense">expense</string>
<string name="please_wait">Please wait…</string>
<string name="currency_rate_confirmation">Please enter the %1$s to %2$s rate to apply to all your bankrolls</string>
</resources>

@ -784,6 +784,8 @@ La aplicación funciona con una suscripción anual para uso ilimitado, pero obti
<string name="less_or_equal_sign"><![CDATA[≤]]></string>
<string name="less_sign"><![CDATA[<]]></string>
<string name="expense">expense</string>
<string name="please_wait">Please wait…</string>
<string name="currency_rate_confirmation">Please enter the %1$s to %2$s rate to apply to all your bankrolls</string>
</resources>

@ -789,5 +789,7 @@
<string name="less_sign"><![CDATA[<]]></string>
<string name="transaction_filter">Filtre de transactions</string>
<string name="expense">frais</string>
<string name="please_wait">Veuillez patienter…</string>
<string name="currency_rate_confirmation">Veuillez entrer le taux de %1$s vers %2$s pour l\'appliquer à toutes vos bankrolls</string>
</resources>

@ -779,5 +779,7 @@
<string name="less_or_equal_sign"><![CDATA[≤]]></string>
<string name="less_sign"><![CDATA[<]]></string>
<string name="expense">expense</string>
<string name="please_wait">Please wait…</string>
<string name="currency_rate_confirmation">Please enter the %1$s to %2$s rate to apply to all your bankrolls</string>
</resources>

@ -779,5 +779,7 @@
<string name="less_or_equal_sign"><![CDATA[≤]]></string>
<string name="less_sign"><![CDATA[<]]></string>
<string name="expense">expense</string>
<string name="please_wait">Please wait…</string>
<string name="currency_rate_confirmation">Please enter the %1$s to %2$s rate to apply to all your bankrolls</string>
</resources>

@ -783,5 +783,7 @@
<string name="less_or_equal_sign"><![CDATA[≤]]></string>
<string name="less_sign"><![CDATA[<]]></string>
<string name="expense">expense</string>
<string name="please_wait">Please wait…...</string>
<string name="currency_rate_confirmation">Please enter the %1$s to %2$s rate to apply to all your bankrolls</string>
</resources>

@ -778,5 +778,7 @@
<string name="less_or_equal_sign"><![CDATA[≤]]></string>
<string name="less_sign"><![CDATA[<]]></string>
<string name="expense">expense</string>
<string name="please_wait">Please wait…</string>
<string name="currency_rate_confirmation">Please enter the %1$s to %2$s rate to apply to all your bankrolls</string>
</resources>

@ -780,5 +780,7 @@
<string name="less_or_equal_sign"><![CDATA[≤]]></string>
<string name="less_sign"><![CDATA[<]]></string>
<string name="expense">expense</string>
<string name="please_wait">Please wait…</string>
<string name="currency_rate_confirmation">Please enter the %1$s to %2$s rate to apply to all your bankrolls</string>
</resources>

@ -773,5 +773,7 @@
<string name="less_or_equal_sign"><![CDATA[≤]]></string>
<string name="less_sign"><![CDATA[<]]></string>
<string name="expense">expense</string>
<string name="please_wait">Please wait…</string>
<string name="currency_rate_confirmation">Please enter the %1$s to %2$s rate to apply to all your bankrolls</string>
</resources>

@ -833,5 +833,7 @@
<string name="empty_reports_screen">This screen will show where you perform the best when you\'ll have more data. You also can create custom reports using the top right button.</string>
<string name="transaction_filter">Transaction Filter</string>
<string name="expense">expense</string>
<string name="please_wait">Please wait…</string>
<string name="currency_rate_confirmation">Please enter the %1$s to %2$s rate to apply to all your bankrolls</string>
</resources>

@ -1,7 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext.kotlin_version = '1.5.21'
ext.kotlin_version = '1.7.21'
repositories {
google()
jcenter()
@ -15,6 +15,9 @@ buildscript {
classpath 'com.google.gms:google-services:4.3.4'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.4.1'
// serialization
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
}
}

Loading…
Cancel
Save