Working on player comments

feature/players
Aurelien Hubert 6 years ago
parent 48868992a4
commit 54b6457fc9
  1. 295
      app/src/main/java/net/pokeranalytics/android/model/migrations/PokerAnalyticsMigration.kt
  2. 79
      app/src/main/java/net/pokeranalytics/android/model/realm/Comment.kt
  3. 96
      app/src/main/java/net/pokeranalytics/android/model/realm/Player.kt
  4. 52
      app/src/main/java/net/pokeranalytics/android/ui/fragment/data/PlayerDataFragment.kt
  5. 26
      app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/CustomFieldRow.kt
  6. 2
      app/src/main/res/layout/fragment_custom_view.xml
  7. 66
      app/src/main/res/layout/fragment_player.xml

@ -2,167 +2,176 @@ package net.pokeranalytics.android.model.migrations
import io.realm.DynamicRealm
import io.realm.RealmMigration
import net.pokeranalytics.android.model.realm.Comment
import timber.log.Timber
import java.util.*
class PokerAnalyticsMigration : RealmMigration {
override fun migrate(realm: DynamicRealm, oldVersion: Long, newVersion: Long) {
// DynamicRealm exposes an editable schema
val schema = realm.schema
var currentVersion = oldVersion.toInt()
Timber.d("*** migrate from $oldVersion to $newVersion")
// Migrate to version 1
if (currentVersion == 0) {
Timber.d("*** Running migration 1")
schema.get("Filter")?.addField("entityType", Int::class.java)?.setNullable("entityType", true)
schema.get("FilterElement")?.let {
it.setNullable("filterName", true)
it.setNullable("sectionName", true)
}
schema.get("FilterElementBlind")?.renameField("code", "currencyCode")
currentVersion++
}
// Migrate to version 2
if (currentVersion == 1) {
Timber.d("*** Running migration ${currentVersion + 1}")
schema.rename("FilterElement", "FilterCondition")
schema.get("Filter")?.renameField("filterElements", "filterConditions")
schema.get("SessionSet")?.let {
it.addField("id", String::class.java).setRequired("id", true)
it.addPrimaryKey("id")
}
currentVersion++
}
// Migrate to version 3
if (currentVersion == 2) {
Timber.d("*** Running migration ${currentVersion + 1}")
schema.rename("Report", "ReportSetup")
schema.get("Filter")?.removeField("entityType")
schema.get("Session")?.let {
it.addField("blinds", String::class.java).transform {
}
}
schema.get("FilterCondition")?.let {
it.removeField("blindValues")
it.removeField("numericValues")
it.addField("operator", Integer::class.java)
it.addField("intValue", Integer::class.java)
it.addRealmListField("intValues", Integer::class.java)
it.addField("doubleValue", Double::class.java).setNullable("doubleValue", true)
it.addRealmListField("doubleValues", Double::class.java)
if (it.isRequired("doubleValues")) {
it.setRequired("doubleValues", false)
}
it.addField("stringValue", String::class.java)
}
schema.get("ComputableResult")?.removeField("sessionSet")
schema.get("Bankroll")?.addField("initialValue", Double::class.java)
currentVersion++
}
// Migrate to version 4
if (currentVersion == 3) {
Timber.d("*** Running migration ${currentVersion + 1}")
schema.get("Result")?.addField("numberOfRebuy", Double::class.java)?.setNullable("numberOfRebuy", true)
currentVersion++
}
// Migrate to version 5
if (currentVersion == 4) {
Timber.d("*** Running migration ${currentVersion + 1}")
schema.get("Bankroll")?.removeField("transactions")
currentVersion++
}
// Migrate to version 6
if (currentVersion == 5) {
Timber.d("*** Running migration ${currentVersion + 1}")
schema.get("Transaction")?.let {
it.addField("dayOfWeek", Integer::class.java)
it.addField("month", Integer::class.java)
it.addField("year", Integer::class.java)
it.addField("dayOfMonth", Integer::class.java)
}
val cfEntry = schema.create("CustomFieldEntry")?.let {
it.addField("id", String::class.java).setRequired("id", true)
it.addPrimaryKey("id")
it.addField("value", String::class.java).setNullable("value", false)
it.addField("order", Integer::class.java).setNullable("order", false)
// DynamicRealm exposes an editable schema
val schema = realm.schema
var currentVersion = oldVersion.toInt()
Timber.d("*** migrate from $oldVersion to $newVersion")
// Migrate to version 1
if (currentVersion == 0) {
Timber.d("*** Running migration 1")
schema.get("Filter")?.addField("entityType", Int::class.java)?.setNullable("entityType", true)
schema.get("FilterElement")?.let {
it.setNullable("filterName", true)
it.setNullable("sectionName", true)
}
schema.get("FilterElementBlind")?.renameField("code", "currencyCode")
currentVersion++
}
// Migrate to version 2
if (currentVersion == 1) {
Timber.d("*** Running migration ${currentVersion + 1}")
schema.rename("FilterElement", "FilterCondition")
schema.get("Filter")?.renameField("filterElements", "filterConditions")
schema.get("SessionSet")?.let {
it.addField("id", String::class.java).setRequired("id", true)
it.addPrimaryKey("id")
}
currentVersion++
}
// Migrate to version 3
if (currentVersion == 2) {
Timber.d("*** Running migration ${currentVersion + 1}")
schema.rename("Report", "ReportSetup")
schema.get("Filter")?.removeField("entityType")
schema.get("Session")?.let {
it.addField("blinds", String::class.java).transform {
}
}
schema.get("FilterCondition")?.let {
it.removeField("blindValues")
it.removeField("numericValues")
it.addField("operator", Integer::class.java)
it.addField("intValue", Integer::class.java)
it.addRealmListField("intValues", Integer::class.java)
it.addField("doubleValue", Double::class.java).setNullable("doubleValue", true)
it.addRealmListField("doubleValues", Double::class.java)
if (it.isRequired("doubleValues")) {
it.setRequired("doubleValues", false)
}
it.addField("stringValue", String::class.java)
}
schema.get("ComputableResult")?.removeField("sessionSet")
schema.get("Bankroll")?.addField("initialValue", Double::class.java)
currentVersion++
}
// Migrate to version 4
if (currentVersion == 3) {
Timber.d("*** Running migration ${currentVersion + 1}")
schema.get("Result")?.addField("numberOfRebuy", Double::class.java)?.setNullable("numberOfRebuy", true)
currentVersion++
}
// Migrate to version 5
if (currentVersion == 4) {
Timber.d("*** Running migration ${currentVersion + 1}")
schema.get("Bankroll")?.removeField("transactions")
currentVersion++
}
// Migrate to version 6
if (currentVersion == 5) {
Timber.d("*** Running migration ${currentVersion + 1}")
schema.get("Transaction")?.let {
it.addField("dayOfWeek", Integer::class.java)
it.addField("month", Integer::class.java)
it.addField("year", Integer::class.java)
it.addField("dayOfMonth", Integer::class.java)
}
val cfEntry = schema.create("CustomFieldEntry")?.let {
it.addField("id", String::class.java).setRequired("id", true)
it.addPrimaryKey("id")
it.addField("value", String::class.java).setNullable("value", false)
it.addField("order", Integer::class.java).setNullable("order", false)
// it.addRealmObjectField("customField", it).setNullable("customField", false)
it.addField("numericValue", Double::class.java).setNullable("numericValue", true)
}
cfEntry?.let { customFieldEntrySchema ->
schema.get("CustomField")?.let {
it.addField("type", Integer::class.java).setNullable("type", false)
it.addField("duplicateValue", Boolean::class.java)
it.addField("sortCondition", Integer::class.java).setRequired("sortCondition", true)
it.addRealmListField("entries", customFieldEntrySchema)
}
schema.get("Session")?.let {
it.addField("startDateHourMinuteComponent", Double::class.java)
.setNullable("startDateHourMinuteComponent", true)
it.addField("endDateHourMinuteComponent", Double::class.java)
.setNullable("endDateHourMinuteComponent", true)
it.addRealmListField("customFieldEntries", customFieldEntrySchema)
}
}
schema.get("ReportSetup")?.let {
it.addRealmListField("statIds", Int::class.java).setNullable("statIds", true)
it.addRealmListField("criteriaCustomFieldIds", String::class.java)
it.addRealmListField("criteriaIds", Int::class.java).setNullable("criteriaIds", true)
it.removeField("filters")
schema.get("Filter")?.let { filterSchema ->
it.addRealmObjectField("filter", filterSchema)
}
}
schema.get("Filter")?.addField("filterableTypeUniqueIdentifier", Integer::class.java)
schema.get("Filter")?.addField("useCount", Int::class.java)
schema.get("Filter")?.removeField("usageCount")
currentVersion++
}
// Migrate to version 7
if (currentVersion == 6) {
Timber.d("*** Running migration ${currentVersion + 1}")
schema.get("TransactionType")?.addField("useCount", Int::class.java)
currentVersion++
}
it.addField("numericValue", Double::class.java).setNullable("numericValue", true)
}
cfEntry?.let { customFieldEntrySchema ->
schema.get("CustomField")?.let {
it.addField("type", Integer::class.java).setNullable("type", false)
it.addField("duplicateValue", Boolean::class.java)
it.addField("sortCondition", Integer::class.java).setRequired("sortCondition", true)
it.addRealmListField("entries", customFieldEntrySchema)
}
schema.get("Session")?.let {
it.addField("startDateHourMinuteComponent", Double::class.java)
.setNullable("startDateHourMinuteComponent", true)
it.addField("endDateHourMinuteComponent", Double::class.java)
.setNullable("endDateHourMinuteComponent", true)
it.addRealmListField("customFieldEntries", customFieldEntrySchema)
}
}
schema.get("ReportSetup")?.let {
it.addRealmListField("statIds", Int::class.java).setNullable("statIds", true)
it.addRealmListField("criteriaCustomFieldIds", String::class.java)
it.addRealmListField("criteriaIds", Int::class.java).setNullable("criteriaIds", true)
it.removeField("filters")
schema.get("Filter")?.let { filterSchema ->
it.addRealmObjectField("filter", filterSchema)
}
}
schema.get("Filter")?.addField("filterableTypeUniqueIdentifier", Integer::class.java)
schema.get("Filter")?.addField("useCount", Int::class.java)
schema.get("Filter")?.removeField("usageCount")
currentVersion++
}
// Migrate to version 7
if (currentVersion == 6) {
Timber.d("*** Running migration ${currentVersion + 1}")
schema.get("TransactionType")?.addField("useCount", Int::class.java)
currentVersion++
}
// Migrate to version 8
if (currentVersion == 7) {
schema.create("Comment")?.let {
it.addField("id", String::class.java).setRequired("id", true)
it.addField("content", String::class.java)
it.addField("date", Date::class.java)
}
schema.get("Player")?.let {
it.addField("summary", String::class.java).setRequired("summary", true)
it.addField("color", Int::class.java).setNullable("color", true)
it.addField("picture", String::class.java)
it.addRealmListField("comments", Comment::class.java)
}
currentVersion++
}
}
}
override fun equals(other: Any?): Boolean {
return other is RealmMigration

@ -0,0 +1,79 @@
package net.pokeranalytics.android.model.realm
import android.content.Context
import androidx.fragment.app.Fragment
import io.realm.Realm
import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey
import net.pokeranalytics.android.R
import net.pokeranalytics.android.exceptions.ModelException
import net.pokeranalytics.android.model.interfaces.DeleteValidityStatus
import net.pokeranalytics.android.model.interfaces.Identifiable
import net.pokeranalytics.android.model.interfaces.Manageable
import net.pokeranalytics.android.model.interfaces.SaveValidityStatus
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.fragment.components.input.InputFragment
import net.pokeranalytics.android.ui.fragment.components.input.InputFragmentType
import net.pokeranalytics.android.ui.view.RowEditableDataSource
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.util.NULL_TEXT
import java.util.*
open class Comment : RealmObject(), Manageable, RowRepresentable {
@PrimaryKey
override var id = UUID.randomUUID().toString()
var content: String = ""
var date: Date = Date()
@Ignore
override val realmObjectClass: Class<out Identifiable> = Comment::class.java
@Ignore
override val viewType: Int = RowViewType.TITLE.ordinal
@Ignore
override val inputFragmentType: InputFragmentType = InputFragmentType.EDIT_TEXT_MULTI_LINES
override fun localizedTitle(context: Context): String {
return context.getString(R.string.comment)
}
override fun getDisplayName(context: Context): String {
return if (content.isNotEmpty()) content else NULL_TEXT
}
override fun startEditing(dataSource: Any?, parent: Fragment?) {
if (parent == null) return
if (parent !is RowRepresentableDelegate) return
val data = RowEditableDataSource()
data.append(this.content, R.string.value)
InputFragment.buildAndShow(this, parent, data, isDeletable = true)
}
override fun updateValue(value: Any?, row: RowRepresentable) {
this.content = value as String? ?: ""
}
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 isValidForDelete(realm: Realm): Boolean {
return true
}
override fun getFailedDeleteMessage(status: DeleteValidityStatus): Int {
return R.string.cf_entry_delete_popup_message
}
}

@ -2,6 +2,7 @@ package net.pokeranalytics.android.model.realm
import android.content.Context
import io.realm.Realm
import io.realm.RealmList
import io.realm.RealmObject
import io.realm.annotations.Ignore
import io.realm.annotations.PrimaryKey
@ -13,38 +14,38 @@ import net.pokeranalytics.android.model.interfaces.NameManageable
import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.PlayerRow
import net.pokeranalytics.android.util.NULL_TEXT
import net.pokeranalytics.android.util.extensions.isSameDay
import net.pokeranalytics.android.util.extensions.mediumDate
import java.util.*
open class Player : RealmObject(), NameManageable, Deletable, StaticRowRepresentableDataSource, RowRepresentable {
companion object {
val rowRepresentation: List<RowRepresentable> by lazy {
val rows = ArrayList<RowRepresentable>()
rows.add(PlayerRow.IMAGE)
rows.add(PlayerRow.NAME)
rows.add(PlayerRow.SUMMARY)
rows
}
}
@Ignore
override val realmObjectClass: Class<out Identifiable> = Player::class.java
@PrimaryKey
override var id = UUID.randomUUID().toString()
// The name of the player
override var name: String = ""
@Ignore
override val viewType: Int = RowViewType.ROW_PLAYER.ordinal
// New fields
var summary: String = ""
var color: Int? = null
var picture: String? = null
var comments: RealmList<Comment> = RealmList()
@Ignore
override val realmObjectClass: Class<out Identifiable> = Player::class.java
@Ignore
override val viewType: Int = RowViewType.ROW_PLAYER.ordinal
@Ignore
private var rowRepresentation: List<RowRepresentable> = mutableListOf()
@Ignore
private var commentsToDelete: ArrayList<Comment> = ArrayList()
override fun isValidForDelete(realm: Realm): Boolean {
@ -80,6 +81,42 @@ open class Player : RealmObject(), NameManageable, Deletable, StaticRowRepresent
}
}
/**
* Update the row representation
*/
private fun updatedRowRepresentationForCurrentState(): List<RowRepresentable> {
val rows = ArrayList<RowRepresentable>()
rows.add(PlayerRow.IMAGE)
rows.add(PlayerRow.NAME)
rows.add(PlayerRow.SUMMARY)
if (comments.size > 0) {
rows.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, R.string.comments))
val currentCommentCalendar = Calendar.getInstance()
val currentDateCalendar = Calendar.getInstance()
val commentsToDisplay = ArrayList<Comment>()
commentsToDisplay.addAll(comments)
commentsToDisplay.sortByDescending { it.date }
commentsToDisplay.forEachIndexed { index, comment ->
currentCommentCalendar.time = comment.date
if (!currentCommentCalendar.isSameDay(currentDateCalendar) || index == 0) {
currentDateCalendar.time = currentCommentCalendar.time
rows.add(CustomizableRowRepresentable(RowViewType.HEADER_TITLE, title = currentDateCalendar.time.mediumDate()))
}
rows.add(comment)
}
}
return rows
}
/**
* Return if the player has a picture
*/
@ -87,4 +124,31 @@ open class Player : RealmObject(), NameManageable, Deletable, StaticRowRepresent
return picture != null && picture?.isNotEmpty() == true
}
/**
* Update row representation
*/
fun updateRowRepresentation() {
this.rowRepresentation = this.updatedRowRepresentationForCurrentState()
}
/**
* Add an entry
*/
fun addComent(): Comment {
val entry = Comment()
this.comments.add(entry)
updateRowRepresentation()
return entry
}
/**
* Delete an entry
*/
fun deleteComment(comment: Comment) {
commentsToDelete.add(comment)
this.comments.remove(comment)
updateRowRepresentation()
}
}

