Merge branch 'dev' of gitlab.com:stax-river/poker-analytics into dev

dev
Laurent 7 years ago
commit 595bea05a6
  1. 24
      app/src/main/java/net/pokeranalytics/android/model/interfaces/Manageable.kt
  2. 22
      app/src/main/java/net/pokeranalytics/android/model/realm/Bankroll.kt
  3. 9
      app/src/main/java/net/pokeranalytics/android/model/realm/Transaction.kt
  4. 17
      app/src/main/java/net/pokeranalytics/android/model/realm/TransactionType.kt
  5. 2
      app/src/main/java/net/pokeranalytics/android/ui/activity/BankrollActivity.kt
  6. 7
      app/src/main/java/net/pokeranalytics/android/ui/fragment/BankrollEditDataFragment.kt
  7. 90
      app/src/main/java/net/pokeranalytics/android/ui/fragment/BankrollFragment.kt
  8. 79
      app/src/main/java/net/pokeranalytics/android/ui/fragment/DataListFragment.kt
  9. 114
      app/src/main/java/net/pokeranalytics/android/ui/fragment/DeletableItemFragment.kt
  10. 3
      app/src/main/java/net/pokeranalytics/android/ui/fragment/EditableDataFragment.kt
  11. 24
      app/src/main/java/net/pokeranalytics/android/ui/fragment/FeedFragment.kt
  12. 6
      app/src/main/java/net/pokeranalytics/android/ui/fragment/SessionFragment.kt
  13. 4
      app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/CustomizableRowRepresentable.kt
  14. 29
      app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/TransactionTypeRow.kt
  15. 14
      app/src/main/java/net/pokeranalytics/android/util/extensions/DateExtension.kt
  16. 2
      app/src/main/res/layout/fragment_data_list.xml
  17. 49
      app/src/main/res/layout/fragment_feed.xml
  18. 3
      app/src/main/res/values-fr/strings.xml
  19. 3
      app/src/main/res/values/strings.xml

