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

feature/top10
Razmig Sarkissian 7 years ago
commit db412ff0a3
  1. 5
      app/src/main/java/net/pokeranalytics/android/PokerAnalyticsApplication.kt
  2. 83
      app/src/main/java/net/pokeranalytics/android/ui/adapter/HistorySessionRowRepresentableAdapter.kt
  3. 80
      app/src/main/java/net/pokeranalytics/android/ui/fragment/HistoryFragment.kt
  4. 7
      app/src/main/java/net/pokeranalytics/android/ui/fragment/SettingsFragment.kt
  5. 4
      app/src/main/java/net/pokeranalytics/android/ui/view/rowrepresentable/SettingRow.kt
  6. 97
      app/src/main/java/net/pokeranalytics/android/util/LocaleUtils.kt
  7. 1
      app/src/main/java/net/pokeranalytics/android/util/Preferences.kt
  8. 2
      app/src/main/res/values/strings.xml

@ -1,6 +1,7 @@
package net.pokeranalytics.android package net.pokeranalytics.android
import android.app.Application import android.app.Application
import android.content.Context
import com.crashlytics.android.Crashlytics import com.crashlytics.android.Crashlytics
import io.fabric.sdk.android.Fabric import io.fabric.sdk.android.Fabric
import io.realm.Realm import io.realm.Realm
@ -69,6 +70,10 @@ class PokerAnalyticsApplication : Application() {
} }
} }
override fun attachBaseContext(base: Context?) {
super.attachBaseContext(base)
}
/** /**
* Create fake sessions if we have less than 10 sessions * Create fake sessions if we have less than 10 sessions
*/ */

