Fixes lifecycle crash on DataManagerFragment

od
Laurent 6 years ago
parent 62503c0c2d
commit 31b9607284
  1. 4
      app/src/main/java/net/pokeranalytics/android/ui/fragment/components/BaseFragment.kt
  2. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/BankrollDataFragment.kt
  3. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/CustomFieldDataFragment.kt
  4. 33
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/DataManagerFragment.kt
  5. 27
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/EditableDataFragment.kt
  6. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/LocationDataFragment.kt
  7. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/PlayerDataFragment.kt
  8. 4
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/TransactionDataFragment.kt
  9. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/TransactionTypeDataFragment.kt
  10. 37
      app/src/main/java/net/pokeranalytics/android/ui/fragment/report/AbstractReportFragment.kt
  11. 2
      app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ComparisonReportFragment.kt
  12. 8
      app/src/main/java/net/pokeranalytics/android/ui/fragment/report/ProgressReportFragment.kt
  13. 40
      app/src/main/java/net/pokeranalytics/android/ui/viewmodel/DataManagerViewModel.kt

@ -15,7 +15,9 @@ abstract class BaseFragment : Fragment() {
var parentActivity: BaseActivity? = null var parentActivity: BaseActivity? = null
enum class BundleKey(val value: String) { enum class BundleKey(val value: String) {
STYLE("style") STYLE("style"),
PRIMARY_KEY("primary_key"),
DATA_TYPE("data_type")
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {

@ -37,7 +37,7 @@ class BankrollDataFragment : EditableDataFragment(), StaticRowRepresentableDataS
// Return the item as a Bankroll object // Return the item as a Bankroll object
private val bankroll: Bankroll private val bankroll: Bankroll
get() { get() {
return this.item as Bankroll return this.viewModel.item as Bankroll
} }
private lateinit var defaultCurrency: Currency private lateinit var defaultCurrency: Currency

@ -33,7 +33,7 @@ class CustomFieldDataFragment : EditableDataFragment(), StaticRowRepresentableDa
// Return the item as a Custom TypedCSVField object // Return the item as a Custom TypedCSVField object
private val customField: CustomField private val customField: CustomField
get() { get() {
return this.item as CustomField return this.viewModel.item as CustomField
} }
private val itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.Callback() { private val itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.Callback() {

@ -8,22 +8,26 @@ import android.view.MenuInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.ViewModelProviders
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.ConfigurationException import net.pokeranalytics.android.exceptions.ConfigurationException
import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.interfaces.Deletable
import net.pokeranalytics.android.model.interfaces.Savable import net.pokeranalytics.android.model.interfaces.Savable
import net.pokeranalytics.android.model.interfaces.SaveValidityStatus import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
import net.pokeranalytics.android.ui.activity.DataListActivity import net.pokeranalytics.android.ui.activity.DataListActivity
import net.pokeranalytics.android.ui.activity.EditableDataActivity import net.pokeranalytics.android.ui.activity.EditableDataActivity
import net.pokeranalytics.android.ui.fragment.components.RealmFragment import net.pokeranalytics.android.ui.fragment.components.RealmFragment
import net.pokeranalytics.android.ui.viewmodel.DataManagerViewModel
open class DataManagerFragment : RealmFragment() { open class DataManagerFragment : RealmFragment() {
lateinit var item: Deletable protected val viewModel: DataManagerViewModel by lazy {
ViewModelProviders.of(this).get(DataManagerViewModel::class.java)
}
protected lateinit var liveDataType: LiveData // lateinit var item: Deletable
protected var primaryKey: String? = null // protected lateinit var liveDataType: LiveData
// protected var primaryKey: String? = null
// protected var dataType: Int? = null
var deleteButtonShouldAppear = false var deleteButtonShouldAppear = false
set(value) { set(value) {
@ -37,7 +41,6 @@ open class DataManagerFragment : RealmFragment() {
this.updateMenuUI() this.updateMenuUI()
} }
protected var dataType: Int? = null
private var editableMenu: Menu? = null private var editableMenu: Menu? = null
@ -75,8 +78,8 @@ open class DataManagerFragment : RealmFragment() {
*/ */
private fun loadItem() { private fun loadItem() {
this.item = this.liveDataType.updateOrCreate(this.getRealm(), primaryKey) // this.item = this.liveDataType.updateOrCreate(this.getRealm(), primaryKey)
this.deleteButtonShouldAppear = this.primaryKey != null this.deleteButtonShouldAppear = this.viewModel.primaryKey != null
} }
@ -85,7 +88,7 @@ open class DataManagerFragment : RealmFragment() {
*/ */
protected open fun saveData() { protected open fun saveData() {
val savable = this.item val savable = this.viewModel.item
this.willSaveData() this.willSaveData()
when (savable) { when (savable) {
@ -94,7 +97,7 @@ open class DataManagerFragment : RealmFragment() {
when (status) { when (status) {
SaveValidityStatus.VALID -> { SaveValidityStatus.VALID -> {
this.getRealm().executeTransaction { this.getRealm().executeTransaction {
val managedItem = it.copyToRealmOrUpdate(this.item) val managedItem = it.copyToRealmOrUpdate(this.viewModel.item)
if (managedItem is Savable) { if (managedItem is Savable) {
val uniqueIdentifier = managedItem.id val uniqueIdentifier = managedItem.id
finishActivityWithResult(uniqueIdentifier) finishActivityWithResult(uniqueIdentifier)
@ -132,14 +135,14 @@ open class DataManagerFragment : RealmFragment() {
val realm = this.getRealm() val realm = this.getRealm()
if (this.item.isValidForDelete(realm)) { if (this.viewModel.item.isValidForDelete(realm)) {
val intent = Intent() val intent = Intent()
intent.putExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName, this.item.id) intent.putExtra(DataListActivity.IntentKey.ITEM_DELETED.keyName, this.viewModel.item.id)
activity?.setResult(Activity.RESULT_OK, intent) activity?.setResult(Activity.RESULT_OK, intent)
activity?.finish() activity?.finish()
} else { } else {
val status = this.item.getDeleteStatus(requireContext(), realm) val status = this.viewModel.item.getDeleteStatus(requireContext(), realm)
val message = this.item.getFailedDeleteMessage(status) val message = this.viewModel.item.getFailedDeleteMessage(status)
val builder = AlertDialog.Builder(requireContext()) val builder = AlertDialog.Builder(requireContext())
.setMessage(message) .setMessage(message)
.setNegativeButton(R.string.ok, null) .setNegativeButton(R.string.ok, null)
@ -154,7 +157,7 @@ open class DataManagerFragment : RealmFragment() {
*/ */
private fun finishActivityWithResult(uniqueIdentifier: String) { private fun finishActivityWithResult(uniqueIdentifier: String) {
val intent = Intent() val intent = Intent()
intent.putExtra(EditableDataActivity.IntentKey.DATA_TYPE.keyName, dataType) intent.putExtra(EditableDataActivity.IntentKey.DATA_TYPE.keyName, viewModel.dataType)
intent.putExtra(EditableDataActivity.IntentKey.PRIMARY_KEY.keyName, uniqueIdentifier) intent.putExtra(EditableDataActivity.IntentKey.PRIMARY_KEY.keyName, uniqueIdentifier)
activity?.setResult(Activity.RESULT_OK, intent) activity?.setResult(Activity.RESULT_OK, intent)
activity?.finish() activity?.finish()

@ -10,7 +10,6 @@ import io.realm.RealmModel
import kotlinx.android.synthetic.main.fragment_editable_data.* import kotlinx.android.synthetic.main.fragment_editable_data.*
import kotlinx.android.synthetic.main.fragment_editable_data.view.* import kotlinx.android.synthetic.main.fragment_editable_data.view.*
import net.pokeranalytics.android.R import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.interfaces.Editable import net.pokeranalytics.android.model.interfaces.Editable
import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource
@ -39,6 +38,10 @@ open class EditableDataFragment : DataManagerFragment(), RowRepresentableDelegat
open fun initData() { open fun initData() {
this.viewModel.dataType = this.arguments?.getInt(BundleKey.DATA_TYPE.value)
this.viewModel.primaryKey = this.arguments?.getString(BundleKey.PRIMARY_KEY.value)
viewModel.loadItemWithRealm(getRealm())
val dataSource = getDataSource() val dataSource = getDataSource()
this.rowRepresentableAdapter = RowRepresentableAdapter(getDataSource(), this) this.rowRepresentableAdapter = RowRepresentableAdapter(getDataSource(), this)
//this.rowRepresentableAdapter.setHasStableIds(true) //this.rowRepresentableAdapter.setHasStableIds(true)
@ -57,9 +60,12 @@ open class EditableDataFragment : DataManagerFragment(), RowRepresentableDelegat
* Set fragment data * Set fragment data
*/ */
fun setData(dataType: Int, primaryKey: String?) { fun setData(dataType: Int, primaryKey: String?) {
this.dataType = dataType
this.liveDataType = LiveData.values()[dataType] val bundle = Bundle()
this.primaryKey = primaryKey bundle.putString(BundleKey.PRIMARY_KEY.value, primaryKey)
bundle.putInt(BundleKey.DATA_TYPE.value, dataType)
this.arguments = bundle
} }
override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) { override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) {
@ -69,7 +75,7 @@ open class EditableDataFragment : DataManagerFragment(), RowRepresentableDelegat
override fun onRowValueChanged(value: Any?, row: RowRepresentable) { override fun onRowValueChanged(value: Any?, row: RowRepresentable) {
this.getRealm().executeTransaction { this.getRealm().executeTransaction {
try { try {
(this.item as Editable).updateValue(value, row) (this.viewModel.item as Editable).updateValue(value, row)
} catch (e: Exception) { } catch (e: Exception) {
Crashlytics.log("Exception caught: row = $row, value=$value, class=${this.javaClass}") Crashlytics.log("Exception caught: row = $row, value=$value, class=${this.javaClass}")
throw e throw e
@ -86,13 +92,16 @@ open class EditableDataFragment : DataManagerFragment(), RowRepresentableDelegat
setDisplayHomeAsUpEnabled(true) setDisplayHomeAsUpEnabled(true)
val proxyItem: RealmModel? = this.liveDataType.getData(this.getRealm(), primaryKey) val liveDataType = this.viewModel.liveDataType
val proxyItem: RealmModel? =
liveDataType.getData(this.getRealm(), this.viewModel.primaryKey)
proxyItem?.let { proxyItem?.let {
this.appBar.toolbar.title = this.liveDataType.updateEntityLocalizedTitle(requireContext()) this.appBar.toolbar.title = liveDataType.updateEntityLocalizedTitle(requireContext())
deleteButtonShouldAppear = true deleteButtonShouldAppear = true
isUpdating = true isUpdating = true
} ?: run { } ?: run {
this.appBar.toolbar.title = this.liveDataType.newEntityLocalizedTitle(requireContext()) this.appBar.toolbar.title = liveDataType.newEntityLocalizedTitle(requireContext())
} }
val viewManager = LinearLayoutManager(requireContext()) val viewManager = LinearLayoutManager(requireContext())
@ -106,7 +115,7 @@ open class EditableDataFragment : DataManagerFragment(), RowRepresentableDelegat
* Return the data source * Return the data source
*/ */
open fun getDataSource(): RowRepresentableDataSource { open fun getDataSource(): RowRepresentableDataSource {
return this.item as RowRepresentableDataSource return this.viewModel.item as RowRepresentableDataSource
} }
} }

@ -23,7 +23,7 @@ class LocationDataFragment : EditableDataFragment(), StaticRowRepresentableDataS
// Return the item as a Location object // Return the item as a Location object
private val location: Location private val location: Location
get() { get() {
return this.item as Location return this.viewModel.item as Location
} }
// Loader boolean // Loader boolean

@ -40,7 +40,7 @@ class PlayerDataFragment : EditableDataFragment(), StaticRowRepresentableDataSou
private val player: Player private val player: Player
get() { get() {
return this.item as Player return this.viewModel.item as Player
} }
private var mediaActivity: MediaActivity? = null private var mediaActivity: MediaActivity? = null

@ -30,7 +30,7 @@ class TransactionDataFragment : EditableDataFragment(), StaticRowRepresentableDa
// Return the item as a Transaction object // Return the item as a Transaction object
private val transaction: Transaction private val transaction: Transaction
get() { get() {
return this.item as Transaction return this.viewModel.item as Transaction
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -95,7 +95,7 @@ class TransactionDataFragment : EditableDataFragment(), StaticRowRepresentableDa
super.onRowValueChanged(value, row) super.onRowValueChanged(value, row)
rowRepresentableAdapter.refreshRow(row) rowRepresentableAdapter.refreshRow(row)
if (primaryKey == null) { // automatically change the row for new data if (viewModel.primaryKey == null) { // automatically change the row for new data
GlobalScope.launch(Dispatchers.Main) { GlobalScope.launch(Dispatchers.Main) {
delay(200) delay(200)
when (row) { when (row) {

@ -12,7 +12,7 @@ class TransactionTypeDataFragment : EditableDataFragment(), RowRepresentableData
val transactionType: TransactionType val transactionType: TransactionType
get() { get() {
return this.item as TransactionType return this.viewModel.item as TransactionType
} }
// RowRepresentableDataSource // RowRepresentableDataSource

@ -21,36 +21,41 @@ import net.pokeranalytics.android.util.extensions.findById
abstract class AbstractReportFragment : DataManagerFragment() { abstract class AbstractReportFragment : DataManagerFragment() {
protected val viewModel: ReportViewModel by lazy { protected val reportViewModel: ReportViewModel by lazy {
(requireActivity() as ViewModelHolder).viewModel as ReportViewModel (requireActivity() as ViewModelHolder).viewModel as ReportViewModel
} }
val selectedReport: Report val selectedReport: Report
get() { get() {
return this.viewModel.report return this.reportViewModel.report
} }
val stat: Stat val stat: Stat
get() { get() {
return this.viewModel.stat return this.reportViewModel.stat
} }
override fun onCreate(savedInstanceState: Bundle?) { private fun initData() {
super.onCreate(savedInstanceState)
this.primaryKey = this.selectedReport.options.reportSetupId this.viewModel.dataType = LiveData.REPORT_SETUP.ordinal
this.liveDataType = LiveData.REPORT_SETUP this.viewModel.primaryKey = this.selectedReport.options.reportSetupId
this.deleteButtonShouldAppear = (this.primaryKey != null)
this.saveButtonShouldAppear = this.selectedReport.options.userGenerated this.viewModel.loadItemWithRealm(getRealm())
} }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
initData()
this.deleteButtonShouldAppear = (this.viewModel.primaryKey != null)
this.saveButtonShouldAppear = this.selectedReport.options.userGenerated
setDisplayHomeAsUpEnabled(true) setDisplayHomeAsUpEnabled(true)
setToolbarTitle(this.viewModel.title) setToolbarTitle(this.reportViewModel.title)
} }
override fun saveData() { override fun saveData() {
activity?.let { activity?.let {
@ -66,7 +71,7 @@ abstract class AbstractReportFragment : DataManagerFragment() {
view.findViewById<EditText>(net.pokeranalytics.android.R.id.reportName) view.findViewById<EditText>(net.pokeranalytics.android.R.id.reportName)
nameEditText.inputType = InputType.TYPE_TEXT_FLAG_CAP_SENTENCES nameEditText.inputType = InputType.TYPE_TEXT_FLAG_CAP_SENTENCES
this.primaryKey?.let { id -> this.viewModel.primaryKey?.let { id ->
getRealm().findById<ReportSetup>(id)?.let { reportSetup -> getRealm().findById<ReportSetup>(id)?.let { reportSetup ->
nameEditText.hint = reportSetup.name nameEditText.hint = reportSetup.name
} }
@ -104,11 +109,11 @@ abstract class AbstractReportFragment : DataManagerFragment() {
private fun saveReport(name: String) { private fun saveReport(name: String) {
this.viewModel.title = name this.reportViewModel.title = name
val rs = this.item as ReportSetup val rs = this.viewModel.item as ReportSetup
getRealm().executeTransaction { realm -> getRealm().executeTransaction { realm ->
val firstSave = (this.primaryKey == null) val firstSave = (this.viewModel.primaryKey == null)
if (firstSave) { if (firstSave) {
val options = this.selectedReport.options val options = this.selectedReport.options
rs.name = name rs.name = name
@ -134,9 +139,9 @@ abstract class AbstractReportFragment : DataManagerFragment() {
} }
this.primaryKey = rs.id this.viewModel.primaryKey = rs.id
this.deleteButtonShouldAppear = true this.deleteButtonShouldAppear = true
setToolbarTitle(this.viewModel.title) setToolbarTitle(this.reportViewModel.title)
} }
} }

@ -36,7 +36,7 @@ class ComparisonReportFragment : AbstractReportFragment() {
*/ */
private fun initUI() { private fun initUI() {
viewPager.adapter = ReportPagerAdapter(requireContext(), requireActivity().supportFragmentManager, this.viewModel) viewPager.adapter = ReportPagerAdapter(requireContext(), requireActivity().supportFragmentManager, this.reportViewModel)
// setDisplayHomeAsUpEnabled(true) // setDisplayHomeAsUpEnabled(true)

@ -95,7 +95,7 @@ class ProgressReportFragment : AbstractReportFragment() {
this.chipGroup.addView(chip) this.chipGroup.addView(chip)
} }
this.chipGroup.isVisible = this.viewModel.showAggregationChoices this.chipGroup.isVisible = this.reportViewModel.showAggregationChoices
this.chipGroup.isSingleSelection = true this.chipGroup.isSingleSelection = true
this.chipGroup.check(0) this.chipGroup.check(0)
@ -184,14 +184,14 @@ class ProgressReportFragment : AbstractReportFragment() {
} }
dataSet?.let { ds -> dataSet?.let { ds ->
this.viewModel.defineParameters(stat, aggregationType.axisFormatting) this.reportViewModel.defineParameters(stat, aggregationType.axisFormatting)
when (ds) { when (ds) {
is LineDataSet -> { is LineDataSet -> {
this.viewModel.setLineDataSet(ds) this.reportViewModel.setLineDataSet(ds)
graphFragment.reload(GraphFragment.Style.LINE) graphFragment.reload(GraphFragment.Style.LINE)
} }
is BarDataSet -> { is BarDataSet -> {
this.viewModel.setBarDataSet(ds) this.reportViewModel.setBarDataSet(ds)
graphFragment.reload(GraphFragment.Style.BAR) graphFragment.reload(GraphFragment.Style.BAR)
} }
else -> throw PAIllegalStateException("unmanaged data set") else -> throw PAIllegalStateException("unmanaged data set")

@ -0,0 +1,40 @@
package net.pokeranalytics.android.ui.viewmodel
import androidx.lifecycle.ViewModel
import io.realm.Realm
import net.pokeranalytics.android.model.LiveData
import net.pokeranalytics.android.model.interfaces.Deletable
class DataManagerViewModel : ViewModel() {
/***
* A deletable object
*/
lateinit var item: Deletable
/***
* An optional primary key
*/
var primaryKey: String? = null
/***
* The data type of the managed object
*/
var dataType: Int? = null
set(value) {
field = value
if (value != null) {
this.liveDataType = LiveData.values()[value]
}
}
/***
* A Live Data value
*/
lateinit var liveDataType: LiveData
fun loadItemWithRealm(realm: Realm) {
this.item = liveDataType.updateOrCreate(realm, primaryKey)
}
}
Loading…
Cancel
Save