@ -12,12 +12,19 @@ enum class SaveValidityStatus {
DATA_INVALID;
}
enum class DeleteValidityStatus {
VALID,
INVALID,
SESSIONS_LINKED,
TRANSACTIONS_LINKED;
}
/**
* An interface to grouped object which are managed by the database
*/
interface Manageable : Savable, Deletable, Editable
interface NameManageable: Manageable {
interface NameManageable : Manageable {
var name: String
override fun isValidForSave(): Boolean {
@ -32,7 +39,7 @@ interface NameManageable: Manageable {
throw ModelException("${this::class.java} getFailedSaveMessage for $status not handled")
}
override fun getFailedDeleteMessage(): Int {
override fun getFailedDeleteMessage(status: DeleteValidityStatus): Int {
return R.string.relationship_error
}
}
@ -87,7 +94,7 @@ interface Savable : Identifiable {
/**
* A method to get the reason why the object can't be saved
*/
fun getFailedSaveMessage(status:SaveValidityStatus): Int
fun getFailedSaveMessage(status: SaveValidityStatus): Int
}
@ -101,8 +108,17 @@ interface Deletable : Identifiable {
*/
fun isValidForDelete(realm: Realm): Boolean
fun getDeleteStatus(realm: Realm): DeleteValidityStatus {
if (!isValidForDelete(realm)) {
return DeleteValidityStatus.INVALID
}
return DeleteValidityStatus.VALID
}
/**
* A method to get the reason why the object can't be deleted
*/
fun getFailedDeleteMessage(): Int
fun getFailedDeleteMessage(status: DeleteValidityStatus): Int
}

@ -7,6 +7,7 @@ import io.realm.annotations.LinkingObjects
import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus
import net.pokeranalytics.android.model.interfaces.NameManageable
import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
import net.pokeranalytics.android.ui.view.RowRepresentable
@ -66,12 +67,25 @@ open class Bankroll : RealmObject(), NameManageable, RowRepresentable {
override fun isValidForDelete(realm: Realm): Boolean {
return realm.where<Session>().equalTo("bankroll.id", id).findAll().isEmpty()
//TODO: Check transactions
//&& realm.where<Transaction>().equalTo("bankroll.id", id).findAll().isEmpty()
&& realm.where<Transaction>().equalTo("bankroll.id", id).findAll().isEmpty()
}
override fun getFailedDeleteMessage(): Int {
return R.string.bankroll_relationship_error
override fun getDeleteStatus(realm: Realm): DeleteValidityStatus {
return if (!realm.where<Session>().equalTo("bankroll.id", id).findAll().isEmpty()) {
DeleteValidityStatus.SESSIONS_LINKED
} else if (!realm.where<Transaction>().equalTo("bankroll.id", id).findAll().isEmpty()) {
DeleteValidityStatus.TRANSACTIONS_LINKED
} else {
DeleteValidityStatus.VALID
}
}
override fun getFailedDeleteMessage(status: DeleteValidityStatus): Int {
return when (status) {
DeleteValidityStatus.SESSIONS_LINKED -> R.string.bankroll_relationship_error
DeleteValidityStatus.TRANSACTIONS_LINKED -> R.string.bankroll_relationship_error_transactions
else -> super.getFailedDeleteMessage(status)
}
}
override fun getFailedSaveMessage(status: SaveValidityStatus): Int {

@ -10,10 +10,7 @@ import net.pokeranalytics.android.calculus.Stat
import net.pokeranalytics.android.calculus.TextFormat
import net.pokeranalytics.android.model.filter.Filterable
import net.pokeranalytics.android.model.filter.QueryCondition
import net.pokeranalytics.android.model.interfaces.DatedValue
import net.pokeranalytics.android.model.interfaces.Manageable
import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
import net.pokeranalytics.android.model.interfaces.TimeFilterable
import net.pokeranalytics.android.model.interfaces.*
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.graph.GraphUnderlyingEntry
import net.pokeranalytics.android.ui.view.RowRepresentable
@ -115,8 +112,8 @@ open class Transaction : RealmObject(), Manageable, StaticRowRepresentableDataSo
return true
}
override fun getFailedDeleteMessage(): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
override fun getFailedDeleteMessage(status: DeleteValidityStatus): Int {
return R.string.relationship_error
}
override fun getSaveValidityStatus(realm: Realm): SaveValidityStatus {

@ -3,6 +3,8 @@ package net.pokeranalytics.android.model.realm
import io.realm.Realm
import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus
import net.pokeranalytics.android.model.interfaces.NameManageable
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.view.RowRepresentable
@ -69,6 +71,13 @@ open class TransactionType : RealmObject(), NameManageable, StaticRowRepresentab
}
}
override fun boolForRow(row: RowRepresentable): Boolean {
return when (row) {
TransactionTypeRow.TRANSACTION_ADDITIVE -> this.additive
else -> super.boolForRow(row)
}
}
override fun editDescriptors(row: RowRepresentable): ArrayList<RowRepresentableEditDescriptor>? {
return row.editingDescriptors(mapOf("defaultValue" to this.name))
}
@ -76,15 +85,17 @@ open class TransactionType : RealmObject(), NameManageable, StaticRowRepresentab
override fun updateValue(value: Any?, row: RowRepresentable) {
when (row) {
SimpleRow.NAME -> this.name = value as String? ?: ""
TransactionTypeRow.TRANSACTION_ADDITIVE -> this.additive = value as Boolean? ?: false
}
}
override fun isValidForDelete(realm: Realm): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
return true
}
override fun getFailedDeleteMessage(): Int {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
override fun getFailedDeleteMessage(status: DeleteValidityStatus): Int {
return R.string.relationship_error
}
}

@ -69,10 +69,12 @@ class BankrollActivity : PokerAnalyticsActivity() {
indexes.addAll(changeSet.insertions.toList())
indexes.addAll(changeSet.deletions.toList())
indexes.forEach { index ->
if (t.isNotEmpty()) {
t[index]?.bankroll?.let { br ->
bankrolls.add(br)
}
}
}
this.computeBankrollReports(bankrolls)
}

@ -3,8 +3,6 @@ package net.pokeranalytics.android.ui.fragment
import android.app.Activity.RESULT_OK
import android.content.Intent
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.View
import net.pokeranalytics.android.R
import net.pokeranalytics.android.api.CurrencyConverterApi
@ -56,11 +54,6 @@ class BankrollEditDataFragment : EditableDataFragment(), StaticRowRepresentableD
} ?: false
}
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
super.onCreateOptionsMenu(menu, inflater)
menu?.findItem(R.id.delete)?.isVisible = false
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

@ -6,7 +6,6 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.LinearLayoutManager
import com.github.mikephil.charting.data.LineDataSet
import io.realm.RealmObject
@ -24,7 +23,7 @@ import net.pokeranalytics.android.calculus.bankroll.BankrollCalculator
import net.pokeranalytics.android.calculus.bankroll.BankrollReport
import net.pokeranalytics.android.calculus.bankroll.BankrollReportSetup
import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.model.realm.Bankroll
import net.pokeranalytics.android.ui.activity.BankrollDetailsActivity
import net.pokeranalytics.android.ui.activity.DataListActivity
@ -34,7 +33,6 @@ import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
@ -43,7 +41,7 @@ import timber.log.Timber
import java.util.*
import kotlin.collections.ArrayList
class BankrollFragment : RealmFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate {
class BankrollFragment : DeletableItemFragment(), StaticRowRepresentableDataSource, RowRepresentableDelegate {
companion object {
@ -66,9 +64,9 @@ class BankrollFragment : RealmFragment(), StaticRowRepresentableDataSource, RowR
private var rows: ArrayList<RowRepresentable> = ArrayList()
private var bankrollReportForRow: HashMap<RowRepresentable, BankrollReport> = HashMap()
private var deletedItem: RealmObject? = null
private var lastDeletedItemPosition: Int = 0
private var lastItemClickedPosition: Int = 0
private var lastItemClickedId: String = ""
private var deletedRow: RowRepresentable? = null
// Life Cycle
@ -91,7 +89,7 @@ class BankrollFragment : RealmFragment(), StaticRowRepresentableDataSource, RowR
if (needToDeleteItem) {
GlobalScope.launch(Dispatchers.Main) {
delay(300)
deleteItem(lastItemClickedPosition)
deleteItem(bankrollAdapter, LiveData.BANKROLL.items(getRealm()), lastItemClickedId)
}
}
} else if (requestCode == REQUEST_CODE_CREATE && resultCode == Activity.RESULT_OK) {
@ -115,6 +113,7 @@ class BankrollFragment : RealmFragment(), StaticRowRepresentableDataSource, RowR
if (bankrollReportForRow.containsKey(row)) {
bankrollReportForRow[row]?.let { bankrollReport ->
lastItemClickedPosition = position
lastItemClickedId = (row as Identifiable).id
BankrollDetailsActivity.newInstanceForResult(this, bankrollReport, REQUEST_CODE_DETAILS)
}
}
@ -136,6 +135,9 @@ class BankrollFragment : RealmFragment(), StaticRowRepresentableDataSource, RowR
launch(Dispatchers.Main) {
// TODO: Improve that
// We are in the main thread...
val startDate = Date()
// Graph
@ -152,11 +154,13 @@ class BankrollFragment : RealmFragment(), StaticRowRepresentableDataSource, RowR
val bankrolls = LiveData.BANKROLL.items(getRealm()) as RealmResults<Bankroll>
bankrolls.forEach {
val bankrollReportSetup = BankrollReportSetup(it)
bankrolls.forEach { bankroll ->
val bankrollReportSetup = BankrollReportSetup(bankroll)
val bankrollReport = BankrollCalculator.computeReport(getRealm(), bankrollReportSetup)
val computedStat = ComputedStat(Stat.NET_RESULT, bankrollReport.total)
val row = CustomizableRowRepresentable(RowViewType.TITLE_VALUE_ARROW, title = it.name, computedStat = computedStat, isSelectable = true)
val row =
CustomizableRowRepresentable(RowViewType.TITLE_VALUE_ARROW, title = bankroll.name, computedStat = computedStat, isSelectable = true)
row.id = bankroll.id
rows.add(row)
bankrollReportForRow[row] = bankrollReport
@ -198,64 +202,26 @@ class BankrollFragment : RealmFragment(), StaticRowRepresentableDataSource, RowR
}
}
/**
* Delete item
*/
private fun deleteItem(position: Int) {
//TODO: Get bankroll from bankrollReport and delete it
if (isDetached || activity == null) {
return
override fun updateUIAfterDeletion(itemPosition: Int) {
lastItemClickedPosition = rows.indexOfFirst { if (it is Identifiable) it.id == lastItemClickedId else false }
deletedRow = rows.find { if (it is Identifiable) it.id == lastItemClickedId else false }
rows.removeAt(lastItemClickedPosition)
bankrollAdapter.notifyItemRemoved(lastItemClickedPosition)
}
// Save the delete position & create a copy of the object
val bankrollReport = bankrollReportForRow[rowRepresentableForPosition(position)]
val mRecentlyDeletedItem = bankrollReport?.setup?.bankroll
lastDeletedItemPosition = position
override fun updateUIAfterUndoDeletion(newItem: RealmObject) {
if (mRecentlyDeletedItem is Bankroll) {
// Check if the object is valid for the deletion
if ((mRecentlyDeletedItem as Deletable).isValidForDelete(this.getRealm())) {
deletedItem = getRealm().copyFromRealm(mRecentlyDeletedItem)
getRealm().executeTransaction {
mRecentlyDeletedItem.deleteFromRealm()
}
//bankrollAdapter.notifyItemRemoved(position)
//showUndoSnackBar()
//TODO: Refresh bankrolls
initData()
// TODO: Improve that
// We are recreating a Bankroll report because the last one if invalid => the bankroll of the setup has been deleted
} else {
bankrollAdapter.notifyItemChanged(position)
val builder = AlertDialog.Builder(requireContext())
.setMessage((mRecentlyDeletedItem as Deletable).getFailedDeleteMessage())
.setNegativeButton(R.string.ok, null)
builder.show()
}
}
}
deletedRow?.let { row ->
val bankrollReportSetup = BankrollReportSetup(newItem as Bankroll)
val bankrollReport = BankrollCalculator.computeReport(getRealm(), bankrollReportSetup)
bankrollReportForRow[row] = bankrollReport
/**
* Show undo snack bar
*/
private fun showUndoSnackBar() {
/*
val message = String.format(getString(R.string.data_deleted), getString(R.string.bankroll))
val snackBar = Snackbar.make(coordinatorLayout, message, Snackbar.LENGTH_INDEFINITE)
snackBar.setAction(R.string.cancel) {
getRealm().executeTransaction { realm ->
deletedItem?.let {
realm.copyToRealmOrUpdate(it)
bankrollAdapter.notifyItemInserted(lastDeletedItemPosition)
}
rows.add(lastItemClickedPosition, row)
bankrollAdapter.notifyItemInserted(lastItemClickedPosition)
}
}
snackBar.show()
*/
}
}

@ -6,11 +6,8 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.snackbar.Snackbar
import io.realm.RealmObject
import io.realm.RealmResults
import kotlinx.android.synthetic.main.fragment_data_list.*
import kotlinx.coroutines.Dispatchers
@ -19,7 +16,6 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.model.realm.Filter
import net.pokeranalytics.android.ui.activity.DataListActivity
@ -29,12 +25,11 @@ import net.pokeranalytics.android.ui.activity.components.PokerAnalyticsActivity
import net.pokeranalytics.android.ui.adapter.LiveRowRepresentableDataSource
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.ui.helpers.SwipeToDeleteCallback
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
class DataListFragment : RealmFragment(), LiveRowRepresentableDataSource, RowRepresentableDelegate {
class DataListFragment : DeletableItemFragment(), LiveRowRepresentableDataSource, RowRepresentableDelegate {
companion object {
const val REQUEST_CODE_DETAILS = 1000
@ -43,10 +38,7 @@ class DataListFragment : RealmFragment(), LiveRowRepresentableDataSource, RowRep
private lateinit var dataType: LiveData
private lateinit var items: RealmResults<*>
private lateinit var dataListAdapter: RowRepresentableAdapter
private var deletedItem: RealmObject? = null
private var lastDeletedItemPosition: Int = 0
private var lastItemClickedPosition: Int = 0
private var lastItemClickedId: String = ""
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
super.onCreateView(inflater, container, savedInstanceState)
@ -61,11 +53,12 @@ class DataListFragment : RealmFragment(), LiveRowRepresentableDataSource, RowRep
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_CODE_DETAILS && resultCode == Activity.RESULT_OK) {
val needToDeleteItem = data?.getBooleanExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName, false) ?: false
val needToDeleteItem =
data?.getBooleanExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName, false) ?: false
if (needToDeleteItem) {
GlobalScope.launch(Dispatchers.Main) {
delay(300)
deleteItem(lastItemClickedPosition)
deleteItem(dataListAdapter, items, lastItemClickedId)
}
}
}
@ -101,13 +94,8 @@ class DataListFragment : RealmFragment(), LiveRowRepresentableDataSource, RowRep
activity?.setResult(Activity.RESULT_OK, intent)
activity?.finish()
} else {
lastItemClickedPosition = position
EditableDataActivity.newInstanceForResult(
this,
it.ordinal,
(row as Identifiable).id,
REQUEST_CODE_DETAILS
)
lastItemClickedId = (row as Identifiable).id
EditableDataActivity.newInstanceForResult(this, it.ordinal, lastItemClickedId, REQUEST_CODE_DETAILS)
}
}
}
@ -130,7 +118,8 @@ class DataListFragment : RealmFragment(), LiveRowRepresentableDataSource, RowRep
dataListAdapter = RowRepresentableAdapter(this, this)
val swipeToDelete = SwipeToDeleteCallback(dataListAdapter) { position ->
deleteItem(position)
val itemId = (this.items[position] as Identifiable).id
deleteItem(dataListAdapter, items, itemId)
}
val itemTouchHelper = ItemTouchHelper(swipeToDelete)
@ -153,56 +142,6 @@ class DataListFragment : RealmFragment(), LiveRowRepresentableDataSource, RowRep
}
}
/**
* Delete item
*/
private fun deleteItem(position: Int) {
if (isDetached || activity == null) {
return
}
// Save the delete position & create a copy of the object
val mRecentlyDeletedItem = rowRepresentableForPosition(position)
lastDeletedItemPosition = position
if (mRecentlyDeletedItem is RealmObject) {
// Check if the object is valid for the deletion
if ((mRecentlyDeletedItem as Deletable).isValidForDelete(this.getRealm())) {
deletedItem = getRealm().copyFromRealm(mRecentlyDeletedItem)
getRealm().executeTransaction {
mRecentlyDeletedItem.deleteFromRealm()
}
dataListAdapter.notifyItemRemoved(position)
showUndoSnackBar()
} else {
dataListAdapter.notifyItemChanged(position)
val builder = AlertDialog.Builder(requireContext())
.setMessage((mRecentlyDeletedItem as Deletable).getFailedDeleteMessage())
.setNegativeButton(R.string.ok, null)
builder.show()
}
}
}
/**
* Show undo snack bar
*/
private fun showUndoSnackBar() {
val message = String.format(getString(R.string.data_deleted), this.dataType.localizedTitle(requireContext()))
val snackBar = Snackbar.make(constraintLayout, message, Snackbar.LENGTH_INDEFINITE)
snackBar.setAction(R.string.cancel) {
getRealm().executeTransaction { realm ->
deletedItem?.let {
realm.copyToRealmOrUpdate(it)
dataListAdapter.notifyItemInserted(lastDeletedItemPosition)
}
}
}
snackBar.show()
}
/**
* Set fragment data
*/

@ -0,0 +1,114 @@
package net.pokeranalytics.android.ui.fragment
import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AlertDialog
import androidx.coordinatorlayout.widget.CoordinatorLayout
import com.google.android.material.snackbar.Snackbar
import io.realm.RealmObject
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.fragment.components.RealmFragment
/**
* Deletable Item Fragment
* Don't forget to add a CoordinatorLayout at the top of your XML if you want to display correctly the snack bar
*/
open class DeletableItemFragment : RealmFragment() {
private var deletedItem: RealmObject? = null
private var lastDeletedItemPosition: Int = 0
private var dataListAdapter: RowRepresentableAdapter? = null
private var coordinatorLayout: CoordinatorLayout? = null
private var snackBar: Snackbar? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
this.coordinatorLayout = view.findViewById(R.id.coordinatorLayout)
}
override fun onPause() {
super.onPause()
snackBar?.dismiss()
}
/**
* Delete item
* [dataListAdapter]: Adapter to update
* [items]: List of items which contains the element to delete
* [itemId]: Id of the item to delete
* [container]: View to display the Snackbar
*/
fun deleteItem(dataListAdapter: RowRepresentableAdapter, items: List<*>, itemId: String) {
if (isDetached || activity == null) {
return
}
this.dataListAdapter = dataListAdapter
// Save the delete position & create a copy of the object
val itemPosition = items.indexOfFirst { (it as Identifiable).id == itemId }
val itemToDelete = items.find { (it as Identifiable).id == itemId }
if (itemToDelete is RealmObject && itemPosition != -1) {
val deletableItem = (itemToDelete as Deletable)
// Check if the object is valid for the deletion
if (deletableItem.isValidForDelete(this.getRealm())) {
deletedItem = getRealm().copyFromRealm(itemToDelete)
lastDeletedItemPosition = itemPosition
getRealm().executeTransaction {
itemToDelete.deleteFromRealm()
}
updateUIAfterDeletion(itemPosition)
showUndoSnackBar()
} else {
dataListAdapter.notifyItemChanged(itemPosition)
val status = deletableItem.getDeleteStatus(this.getRealm())
val message = deletableItem.getFailedDeleteMessage(status)
val builder = AlertDialog.Builder(requireContext())
.setMessage(message)
.setNegativeButton(R.string.ok, null)
builder.show()
}
}
}
/**
* Show undo snack bar
*/
private fun showUndoSnackBar() {
val message = String.format(getString(R.string.data_deleted))
this.coordinatorLayout?.let { view ->
snackBar = Snackbar.make(view, message, Snackbar.LENGTH_INDEFINITE)
snackBar?.setAction(R.string.cancel) {
getRealm().executeTransaction { realm ->
deletedItem?.let {
val item = realm.copyToRealmOrUpdate(it)
updateUIAfterUndoDeletion(item)
}
}
}
snackBar?.show()
}
}
/**
* Called once the object has been deleted
*/
open fun updateUIAfterDeletion(itemPosition: Int) {
dataListAdapter?.notifyItemRemoved(itemPosition)
}
/**
* Called once the object has been restored
*/
open fun updateUIAfterUndoDeletion(newItem: RealmObject) {
dataListAdapter?.notifyItemInserted(lastDeletedItemPosition)
}
}

@ -192,7 +192,8 @@ open class EditableDataFragment : RealmFragment(), RowRepresentableDelegate {
activity?.setResult(RESULT_OK, intent)
activity?.finish()
} else {
val message = deletable.getFailedDeleteMessage()
val status = deletable.getDeleteStatus(realm)
val message = deletable.getFailedDeleteMessage(status)
val builder = AlertDialog.Builder(requireContext())
.setMessage(message)
.setNegativeButton(R.string.ok, null)

@ -10,7 +10,7 @@ import android.widget.Toast
import androidx.core.app.ActivityOptionsCompat
import androidx.core.view.isVisible
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import com.google.android.material.chip.ChipGroup
import com.google.android.material.tabs.TabLayout
import io.realm.RealmResults
import io.realm.Sort
import io.realm.kotlin.where
@ -27,7 +27,6 @@ import net.pokeranalytics.android.ui.activity.SessionActivity
import net.pokeranalytics.android.ui.adapter.FeedSessionRowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.FeedTransactionRowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.extensions.ChipGroupExtension
import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager
@ -128,14 +127,19 @@ class FeedFragment : RealmFragment(), RowRepresentableDelegate {
}
}
filters.setOnCheckedChangeListener(object : ChipGroupExtension.SingleSelectionOnCheckedListener() {
override fun onCheckedChanged(group: ChipGroup, checkedId: Int) {
super.onCheckedChanged(group, checkedId)
when (checkedId) {
R.id.filterSessions -> recyclerView.adapter = feedSessionAdapter
R.id.filterTransactions -> recyclerView.adapter = feedTransactionAdapter
tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
when (tab.position) {
0 -> recyclerView.adapter = feedSessionAdapter
1 -> recyclerView.adapter = feedTransactionAdapter
}
}
override fun onTabUnselected(tab: TabLayout.Tab) {
}
override fun onTabReselected(tab: TabLayout.Tab) {
}
})
}
@ -182,9 +186,9 @@ class FeedFragment : RealmFragment(), RowRepresentableDelegate {
*/
private fun createNewSession(isTournament: Boolean) {
val sessionCount = this.feedSessionAdapter.realmResults.size
// val sessionCount = this.feedSessionAdapter.realmResults.size
// if (!AppGuard.isProUser && sessionCount >= AppGuard.MAX_SESSIONS_BEFORE_REQUESTING_SUBSCRIPTION) { // && !BuildConfig.DEBUG
//// Toast.makeText(context, "Please subscribe!", Toast.LENGTH_LONG).show()
// Toast.makeText(context, "Please subscribe!", Toast.LENGTH_LONG).show()
// BillingActivity.newInstance(requireContext())
// return
// }

@ -27,6 +27,7 @@ import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableDiffCallback
import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager
import net.pokeranalytics.android.ui.view.rowrepresentable.SessionRow
import net.pokeranalytics.android.util.extensions.getNextMinuteInMilliseconds
import java.util.*
@ -47,7 +48,6 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate {
private val refreshTimer: Runnable = object : Runnable {
override fun run() {
// Refresh header each 30 seconds
refreshTimer()
handler.postDelayed(this, TIMER_DELAY)
}
@ -194,7 +194,7 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate {
floatingActionButton.animate().scaleX(1f).scaleY(1f).alpha(1f)
.setDuration(animationDuration)
.setInterpolator(OvershootInterpolator()).start()
handler.postDelayed(refreshTimer, TIMER_DELAY)
handler.postDelayed(refreshTimer, Date().getNextMinuteInMilliseconds())
}
SessionState.STARTED -> {
sessionMenu?.findItem(R.id.restart)?.isVisible = true
@ -203,7 +203,7 @@ class SessionFragment : RealmFragment(), RowRepresentableDelegate {
floatingActionButton.animate().scaleX(1f).scaleY(1f).alpha(1f)
.setDuration(animationDuration)
.setInterpolator(OvershootInterpolator()).start()
handler.postDelayed(refreshTimer, TIMER_DELAY)
handler.postDelayed(refreshTimer, Date().getNextMinuteInMilliseconds())
}
SessionState.PAUSED -> {
sessionMenu?.findItem(R.id.restart)?.isVisible = true

@ -2,6 +2,7 @@ package net.pokeranalytics.android.ui.view.rowrepresentable
import android.content.Context
import net.pokeranalytics.android.calculus.ComputedStat
import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
@ -15,7 +16,7 @@ class CustomizableRowRepresentable(
var value: String? = null,
var computedStat: ComputedStat? = null,
var isSelectable: Boolean? = false
) : RowRepresentable {
) : RowRepresentable, Identifiable {
override fun localizedTitle(context: Context): String {
@ -30,4 +31,5 @@ class CustomizableRowRepresentable(
override val viewType: Int = customViewType?.ordinal ?: RowViewType.HEADER_TITLE.ordinal
override var id: String = ""
}

@ -1,6 +1,33 @@
package net.pokeranalytics.android.ui.view.rowrepresentable
import net.pokeranalytics.android.R
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetType
import net.pokeranalytics.android.ui.view.DefaultEditDataSource
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
enum class TransactionTypeRow : RowRepresentable, DefaultEditDataSource
enum class TransactionTypeRow : RowRepresentable, DefaultEditDataSource {
TRANSACTION_ADDITIVE;
override val resId: Int?
get() {
return when (this) {
TRANSACTION_ADDITIVE -> R.string.additive
}
}
override val viewType: Int
get() {
return when (this) {
TRANSACTION_ADDITIVE -> RowViewType.TITLE_SWITCH.ordinal
}
}
override val bottomSheetType: BottomSheetType
get() {
return when (this) {
TRANSACTION_ADDITIVE -> BottomSheetType.NONE
}
}
}

@ -141,3 +141,17 @@ fun Date.startOfYear() : Date {
calendar.set(Calendar.MONTH, 0)
return calendar.time
}
// Return the number of seconds until the next minute
fun Date.getNextMinuteInseconds() : Int {
return (getNextMinuteInMilliseconds() / 1000).toInt()
}
// Return the number of milliseconds until the next minute
fun Date.getNextMinuteInMilliseconds() : Long {
val calendar = Calendar.getInstance()
calendar.add(Calendar.MINUTE, 1)
calendar.set(Calendar.SECOND, 0)
calendar.set(Calendar.MILLISECOND, 0)
return calendar.time.time - this.time
}

@ -2,7 +2,7 @@
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
android:id="@+id/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/container"
@ -16,45 +17,25 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<LinearLayout
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal"
android:padding="8dp">
app:tabMode="fixed">
<com.google.android.material.chip.ChipGroup
android:id="@+id/filters"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:chipSpacing="8dp"
app:singleSelection="true">
<!--
<com.google.android.material.chip.Chip
android:id="@+id/filterAll"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/all" />
-->
<com.google.android.material.chip.Chip
<com.google.android.material.tabs.TabItem
android:id="@+id/filterSessions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="true"
android:text="@string/sessions" />
android:text="@string/sessions"/>
<com.google.android.material.chip.Chip
<com.google.android.material.tabs.TabItem
android:id="@+id/filterTransactions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/operations" />
</com.google.android.material.chip.ChipGroup>
android:text="@string/operations"/>
</LinearLayout>
</com.google.android.material.tabs.TabLayout>
</com.google.android.material.appbar.AppBarLayout>
@ -66,7 +47,7 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appBar"
tools:listitem="@layout/row_feed_session" />
tools:listitem="@layout/row_feed_session"/>
<androidx.appcompat.widget.AppCompatTextView
android:id="@+id/noSessionFound"
@ -85,7 +66,7 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.5"
tools:visibility="visible" />
tools:visibility="visible"/>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/addButton"
@ -99,7 +80,7 @@
android:transitionName="floating_action_button"
app:fabSize="normal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
app:layout_constraintEnd_toEndOf="parent"/>
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/disclaimerContainer"
@ -121,7 +102,7 @@
android:layout_marginBottom="8dp"
android:text="@string/disclaimer"
android:textSize="18sp"
tools:visibility="visible" />
tools:visibility="visible"/>
<com.google.android.material.button.MaterialButton
android:id="@+id/disclaimerDismiss"
@ -131,7 +112,7 @@
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:text="@string/iunderstand" />
android:text="@string/iunderstand"/>
</androidx.appcompat.widget.LinearLayoutCompat>

@ -14,11 +14,12 @@
<string name="pro_upgrade">Devenez membre Pro</string>
<string name="pro_purchase">Passer Pro</string>
<string name="free_trial">d\'essai gratuit</string>
<string name="bankroll_relationship_error_transactions">Cette bankroll contient au moins une transaction empêchant la suppression.</string>
<!-- Not translated -->
<string name="address">Adresse</string>
<string name="suggestions">Suggestions de noms</string>
<string name="data_deleted" formatted="false">%s effacés</string>
<string name="data_deleted">Élément effacé</string>
<string name="end_date_not_possible">La date de fin doit être après la date de début</string>
<string name="save">Sauvegarder</string>
<string name="tournament_name">Nom du tournoi</string>

@ -27,7 +27,7 @@
<string name="address">Address</string>
<string name="suggestions">Naming suggestions</string>
<string name="data_deleted" formatted="false">%s deleted</string>
<string name="data_deleted">Data deleted</string>
<string name="end_date_not_possible">The end date should be after the start date</string>
<string name="save">Save</string>
<string name="tournament_name">Tournament name</string>
@ -39,6 +39,7 @@
<string name="duplicate_tournament_feature_error">A tournament feature with the same name already exists.</string>
<string name="tournament_name_empty_field_error">A tournament name can not be empty.</string>
<string name="duplicate_tournament_name_error">This name already exists.</string>
<string name="bankroll_relationship_error_transactions">One or more transactions are associated with this bankroll, please delete the linked transaction(s) first.</string>
<!-- Translated -->

Loading…
Cancel
Save