@ -1,5 +1,6 @@
package net.pokeranalytics.android.ui.adapter package net.pokeranalytics.android.ui.adapter
import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -11,6 +12,7 @@ import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.ui.view.BindableHolder import net.pokeranalytics.android.ui.view.BindableHolder
import net.pokeranalytics.android.ui.view.RowViewType import net.pokeranalytics.android.ui.view.RowViewType
import net.pokeranalytics.android.util.NULL_TEXT
import net.pokeranalytics.android.util.extensions.getMonthAndYear import net.pokeranalytics.android.util.extensions.getMonthAndYear
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.*
@ -22,15 +24,16 @@ import kotlin.collections.HashMap
* @param dataSource the datasource providing rows * @param dataSource the datasource providing rows
* @param delegate the delegate, notified of UI actions * @param delegate the delegate, notified of UI actions
*/ */
class HistoryRowRepresentableAdapter( class HistorySessionRowRepresentableAdapter(
var realmResults: RealmResults<Session>,
var delegate: RowRepresentableDelegate? = null, var delegate: RowRepresentableDelegate? = null,
var headers: RealmResults<Session> var realmResults: RealmResults<Session>,
var pendingRealmResults: RealmResults<Session>,
var distinctHeaders: RealmResults<Session>
) : ) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() { RecyclerView.Adapter<RecyclerView.ViewHolder>() {
private var headersPositions = HashMap<Int, Date>() private var headersPositions = HashMap<Int, Date?>()
private lateinit var sortedHeaders: SortedMap<Int, Date> private lateinit var sortedHeaders: SortedMap<Int, Date?>
init { init {
refreshData() refreshData()
@ -40,7 +43,7 @@ class HistoryRowRepresentableAdapter(
* Display a session view * Display a session view
*/ */
inner class RowSessionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder { inner class RowSessionViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder {
fun bind(position: Int, row: Session?, adapter: HistoryRowRepresentableAdapter) { fun bind(position: Int, row: Session?, adapter: HistorySessionRowRepresentableAdapter) {
itemView.sessionRow.setData(row as Session) itemView.sessionRow.setData(row as Session)
val listener = View.OnClickListener { val listener = View.OnClickListener {
@ -54,10 +57,10 @@ class HistoryRowRepresentableAdapter(
* Display a session view * Display a session view
*/ */
inner class HeaderTitleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder { inner class HeaderTitleViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), BindableHolder {
fun bind(position: Int, session: Session?, adapter: HistoryRowRepresentableAdapter) { fun bind(position: Int, title: String, adapter: HistorySessionRowRepresentableAdapter) {
// Title // Title
itemView.findViewById<AppCompatTextView>(R.id.title)?.let { itemView.findViewById<AppCompatTextView>(R.id.title)?.let {
it.text = session?.creationDate?.getMonthAndYear() it.text = title
} }
} }
} }
@ -83,27 +86,45 @@ class HistoryRowRepresentableAdapter(
} }
override fun getItemCount(): Int { override fun getItemCount(): Int {
return realmResults.size + headers.size return realmResults.size + pendingRealmResults.size + distinctHeaders.size
} }
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is RowSessionViewHolder) { if (holder is RowSessionViewHolder) {
holder.bind(position, realmResults[getRealIndex(position)], this) holder.bind(position, getSessionForPosition(position), this)
} else if (holder is HeaderTitleViewHolder) { } else if (holder is HeaderTitleViewHolder) {
holder.bind(position, headers[getRealIndex(position)], this) holder.bind(position, getHeaderForPosition(holder.itemView.context, position), this)
} }
} }
/** /**
* Get real index * Return the header
*/ */
private fun getRealIndex(position: Int): Int { private fun getHeaderForPosition(context: Context, position :Int) : String {
if (sortedHeaders.containsKey(position)) { if (sortedHeaders.containsKey(position)) {
// Header position
//Timber.d("getRealIndex: Header: ${sortedHeaders.keys.indexOf(position)}") // If the header has no date, it's a pending session
return sortedHeaders.keys.indexOf(position) return if (sortedHeaders[position] == null) {
context.getString(R.string.pending)
} else { } else {
// Else, return the formatted date
val realmHeaderPosition = if (pendingRealmResults.size > 0) sortedHeaders.keys.indexOf(position) - 1 else sortedHeaders.keys.indexOf(position)
distinctHeaders[realmHeaderPosition]?.startDate?.getMonthAndYear() ?: ""
}
}
return NULL_TEXT
}
/**
* Get real index
*/
private fun getSessionForPosition(position: Int): Session? {
return if (pendingRealmResults.size > 0 && position < pendingRealmResults.size + 1) {
// If we have pending session & the position is between these sessions
pendingRealmResults[position - 1]
} else {
// Else, return the correct session
// Row position // Row position
var headersBefore = 0 var headersBefore = 0
for (key in sortedHeaders.keys) { for (key in sortedHeaders.keys) {
@ -113,8 +134,8 @@ class HistoryRowRepresentableAdapter(
break break
} }
} }
//Timber.d("getRealIndex: Row: $position $headersBefore")
return position - headersBefore realmResults[position - headersBefore - pendingRealmResults.size]
} }
} }
@ -124,17 +145,24 @@ class HistoryRowRepresentableAdapter(
fun refreshData() { fun refreshData() {
headersPositions.clear() headersPositions.clear()
// If we have pending sessions, set the first header to null
if (pendingRealmResults.size > 0) {
headersPositions[0] = null
}
val start = System.currentTimeMillis() val start = System.currentTimeMillis()
var previousYear = 0 var previousYear = Int.MAX_VALUE
var previousMonth = 0 var previousMonth = Int.MAX_VALUE
val calendar = Calendar.getInstance() val calendar = Calendar.getInstance()
// Add headers if the date doesn't exist yet
for ((index, session) in realmResults.withIndex()) { for ((index, session) in realmResults.withIndex()) {
calendar.time = session.creationDate calendar.time = session.creationDate
if (calendar.get(Calendar.YEAR) == previousYear && calendar.get(Calendar.MONTH) < previousMonth || (calendar.get(Calendar.YEAR) < previousYear) || index == 0) { if (checkHeaderCondition(calendar, previousYear, previousMonth)) {
headersPositions[index + headersPositions.size] = session.creationDate headersPositions[index + headersPositions.size + pendingRealmResults.size] = session.creationDate
previousYear = calendar.get(Calendar.YEAR) previousYear = calendar.get(Calendar.YEAR)
previousMonth = calendar.get(Calendar.MONTH) previousMonth = calendar.get(Calendar.MONTH)
} }
@ -145,5 +173,14 @@ class HistoryRowRepresentableAdapter(
Timber.d("Create viewTypesPositions in: ${System.currentTimeMillis() - start}ms") Timber.d("Create viewTypesPositions in: ${System.currentTimeMillis() - start}ms")
} }
/**
* Check if we need to add a header
* Can be change to manage different condition
*/
private fun checkHeaderCondition(currentCalendar: Calendar, previousYear: Int, previousMonth: Int) : Boolean {
return currentCalendar.get(Calendar.YEAR) == previousYear && currentCalendar.get(Calendar.MONTH) < previousMonth || (currentCalendar.get(Calendar.YEAR) < previousYear)
}
} }

@ -12,7 +12,7 @@ import net.pokeranalytics.android.R
import net.pokeranalytics.android.model.interfaces.Manageable import net.pokeranalytics.android.model.interfaces.Manageable
import net.pokeranalytics.android.model.realm.Session import net.pokeranalytics.android.model.realm.Session
import net.pokeranalytics.android.ui.activity.SessionActivity import net.pokeranalytics.android.ui.activity.SessionActivity
import net.pokeranalytics.android.ui.adapter.HistoryRowRepresentableAdapter import net.pokeranalytics.android.ui.adapter.HistorySessionRowRepresentableAdapter
import net.pokeranalytics.android.ui.adapter.LiveRowRepresentableDataSource import net.pokeranalytics.android.ui.adapter.LiveRowRepresentableDataSource
import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate import net.pokeranalytics.android.ui.adapter.RowRepresentableDelegate
import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment
@ -31,9 +31,9 @@ class HistoryFragment : PokerAnalyticsFragment(), LiveRowRepresentableDataSource
} }
} }
private lateinit var historyAdapter: HistoryRowRepresentableAdapter private lateinit var historyAdapter: HistorySessionRowRepresentableAdapter
// Old // Old
//private lateinit var historyAdapter: HistoryRowRepresentableAdapter //private lateinit var historyAdapter: HistorySessionRowRepresentableAdapter
private lateinit var realmSessions: RealmResults<Session> private lateinit var realmSessions: RealmResults<Session>
private val sessions: ArrayList<Session> = ArrayList() private val sessions: ArrayList<Session> = ArrayList()
private val rows: ArrayList<RowRepresentable> = ArrayList() private val rows: ArrayList<RowRepresentable> = ArrayList()
@ -81,22 +81,22 @@ class HistoryFragment : PokerAnalyticsFragment(), LiveRowRepresentableDataSource
* Init data * Init data
*/ */
private fun initData() { private fun initData() {
realmSessions = getRealm().where<Session>().findAll().sort("creationDate", Sort.DESCENDING)
val viewManager = SmoothScrollLinearLayoutManager(requireContext())
realmSessions = getRealm().where<Session>().findAll().sort("startDate", Sort.DESCENDING)
realmSessions.addChangeListener { t, changeSet -> realmSessions.addChangeListener { t, changeSet ->
if (changeSet.insertions.isNotEmpty() || changeSet.deletions.isNotEmpty()) { if (changeSet.insertions.isNotEmpty() || changeSet.deletions.isNotEmpty()) {
historyAdapter.refreshData() historyAdapter.refreshData()
} }
historyAdapter.notifyDataSetChanged() historyAdapter.notifyDataSetChanged()
} }
val months = getRealm().where<Session>().distinct("year", "month").findAll().sort("creationDate", Sort.DESCENDING)
historyAdapter = HistoryRowRepresentableAdapter(realmSessions, this, months)
// Old val startedSessions = getRealm().where<Session>().isNotNull("year").isNotNull("month").findAll().sort("startDate", Sort.DESCENDING)
//historyAdapter = RowRepresentableAdapter(this, this) val pendingSessions = getRealm().where<Session>().isNull("year").isNull("month").findAll().sort("startDate", Sort.DESCENDING)
val distinctDateSessions = getRealm().where<Session>().distinct("year", "month").findAll().sort("startDate", Sort.DESCENDING)
historyAdapter = HistorySessionRowRepresentableAdapter(this, startedSessions, pendingSessions, distinctDateSessions)
val viewManager = SmoothScrollLinearLayoutManager(requireContext())
recyclerView.apply { recyclerView.apply {
setHasFixedSize(true) setHasFixedSize(true)
layoutManager = viewManager layoutManager = viewManager
@ -104,66 +104,6 @@ class HistoryFragment : PokerAnalyticsFragment(), LiveRowRepresentableDataSource
} }
} }
/**
* Create the endedSessions headers
* TODO: Remove
*/
private fun createSessionsHeaders() {
/*
val start = System.currentTimeMillis()
val oldRows = ArrayList<RowRepresentable>()
oldRows.addAll(rows)
rows.clear()
sessions.clear()
sessions.addAll(getRealm().copyFromRealm(realmSessions))
noSessionFound.isVisible = sessions.isEmpty()
GlobalScope.launch {
val groupedByDay = false
val calendar = Calendar.getInstance()
val currentCalendar = Calendar.getInstance()
for ((index, session) in sessions.withIndex()) {
currentCalendar.time = session.creationDate
if (groupedByDay) {
if (!calendar.isSameDay(currentCalendar) || index == 0) {
calendar.time = currentCalendar.time
val header = CustomizableRowRepresentable(
customViewType = RowViewType.HEADER_TITLE,
title = session.creationDate.longDate()
)
rows.add(header)
}
} else {
if (!calendar.isSameMonth(currentCalendar) || index == 0) {
calendar.time = currentCalendar.time
val header = CustomizableRowRepresentable(
customViewType = RowViewType.HEADER_TITLE,
title = session.creationDate.getMonthAndYear()
)
rows.add(header)
}
}
rows.add(session)
}
val diffResult = DiffUtil.calculateDiff(HistorySessionDiffCallback(rows, oldRows))
historyAdapter.updateRows(diffResult)
if (newSessionCreated) {
newSessionCreated = false
recyclerView.smoothScrollToPosition(0)
}
Timber.d("createSessionsHeaders in: ${System.currentTimeMillis() - start}ms")
}
*/
}
override fun rowRepresentableForPosition(position: Int): RowRepresentable? { override fun rowRepresentableForPosition(position: Int): RowRepresentable? {
return this.rows[position] return this.rows[position]
} }

@ -20,6 +20,7 @@ import net.pokeranalytics.android.ui.adapter.StaticRowRepresentableDataSource
import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment import net.pokeranalytics.android.ui.fragment.components.PokerAnalyticsFragment
import net.pokeranalytics.android.ui.view.RowRepresentable import net.pokeranalytics.android.ui.view.RowRepresentable
import net.pokeranalytics.android.ui.view.rowrepresentable.SettingRow import net.pokeranalytics.android.ui.view.rowrepresentable.SettingRow
import net.pokeranalytics.android.util.LocaleUtils
import net.pokeranalytics.android.util.Preferences import net.pokeranalytics.android.util.Preferences
import net.pokeranalytics.android.util.URL import net.pokeranalytics.android.util.URL
import net.pokeranalytics.android.util.extensions.openContactMail import net.pokeranalytics.android.util.extensions.openContactMail
@ -56,8 +57,8 @@ class SettingsFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, Sta
override fun stringForRow(row: RowRepresentable): String { override fun stringForRow(row: RowRepresentable): String {
return when (row) { return when (row) {
SettingRow.VERSION -> BuildConfig.VERSION_NAME + if (BuildConfig.DEBUG) " DEBUG" else "" SettingRow.VERSION -> BuildConfig.VERSION_NAME + if (BuildConfig.DEBUG) " DEBUG" else ""
SettingRow.LANGUAGE -> LocaleUtils.getCurrentLocale(requireContext()).displayLanguage
SettingRow.CURRENCY -> Currency.getInstance(Preferences.getCurrencyLocale(this.parentActivity)).symbol SettingRow.CURRENCY -> Currency.getInstance(Preferences.getCurrencyLocale(this.parentActivity)).symbol
else -> "" else -> ""
} }
} }
@ -93,6 +94,10 @@ class SettingsFragment : PokerAnalyticsFragment(), RowRepresentableDelegate, Sta
SettingRow.RATE_APP -> parentActivity.openPlayStorePage() SettingRow.RATE_APP -> parentActivity.openPlayStorePage()
SettingRow.CONTACT_US -> parentActivity.openContactMail(R.string.contact) SettingRow.CONTACT_US -> parentActivity.openContactMail(R.string.contact)
SettingRow.BUG_REPORT -> parentActivity.openContactMail(R.string.bug_report_subject) SettingRow.BUG_REPORT -> parentActivity.openContactMail(R.string.bug_report_subject)
SettingRow.LANGUAGE -> {
LocaleUtils.setLocale(requireContext(), "fr")
activity?.recreate()
}
SettingRow.CURRENCY -> CurrenciesActivity.newInstanceForResult(this@SettingsFragment, SettingsFragment.REQUEST_CODE_CURRENCY) SettingRow.CURRENCY -> CurrenciesActivity.newInstanceForResult(this@SettingsFragment, SettingsFragment.REQUEST_CODE_CURRENCY)
SettingRow.FOLLOW_US -> { SettingRow.FOLLOW_US -> {
when (position) { when (position) {

@ -18,6 +18,7 @@ enum class SettingRow : RowRepresentable {
FOLLOW_US, FOLLOW_US,
// Preferences // Preferences
LANGUAGE,
CURRENCY, CURRENCY,
// Data management // Data management
@ -80,6 +81,7 @@ enum class SettingRow : RowRepresentable {
PRIVACY_POLICY -> R.string.privacy_policy PRIVACY_POLICY -> R.string.privacy_policy
TERMS_OF_USE -> R.string.terms_of_use TERMS_OF_USE -> R.string.terms_of_use
FOLLOW_US -> R.string.follow_us FOLLOW_US -> R.string.follow_us
LANGUAGE -> R.string.language
CURRENCY -> R.string.currency CURRENCY -> R.string.currency
GDPR -> R.string.gdpr GDPR -> R.string.gdpr
else -> null else -> null
@ -92,7 +94,7 @@ enum class SettingRow : RowRepresentable {
get() { get() {
return when (this) { return when (this) {
VERSION -> RowViewType.TITLE_VALUE.ordinal VERSION -> RowViewType.TITLE_VALUE.ordinal
CURRENCY -> RowViewType.TITLE_VALUE_ARROW.ordinal LANGUAGE, CURRENCY -> RowViewType.TITLE_VALUE_ARROW.ordinal
FOLLOW_US -> RowViewType.ROW_FOLLOW_US.ordinal FOLLOW_US -> RowViewType.ROW_FOLLOW_US.ordinal
else -> RowViewType.TITLE_ARROW.ordinal else -> RowViewType.TITLE_ARROW.ordinal
} }

@ -0,0 +1,97 @@
package net.pokeranalytics.android.util
import android.annotation.TargetApi
import android.content.Context
import android.os.Build
import android.preference.PreferenceManager
import java.util.*
class LocaleUtils {
companion object {
/**
* Return the current locale
*/
fun getCurrentLocale(context: Context) : Locale {
val defaultLocaleCode = Preferences.getString(Preferences.Keys.LOCALE_CODE, context)
var locale = Locale.getDefault()
if (defaultLocaleCode != null) {
locale = Locale(defaultLocaleCode)
Locale.setDefault(locale)
}
return locale
}
private val SELECTED_LANGUAGE = "Locale.Helper.Selected.Language"
fun onAttach(context: Context): Context {
val lang = getPersistedData(context, Locale.getDefault().language)
return setLocale(context, lang)
}
fun onAttach(context: Context, defaultLanguage: String): Context {
val lang = getPersistedData(context, defaultLanguage)
return setLocale(context, lang)
}
fun getLanguage(context: Context): String? {
return getPersistedData(context, Locale.getDefault().language)
}
fun setLocale(context: Context, language: String?): Context {
persist(context, language)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
updateResources(context, language)
} else updateResourcesLegacy(context, language)
}
private fun getPersistedData(context: Context, defaultLanguage: String): String? {
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
return preferences.getString(SELECTED_LANGUAGE, defaultLanguage)
}
private fun persist(context: Context, language: String?) {
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
val editor = preferences.edit()
editor.putString(SELECTED_LANGUAGE, language)
editor.apply()
}
@TargetApi(Build.VERSION_CODES.N)
private fun updateResources(context: Context, language: String?): Context {
val locale = Locale(language)
Locale.setDefault(locale)
val configuration = context.resources.configuration
configuration.setLocale(locale)
configuration.setLayoutDirection(locale)
return context.createConfigurationContext(configuration)
}
private fun updateResourcesLegacy(context: Context, language: String?): Context {
val locale = Locale(language)
Locale.setDefault(locale)
val resources = context.resources
val configuration = resources.configuration
configuration.locale = locale
configuration.setLayoutDirection(locale)
resources.updateConfiguration(configuration, resources.displayMetrics)
return context
}
}
}

@ -8,6 +8,7 @@ class Preferences {
enum class Keys(var identifier: String) { enum class Keys(var identifier: String) {
CURRENCY_CODE("CurrencyCode"), CURRENCY_CODE("CurrencyCode"),
LOCALE_CODE("LocaleCode"),
FIRST_LAUNCH("firstLaunch") FIRST_LAUNCH("firstLaunch")
} }

@ -31,6 +31,8 @@
<string name="tournament_feature">Tournament Feature</string> <string name="tournament_feature">Tournament Feature</string>
<string name="new_entity">New</string> <string name="new_entity">New</string>
<string name="pending">Pending</string>
<!-- Translated --> <!-- Translated -->
<string name="_ago">%s ago</string> <string name="_ago">%s ago</string>

Loading…
Cancel
Save