diff --git a/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt b/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt index 42d91b9d..bf86aea3 100644 --- a/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt +++ b/app/src/main/java/net/pokeranalytics/android/model/realm/Session.kt @@ -27,11 +27,10 @@ import net.pokeranalytics.android.ui.adapter.UnmanagedRowRepresentableException import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor import net.pokeranalytics.android.ui.view.RowViewType -import net.pokeranalytics.android.ui.view.rowrepresentable.HeaderRowRepresentable +import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable import net.pokeranalytics.android.ui.view.rowrepresentable.SeparatorRowRepresentable import net.pokeranalytics.android.ui.view.rowrepresentable.SessionRow import net.pokeranalytics.android.util.* -import timber.log.Timber import java.util.* import java.util.Currency import kotlin.collections.ArrayList @@ -475,7 +474,7 @@ open class Session : RealmObject(), SessionInterface, Manageable, StaticRowRepre when (getState()) { SessionState.STARTED -> { rows.add( - HeaderRowRepresentable( + CustomizableRowRepresentable( RowViewType.HEADER_TITLE_AMOUNT_BIG, title = getFormattedDuration(), computedStat = ComputedStat(Stat.NETRESULT, result?.net ?: 0.0) @@ -485,7 +484,7 @@ open class Session : RealmObject(), SessionInterface, Manageable, StaticRowRepre } SessionState.PAUSED -> { rows.add( - HeaderRowRepresentable( + CustomizableRowRepresentable( RowViewType.HEADER_TITLE_AMOUNT_BIG, resId = R.string.pause, computedStat = ComputedStat(Stat.NETRESULT, result?.net ?: 0.0) @@ -495,14 +494,14 @@ open class Session : RealmObject(), SessionInterface, Manageable, StaticRowRepre } SessionState.FINISHED -> { rows.add( - HeaderRowRepresentable( + CustomizableRowRepresentable( RowViewType.HEADER_TITLE_AMOUNT_BIG, title = getFormattedDuration(), computedStat = ComputedStat(Stat.NETRESULT, result?.net ?: 0.0) ) ) rows.add( - HeaderRowRepresentable( + CustomizableRowRepresentable( RowViewType.HEADER_TITLE_AMOUNT, resId = R.string.hour_rate_without_pauses, computedStat = ComputedStat(Stat.HOURLY_RATE, this.hourlyRate) @@ -513,7 +512,7 @@ open class Session : RealmObject(), SessionInterface, Manageable, StaticRowRepre /* if (!isTournament()) { rows.add( - HeaderRowRepresentable( + CustomizableRowRepresentable( RowViewType.HEADER_TITLE_VALUE, resId = R.string.bankroll_variation, computedStat = ComputedStat(Stat.HOURLY_RATE, 0.0) diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/HistoryFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/HistoryFragment.kt index 5cb2b52d..2cf6fd02 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/HistoryFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/HistoryFragment.kt @@ -24,7 +24,7 @@ import net.pokeranalytics.android.ui.view.HistorySessionDiffCallback import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.SmoothScrollLinearLayoutManager -import net.pokeranalytics.android.ui.view.rowrepresentable.HeaderRowRepresentable +import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable import net.pokeranalytics.android.util.getMonthAndYear import net.pokeranalytics.android.util.isSameDay import net.pokeranalytics.android.util.isSameMonth @@ -128,7 +128,7 @@ class HistoryFragment : PokerAnalyticsFragment(), LiveRowRepresentableDataSource if (groupedByDay) { if (!calendar.isSameDay(currentCalendar) || index == 0) { calendar.time = currentCalendar.time - val header = HeaderRowRepresentable( + val header = CustomizableRowRepresentable( customViewType = RowViewType.HEADER_TITLE, title = session.creationDate.longDate() ) @@ -137,7 +137,7 @@ class HistoryFragment : PokerAnalyticsFragment(), LiveRowRepresentableDataSource } else { if (!calendar.isSameMonth(currentCalendar) || index == 0) { calendar.time = currentCalendar.time - val header = HeaderRowRepresentable( + val header = CustomizableRowRepresentable( customViewType = RowViewType.HEADER_TITLE, title = session.creationDate.getMonthAndYear() ) diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/LocationDataFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/LocationDataFragment.kt index d86f5b13..b4f4be86 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/LocationDataFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/LocationDataFragment.kt @@ -1,13 +1,20 @@ package net.pokeranalytics.android.ui.fragment +import android.os.Bundle +import android.view.View +import com.google.android.libraries.places.api.model.PlaceLikelihood +import net.pokeranalytics.android.R import net.pokeranalytics.android.model.realm.Location import net.pokeranalytics.android.ui.adapter.RowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.helpers.PlacePickerManager import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentableEditDescriptor +import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.rowrepresentable.LocationRow import net.pokeranalytics.android.ui.view.rowrepresentable.SimpleRow +import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable +import timber.log.Timber /** * Custom EditableDataFragment to manage the LOCATE_ME case @@ -22,29 +29,52 @@ class LocationDataFragment : EditableDataFragment(), StaticRowRepresentableDataS // Loader boolean private var isLookingForPlaces: Boolean = false + private var placesForRows: HashMap = HashMap() + private var rowPlaces: ArrayList = ArrayList() + private var locationActivated = false + + val rows = ArrayList() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + shouldOpenKeyboard = false + locationActivated = parentActivity.hasLocationPermissionGranted() + + if (isUpdating) { + + // If we update a location, we set the switch to the correct value + locationActivated = location.latitude != null && location.longitude != null + + } else if (locationActivated) { + + // If we create a new location, we try to locate the user by default + isLookingForPlaces = true + getSuggestionsPlaces() + + } + + updateAdapterUI() + } override fun getDataSource(): RowRepresentableDataSource { return this } override fun adapterRows(): List? { - val rows = ArrayList() - rows.add(SimpleRow.NAME) - rows.addAll(LocationRow.values()) return rows } override fun stringForRow(row: RowRepresentable): String { return when (row) { SimpleRow.NAME -> location.name - LocationRow.ADDRESS -> location.address else -> return super.stringForRow(row) } } override fun boolForRow(row: RowRepresentable): Boolean { return when (row) { - LocationRow.LOCATE_ME -> return isLookingForPlaces + LocationRow.LOCATION_PERMISSION_SWITCH -> return locationActivated else -> super.boolForRow(row) } } @@ -58,17 +88,19 @@ class LocationDataFragment : EditableDataFragment(), StaticRowRepresentableDataS SimpleRow.NAME.resId ) ) - LocationRow.ADDRESS -> data.add( - RowRepresentableEditDescriptor( - location.address, - LocationRow.ADDRESS.resId - ) - ) } return data } override fun onRowSelected(position: Int, row: RowRepresentable, fromAction: Boolean) { + + // If we click on a location row, save the location + placesForRows[row]?.place?.let { place -> + location.setPlace(place) + saveData() + return + } + when (row) { LocationRow.LOCATE_ME -> { isLookingForPlaces = true @@ -80,11 +112,108 @@ class LocationDataFragment : EditableDataFragment(), StaticRowRepresentableDataS } override fun onRowValueChanged(value: Any?, row: RowRepresentable) { - super.onRowValueChanged(value, row) when (row) { - LocationRow.LOCATE_ME -> { + LocationRow.LOCATION_PERMISSION_SWITCH -> { + if (value is Boolean && value != locationActivated) { + rowPlaces.clear() + locationActivated = value + isLookingForPlaces = value + if (value) { + updateAdapterUI() + getSuggestionsPlaces() + } else { + clearCurrentLocation() + updateAdapterUI() + } + } + } + else -> super.onRowValueChanged(value, row) + } + } + + /** + * Clear current location + */ + private fun clearCurrentLocation() { + location.latitude = null + location.longitude = null + } + + /** + * Refresh rows + */ + private fun refreshRows() { + rows.clear() + rows.add(SimpleRow.NAME) + rows.add(LocationRow.LOCATION_PERMISSION_SWITCH) + + if (isLookingForPlaces) { + rows.add(LocationRow.LOCATION_LOADER) + } + + if (locationActivated && rowPlaces.size > 0) { + rows.add(CustomizableRowRepresentable(resId = R.string.suggestions)) + for (row in rowPlaces) { + rows.add(row) + } + } + } + + /** + * Update UI adapter + */ + private fun updateAdapterUI() { + + val currentRowsSize = rows.size + refreshRows() + val newRowsSize = rows.size + + if (currentRowsSize < newRowsSize) { + rowRepresentableAdapter.notifyItemRangeInserted(currentRowsSize, newRowsSize - currentRowsSize) + } else { + rowRepresentableAdapter.notifyItemRangeRemoved(newRowsSize, currentRowsSize - newRowsSize) + } + } + + /** + * Return the places around the user + */ + private fun getSuggestionsPlaces() { + + val maxResults = 5 + + parentActivity.askForPlacesRequest { success, places -> + + if (success) { + // Try to get the location of the user + parentActivity.findCurrentLocation {currentLocation -> + currentLocation?.let { + Timber.d("Current location: ${it.latitude}, ${it.longitude}") + location.latitude = currentLocation.latitude + location.longitude = currentLocation.longitude + } + } + } + + if (success && places.size > 0) { + locationActivated = true + rowPlaces.clear() + placesForRows.clear() + + for ((index, place) in places.withIndex()) { + if (index < maxResults) { + val row = CustomizableRowRepresentable(customViewType = RowViewType.LOCATION_TITLE, title = place.place.name, isSelectable = true) + rowPlaces.add(row) + placesForRows[row] = place + } + } + + isLookingForPlaces = false + updateAdapterUI() + } else { isLookingForPlaces = false - rowRepresentableAdapter.notifyDataSetChanged() + locationActivated = false + updateAdapterUI() } } } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/fragment/StatsFragment.kt b/app/src/main/java/net/pokeranalytics/android/ui/fragment/StatsFragment.kt index 684a1172..9dbb5c34 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/fragment/StatsFragment.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/fragment/StatsFragment.kt @@ -16,7 +16,7 @@ import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource import net.pokeranalytics.android.ui.fragment.components.SessionObserverFragment import net.pokeranalytics.android.ui.view.RowRepresentable -import net.pokeranalytics.android.ui.view.rowrepresentable.HeaderRowRepresentable +import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable import net.pokeranalytics.android.util.NULL_TEXT class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSource { @@ -145,7 +145,7 @@ class StatsFragment : SessionObserverFragment(), StaticRowRepresentableDataSourc val rows: ArrayList = ArrayList() results.forEach { results -> - rows.add(HeaderRowRepresentable(title = results.group.name)) + rows.add(CustomizableRowRepresentable(title = results.group.name)) results.group.stats?.forEach { stat -> rows.add(StatRepresentable(stat, results.computedStat(stat))) } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/HistorySessionDiffCallback.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/HistorySessionDiffCallback.kt index a0badd28..09ecd30a 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/HistorySessionDiffCallback.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/HistorySessionDiffCallback.kt @@ -3,7 +3,7 @@ package net.pokeranalytics.android.ui.view import androidx.annotation.Nullable import androidx.recyclerview.widget.DiffUtil import net.pokeranalytics.android.model.realm.Session -import net.pokeranalytics.android.ui.view.rowrepresentable.HeaderRowRepresentable +import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable class HistorySessionDiffCallback(var newRows: List, var oldRows: List) : DiffUtil.Callback() { @@ -22,9 +22,9 @@ class HistorySessionDiffCallback(var newRows: List, var oldRow val session1 = oldRows[oldItemPosition] as Session val session2 = newRows[newItemPosition] as Session return session1.id == session2.id - } else if (oldRows[oldItemPosition] is HeaderRowRepresentable && newRows[newItemPosition] is HeaderRowRepresentable) { - val header1 = oldRows[oldItemPosition] as HeaderRowRepresentable - val header2 = newRows[newItemPosition] as HeaderRowRepresentable + } else if (oldRows[oldItemPosition] is CustomizableRowRepresentable && newRows[newItemPosition] is CustomizableRowRepresentable) { + val header1 = oldRows[oldItemPosition] as CustomizableRowRepresentable + val header2 = newRows[newItemPosition] as CustomizableRowRepresentable return header1.title == header2.title } @@ -40,9 +40,9 @@ class HistorySessionDiffCallback(var newRows: List, var oldRow return false //session1.id == session2.id - } else if (oldRows[oldItemPosition] is HeaderRowRepresentable && newRows[newItemPosition] is HeaderRowRepresentable) { - val header1 = oldRows[oldItemPosition] as HeaderRowRepresentable - val header2 = newRows[newItemPosition] as HeaderRowRepresentable + } else if (oldRows[oldItemPosition] is CustomizableRowRepresentable && newRows[newItemPosition] is CustomizableRowRepresentable) { + val header1 = oldRows[oldItemPosition] as CustomizableRowRepresentable + val header2 = newRows[newItemPosition] as CustomizableRowRepresentable return header1.title == header2.title } diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt index c2f01dab..b6b9fcde 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/RowViewType.kt @@ -14,7 +14,7 @@ import kotlinx.android.synthetic.main.row_history_session.view.* import net.pokeranalytics.android.R import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.ui.adapter.RowRepresentableAdapter -import net.pokeranalytics.android.ui.view.rowrepresentable.HeaderRowRepresentable +import net.pokeranalytics.android.ui.view.rowrepresentable.CustomizableRowRepresentable /** * An interface used to factor the configuration of RecyclerView.ViewHolder @@ -35,6 +35,7 @@ enum class RowViewType(private var layoutRes: Int) { HEADER_TITLE_VALUE(R.layout.row_header_title_value), HEADER_TITLE_AMOUNT(R.layout.row_header_title_amount), HEADER_TITLE_AMOUNT_BIG(R.layout.row_header_title_amount_big), + LOCATION_TITLE(R.layout.row_title), // Row TITLE(R.layout.row_title), @@ -46,6 +47,7 @@ enum class RowViewType(private var layoutRes: Int) { DATA(R.layout.row_title), BOTTOM_SHEET_DATA(R.layout.row_bottom_sheet_title), TITLE_CHECK(R.layout.row_title_check), + LOADER(R.layout.row_loader), // Custom row ROW_SESSION(R.layout.row_history_session), @@ -66,10 +68,10 @@ enum class RowViewType(private var layoutRes: Int) { return when (this) { // Header Row View Holder - HEADER_TITLE, HEADER_TITLE_VALUE, HEADER_TITLE_AMOUNT, HEADER_TITLE_AMOUNT_BIG -> HeaderViewHolder(layout) + HEADER_TITLE, HEADER_TITLE_VALUE, HEADER_TITLE_AMOUNT, HEADER_TITLE_AMOUNT_BIG, LOCATION_TITLE -> TitleViewHolder(layout) // Row View Holder - TITLE, TITLE_ARROW, TITLE_VALUE, TITLE_VALUE_ARROW, TITLE_GRID, TITLE_SWITCH, TITLE_CHECK, DATA, BOTTOM_SHEET_DATA -> RowViewHolder( + TITLE, TITLE_ARROW, TITLE_VALUE, TITLE_VALUE_ARROW, TITLE_GRID, TITLE_SWITCH, TITLE_CHECK, DATA, BOTTOM_SHEET_DATA, LOADER -> RowViewHolder( layout ) @@ -96,10 +98,10 @@ enum class RowViewType(private var layoutRes: Int) { /** * Display a header */ - inner class HeaderViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder { + inner class TitleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder { override fun bind(position: Int, row: RowRepresentable, adapter: RowRepresentableAdapter) { - if (row is HeaderRowRepresentable) { + if (row is CustomizableRowRepresentable) { // Title itemView.findViewById(R.id.title)?.let { @@ -116,6 +118,18 @@ enum class RowViewType(private var layoutRes: Int) { it.text = row.value } } + + // Listener + row.isSelectable?.let { isSelectable -> + if (isSelectable) { + val listener = View.OnClickListener { + adapter.delegate?.onRowSelected(position, row) + } + itemView.findViewById(R.id.container)?.let { + it.setOnClickListener(listener) + } + } + } } } } @@ -199,7 +213,7 @@ enum class RowViewType(private var layoutRes: Int) { itemView.findViewById(R.id.title)?.text = row.localizedTitle(itemView.context) // Value - itemView.findViewById(R.id.value)?.let {view -> + itemView.findViewById(R.id.value)?.let { view -> adapter.dataSource.contentDescriptorForRow(row)?.textFormat?.let { view.text = it.text view.setTextColor(it.getColor(itemView.context)) diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/TitleRowRepresentable.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/CustomizableRowRepresentable.kt similarity index 96% rename from app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/TitleRowRepresentable.kt rename to app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/CustomizableRowRepresentable.kt index 78d09c83..50a23ee9 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/TitleRowRepresentable.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/CustomizableRowRepresentable.kt @@ -8,7 +8,7 @@ import net.pokeranalytics.android.ui.view.RowViewType /** * A class to display a title (and a value) as a Row Representable object */ -class TitleRowRepresentable( +class CustomizableRowRepresentable( var customViewType: RowViewType? = RowViewType.HEADER_TITLE, override var resId: Int? = null, var title: String? = null, diff --git a/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/SettingRow.kt b/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/SettingRow.kt index f5e6c690..43502fbe 100644 --- a/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/SettingRow.kt +++ b/app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/SettingRow.kt @@ -40,24 +40,24 @@ enum class SettingRow : RowRepresentable { fun getRows(): ArrayList { val rows = ArrayList() - rows.add(HeaderRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.information)) + rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.information)) rows.addAll(arrayListOf(VERSION, RATE_APP, CONTACT_US, BUG_REPORT)) - rows.add(HeaderRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.follow_us)) + rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.follow_us)) rows.addAll(arrayListOf(FOLLOW_US)) - rows.add(HeaderRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.preferences)) + rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.preferences)) rows.addAll(arrayListOf(CURRENCY)) rows.add( - HeaderRowRepresentable( + CustomizableRowRepresentable( customViewType = RowViewType.HEADER_TITLE, resId = R.string.data_management ) ) rows.addAll(arrayListOf(BANKROLL, GAME, LOCATION, TOURNAMENT_NAME, TOURNAMENT_FEATURE)) - rows.add(HeaderRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.terms)) + rows.add(CustomizableRowRepresentable(customViewType = RowViewType.HEADER_TITLE, resId = R.string.terms)) rows.addAll(arrayListOf(PRIVACY_POLICY, TERMS_OF_USE, GDPR)) return rows