@ -8,7 +8,9 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import kotlinx.android.synthetic.main.fragment_player.*
import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.realm.Comment
import net.pokeranalytics.android.model.realm.Player
import net.pokeranalytics.android.ui.activity.ColorPickerActivity
import net.pokeranalytics.android.ui.activity.components.MediaActivity
@ -105,9 +107,47 @@ class PlayerDataFragment : EditableDataFragment(), StaticRowRepresentableDataSou
}
override fun onRowValueChanged(value: Any?, row: RowRepresentable) {
super.onRowValueChanged(value, row)
when (row) {
PlayerRow.NAME -> rowRepresentableAdapter.refreshRow(PlayerRow.IMAGE)
is Comment -> {
row.updateValue(value, row)
player.updateRowRepresentation()
rowRepresentableAdapter.notifyDataSetChanged()
}
else -> {
super.onRowValueChanged(value, row)
if (row == PlayerRow.NAME) {
rowRepresentableAdapter.refreshRow(PlayerRow.IMAGE)
}
}
}
}
override fun onRowDeleted(row: RowRepresentable) {
super.onRowDeleted(row)
when (row) {
is Comment -> {
if (!row.isValidForDelete(getRealm())) {
} else {
player.deleteComment(row)
rowRepresentableAdapter.notifyDataSetChanged()
}
/*
if (!row.isValidForDelete(getRealm())) {
val status = row.getDeleteStatus(requireContext(), getRealm())
val message = row.getFailedDeleteMessage(status)
showAlertDialog(requireContext(), R.string.cf_entry_delete_popup_title, message, showCancelButton = true, positiveAction = {
customField.deleteEntry(row)
rowRepresentableAdapter.notifyDataSetChanged()
})
} else {
customField.deleteEntry(row)
rowRepresentableAdapter.notifyDataSetChanged()
}
*/
}
}
}
@ -117,9 +157,17 @@ class PlayerDataFragment : EditableDataFragment(), StaticRowRepresentableDataSou
private fun initUI() {
mediaActivity = parentActivity as MediaActivity?
player.updateRowRepresentation()
if (!deleteButtonShouldAppear) {
onRowSelected(0, SimpleRow.NAME)
}
addComment.setOnClickListener {
val comment = player.addComent()
rowRepresentableAdapter.notifyDataSetChanged()
onRowSelected(-1, comment)
}
}
/**

@ -7,8 +7,8 @@ import net.pokeranalytics.android.model.realm.CustomField
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.fragment.components.input.InputFragment
import net.pokeranalytics.android.ui.fragment.components.input.InputFragmentType
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowEditableDataSource
import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.RowViewType
enum class CustomFieldRow : RowRepresentable {
@ -64,30 +64,6 @@ enum class CustomFieldRow : RowRepresentable {
return list
}
/*
override fun editingDescriptors(map: Map<String, Any?>): ArrayList<RowEditableDescriptor>? {
return when (type) {
Type.LIST.uniqueIdentifier -> {
val defaultValue: Any? by map
val data: RealmList<CustomFieldEntry>? by map
arrayListOf(
RowEditableDescriptor(defaultValue, staticData = data)
)
}
else -> {
val defaultValue: Double? by map
arrayListOf(
RowEditableDescriptor(
defaultValue, inputType = InputType.TYPE_CLASS_NUMBER
or InputType.TYPE_NUMBER_FLAG_DECIMAL
or InputType.TYPE_NUMBER_FLAG_SIGNED
)
)
}
}
}
*/
override fun startEditing(dataSource: Any?, parent: Fragment?) {
if (dataSource == null) return
if (dataSource !is CustomField) return

@ -9,6 +9,8 @@
android:id="@+id/nestedScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="56dp"
android:clipToPadding="false"
android:fillViewport="true"
app:layout_behavior="@string/appbar_scrolling_view_behavior">

@ -1,13 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
<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:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true">
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBar"
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/PokerAnalyticsTheme.Toolbar.Session"
app:layout_constraintEnd_toEndOf="parent"
@ -21,18 +22,61 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:title="@string/stats" />
app:title="@string/player" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:id="@+id/nestedScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:paddingBottom="56dp"
android:clipToPadding="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
<com.google.android.material.bottomappbar.BottomAppBar
android:id="@+id/bottomBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
app:contentInsetEnd="8dp"
app:contentInsetStart="8dp"
app:layout_constraintBottom_toBottomOf="@+id/recyclerView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appBar" />
app:layout_constraintStart_toStartOf="parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/addComment"
style="@style/PokerAnalyticsTheme.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:text="@string/add_comment" />
</FrameLayout>
</com.google.android.material.bottomappbar.BottomAppBar>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Loading…
Cancel
Save