Add clean deletion for CustomField

dev
Aurelien Hubert 7 years ago
parent 813186b023
commit 13e6fed951
  1. 5
      app/src/main/java/net/pokeranalytics/android/model/interfaces/Manageable.kt
  2. 38
      app/src/main/java/net/pokeranalytics/android/model/realm/CustomField.kt
  3. 44
      app/src/main/java/net/pokeranalytics/android/model/realm/CustomFieldEntry.kt
  4. 22
      app/src/main/java/net/pokeranalytics/android/ui/extensions/UIExtensions.kt
  5. 1
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/DeletableItemFragment.kt
  6. 43
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/CustomFieldDataFragment.kt
  7. 4
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/EditableDataFragment.kt

@ -121,4 +121,9 @@ interface Deletable : Identifiable {
*/ */
fun getFailedDeleteMessage(status: DeleteValidityStatus): Int fun getFailedDeleteMessage(status: DeleteValidityStatus): Int
/**
* A method to override if we need to delete linked objects or other stuff
*/
fun deleteDependencies() {}
} }

@ -7,6 +7,7 @@ import io.realm.RealmList
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.annotations.Ignore import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus
import net.pokeranalytics.android.model.interfaces.NameManageable import net.pokeranalytics.android.model.interfaces.NameManageable
@ -60,13 +61,9 @@ open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDa
updateRowRepresentation() updateRowRepresentation()
} }
override fun localizedTitle(context: Context): String {
return this.name
}
override fun getDisplayName(context: Context): String { @Ignore
return this.name private var entriesToDelete: ArrayList<CustomFieldEntry> = ArrayList()
}
@Ignore @Ignore
override var viewType: Int = RowViewType.TITLE_VALUE_ARROW.ordinal override var viewType: Int = RowViewType.TITLE_VALUE_ARROW.ordinal
@ -74,6 +71,15 @@ open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDa
@Ignore @Ignore
private var rowRepresentation: List<RowRepresentable> = mutableListOf() private var rowRepresentation: List<RowRepresentable> = mutableListOf()
override fun localizedTitle(context: Context): String {
return this.name
}
override fun getDisplayName(context: Context): String {
return this.name
}
override fun adapterRows(): List<RowRepresentable>? { override fun adapterRows(): List<RowRepresentable>? {
return rowRepresentation return rowRepresentation
} }
@ -119,6 +125,13 @@ open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDa
} }
} }
override fun deleteDependencies() {
if (isValid) {
val entries = realm.where<CustomFieldEntry>().equalTo("customField.id", id).findAll()
entries.deleteAllFromRealm()
}
}
override fun editDescriptors(row: RowRepresentable): ArrayList<RowRepresentableEditDescriptor>? { override fun editDescriptors(row: RowRepresentable): ArrayList<RowRepresentableEditDescriptor>? {
return when (row) { return when (row) {
is CustomFieldEntry -> row.editingDescriptors( is CustomFieldEntry -> row.editingDescriptors(
@ -208,8 +221,21 @@ open class CustomField : RealmObject(), NameManageable, StaticRowRepresentableDa
*/ */
fun deleteEntry(entry: CustomFieldEntry) { fun deleteEntry(entry: CustomFieldEntry) {
entries.remove(entry) entries.remove(entry)
entriesToDelete.add(entry)
sortEntries() sortEntries()
updateRowRepresentation() updateRowRepresentation()
} }
/**
* Remove the deleted entries from realm
*/
fun cleanDeletedEntries(realm: Realm) {
realm.executeTransaction {
entriesToDelete.forEach {
realm.where<CustomFieldEntry>().equalTo("id", it.id).findFirst()?.deleteFromRealm()
}
entriesToDelete.clear()
}
}
} }

@ -2,10 +2,16 @@ package net.pokeranalytics.android.model.realm
import android.content.Context import android.content.Context
import android.text.InputType import android.text.InputType
import io.realm.Realm
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.annotations.Ignore import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
import io.realm.kotlin.where
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.ModelException
import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus
import net.pokeranalytics.android.model.interfaces.Manageable
import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetType import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetType
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
@ -16,10 +22,10 @@ import java.util.*
import java.util.Currency import java.util.Currency
open class CustomFieldEntry : RealmObject(), RowRepresentable { open class CustomFieldEntry : RealmObject(), Manageable, RowRepresentable {
@PrimaryKey @PrimaryKey
var id = UUID.randomUUID().toString() override var id = UUID.randomUUID().toString()
var order: Int = 0 var order: Int = 0
var customField: CustomField? = null var customField: CustomField? = null
@ -60,6 +66,40 @@ open class CustomFieldEntry : RealmObject(), RowRepresentable {
) )
} }
override fun isValidForSave(): Boolean {
return true
}
override fun alreadyExists(realm: Realm): Boolean {
return realm.where(this::class.java).notEqualTo("id", this.id).findAll().isNotEmpty()
}
override fun getFailedSaveMessage(status: SaveValidityStatus): Int {
throw ModelException("${this::class.java} getFailedSaveMessage for $status not handled")
}
override fun getFailedDeleteMessage(status: DeleteValidityStatus): Int {
return R.string.cf_entry_delete_popup_message
}
override fun deleteDependencies() {
if (isValid) {
val entries = realm.where<CustomFieldEntry>().equalTo("customField.id", id).findAll()
entries.deleteAllFromRealm()
}
}
override fun updateValue(value: Any?, row: RowRepresentable) {
this.value = value as String? ?: ""
}
override fun isValidForDelete(realm: Realm): Boolean {
if (realm.where<Session>().contains("customFieldEntries.id", id).findAll().isNotEmpty()) {
return false
}
return true
}
/** /**
* Return the amount * Return the amount
*/ */

@ -24,10 +24,6 @@ import net.pokeranalytics.android.util.billing.AppGuard
import java.io.File import java.io.File
// Sizes // Sizes
val Int.dp: Int val Int.dp: Int
get() = (this / Resources.getSystem().displayMetrics.density).toInt() get() = (this / Resources.getSystem().displayMetrics.density).toInt()
@ -71,7 +67,8 @@ fun PokerAnalyticsActivity.openPlayStorePage() {
// Open email for "Contact us" // Open email for "Contact us"
fun PokerAnalyticsActivity.openContactMail(subjectStringRes: Int, filePath: String? = null) { fun PokerAnalyticsActivity.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 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) val emailIntent = Intent(Intent.ACTION_SEND)
@ -105,6 +102,7 @@ fun PokerAnalyticsActivity.openUrl(url: String) {
fun PokerAnalyticsActivity.showAlertDialog(title: Int? = null, message: Int? = null) { fun PokerAnalyticsActivity.showAlertDialog(title: Int? = null, message: Int? = null) {
showAlertDialog(this, title, message) showAlertDialog(this, title, message)
} }
fun PokerAnalyticsFragment.showAlertDialog(title: Int? = null, message: Int? = null) { fun PokerAnalyticsFragment.showAlertDialog(title: Int? = null, message: Int? = null) {
context?.let { context?.let {
showAlertDialog(it, title, message) showAlertDialog(it, title, message)
@ -114,7 +112,10 @@ fun PokerAnalyticsFragment.showAlertDialog(title: Int? = null, message: Int? = n
/** /**
* Create and show an alert dialog * Create and show an alert dialog
*/ */
fun showAlertDialog(context: Context, title: Int? = null, message: Int? = null) { fun showAlertDialog(
context: Context, title: Int? = null, message: Int? = null, showCancelButton: Boolean = false,
positiveAction: (() -> Unit)? = null, negativeAction: (() -> Unit)? = null
) {
val builder = AlertDialog.Builder(context) val builder = AlertDialog.Builder(context)
title?.let { title?.let {
builder.setTitle(title) builder.setTitle(title)
@ -122,7 +123,14 @@ fun showAlertDialog(context: Context, title: Int? = null, message: Int? = null)
message?.let { message?.let {
builder.setMessage(message) builder.setMessage(message)
} }
builder.setPositiveButton(net.pokeranalytics.android.R.string.ok, null) builder.setPositiveButton(net.pokeranalytics.android.R.string.ok) { _, _ ->
positiveAction?.invoke()
}
if (showCancelButton) {
builder.setNegativeButton(R.string.cancel) { _, _ ->
negativeAction?.invoke()
}
}
builder.show() builder.show()
} }

@ -62,6 +62,7 @@ open class DeletableItemFragment : RealmFragment() {
deletedItem = getRealm().copyFromRealm(itemToDelete) deletedItem = getRealm().copyFromRealm(itemToDelete)
lastDeletedItemPosition = itemPosition lastDeletedItemPosition = itemPosition
getRealm().executeTransaction { getRealm().executeTransaction {
itemToDelete.deleteDependencies()
itemToDelete.deleteFromRealm() itemToDelete.deleteFromRealm()
} }
itemHasBeenReInserted = false itemHasBeenReInserted = false

@ -8,20 +8,24 @@ import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.chip.ChipGroup import com.google.android.material.chip.ChipGroup
import io.realm.kotlin.where
import kotlinx.android.synthetic.main.fragment_custom_view.* import kotlinx.android.synthetic.main.fragment_custom_view.*
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.realm.CustomField import net.pokeranalytics.android.model.realm.CustomField
import net.pokeranalytics.android.model.realm.CustomFieldEntry import net.pokeranalytics.android.model.realm.CustomFieldEntry
import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.extensions.ChipGroupExtension import net.pokeranalytics.android.ui.extensions.ChipGroupExtension
import net.pokeranalytics.android.ui.extensions.px import net.pokeranalytics.android.ui.extensions.px
import net.pokeranalytics.android.ui.extensions.showAlertDialog
import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetFragment import net.pokeranalytics.android.ui.fragment.components.bottomsheet.BottomSheetFragment
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomFieldRow import net.pokeranalytics.android.ui.view.rowrepresentable.CustomFieldRow
import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow
import net.pokeranalytics.android.util.NULL_TEXT import net.pokeranalytics.android.util.NULL_TEXT
import timber.log.Timber
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@ -36,8 +40,7 @@ class CustomFieldDataFragment : EditableDataFragment(), StaticRowRepresentableDa
return this.item as CustomField return this.item as CustomField
} }
private val oldRows: ArrayList<RowRepresentable> = ArrayList() private val deletedCustomFieldEntries: ArrayList<CustomFieldEntry> = ArrayList()
private val currentEntriesOrder: ArrayList<CustomFieldEntry> = ArrayList()
private val itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.Callback() { private val itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.Callback() {
@ -74,7 +77,15 @@ class CustomFieldDataFragment : EditableDataFragment(), StaticRowRepresentableDa
return true return true
} }
override fun onMoved(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, fromPos: Int, target: RecyclerView.ViewHolder, toPos: Int, x: Int, y: Int) { override fun onMoved(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
fromPos: Int,
target: RecyclerView.ViewHolder,
toPos: Int,
x: Int,
y: Int
) {
super.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y) super.onMoved(recyclerView, viewHolder, fromPos, target, toPos, x, y)
Collections.swap(customField.entries, fromPos - (CustomFieldRow.values().size + 1), toPos - (CustomFieldRow.values().size + 1)) Collections.swap(customField.entries, fromPos - (CustomFieldRow.values().size + 1), toPos - (CustomFieldRow.values().size + 1))
@ -157,7 +168,7 @@ class CustomFieldDataFragment : EditableDataFragment(), StaticRowRepresentableDa
override fun onRowValueChanged(value: Any?, row: RowRepresentable) { override fun onRowValueChanged(value: Any?, row: RowRepresentable) {
when (row) { when (row) {
is CustomFieldEntry -> { is CustomFieldEntry -> {
row.value = value as String? ?: "" row.updateValue(value, row)
customField.updateRowRepresentation() customField.updateRowRepresentation()
rowRepresentableAdapter.notifyDataSetChanged() rowRepresentableAdapter.notifyDataSetChanged()
} }
@ -174,10 +185,24 @@ class CustomFieldDataFragment : EditableDataFragment(), StaticRowRepresentableDa
super.onRowDeleted(row) super.onRowDeleted(row)
when (row) { when (row) {
is CustomFieldEntry -> { is CustomFieldEntry -> {
if (!row.isValidForDelete(getRealm())) {
val status = row.getDeleteStatus(getRealm())
val message = row.getFailedDeleteMessage(status)
showAlertDialog(requireContext(), R.string.cf_entry_delete_popup_title, message, showCancelButton = true, positiveAction = {
customField.deleteEntry(row) customField.deleteEntry(row)
rowRepresentableAdapter.notifyDataSetChanged() rowRepresentableAdapter.notifyDataSetChanged()
})
return
}
customField.deleteEntry(row)
rowRepresentableAdapter.notifyDataSetChanged()
}
} }
} }
override fun onDataSaved() {
super.onDataSaved()
customField.cleanDeletedEntries(getRealm())
} }
/** /**
@ -238,8 +263,18 @@ class CustomFieldDataFragment : EditableDataFragment(), StaticRowRepresentableDa
onRowSelected(0, it) onRowSelected(0, it)
} }
} }
val entries = getRealm().where<CustomFieldEntry>().equalTo("customField.id", customField.id).findAll()
Timber.d("delete customField: entries: ${entries.size}")
entries.forEach {
val sessions = getRealm().where<Session>().contains("customFieldEntries.id", it.id).findAll()
Timber.d("Sessions: ${sessions.size} with entry value: ${it.value}")
}
} }
/** /**
* Update UI * Update UI
*/ */

@ -161,8 +161,8 @@ open class EditableDataFragment : RealmFragment(), RowRepresentableDelegate {
val uniqueIdentifier = (managedItem as Savable).id val uniqueIdentifier = (managedItem as Savable).id
finishActivityWithResult(uniqueIdentifier) finishActivityWithResult(uniqueIdentifier)
} }
} }
onDataSaved()
} }
else -> { else -> {
val message = savable.getFailedSaveMessage(status) val message = savable.getFailedSaveMessage(status)
@ -223,4 +223,6 @@ open class EditableDataFragment : RealmFragment(), RowRepresentableDelegate {
this.primaryKey = primaryKey this.primaryKey = primaryKey
} }
open fun onDataSaved() {}
} }
Loading…
Cancel
Save