Compute sum and ratio in stats

This commit is contained in:
fgerber 2024-04-09 16:51:00 +02:00
parent 6aef6dabb2
commit fd0ae64c0d
7 changed files with 103 additions and 50 deletions

View File

@ -4,6 +4,7 @@ import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.adapter.FragmentStateAdapter
@ -12,14 +13,17 @@ import com.google.android.material.tabs.TabLayoutMediator
import net.helcel.beans.R import net.helcel.beans.R
import net.helcel.beans.activity.adapter.StatsListAdapter import net.helcel.beans.activity.adapter.StatsListAdapter
import net.helcel.beans.databinding.ActivityStatBinding import net.helcel.beans.databinding.ActivityStatBinding
import net.helcel.beans.helper.Settings
import net.helcel.beans.helper.Theme.createActionBar import net.helcel.beans.helper.Theme.createActionBar
private val MODE_LIST = listOf("World", "Country", "Region") const val WORLD = "Continents"
const val COUNTRY = "Countries"
const val REGION = "Regions"
private val MODE_LIST = listOf(WORLD, COUNTRY, REGION)
class StatActivity : AppCompatActivity() { class StatActivity : AppCompatActivity() {
private lateinit var _binding: ActivityStatBinding private lateinit var _binding: ActivityStatBinding
private var activeMode: String = "World" private var activeMode: String = WORLD
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -29,11 +33,11 @@ class StatActivity : AppCompatActivity() {
_binding.stats.layoutManager = _binding.stats.layoutManager =
LinearLayoutManager(this, RecyclerView.VERTICAL, false) LinearLayoutManager(this, RecyclerView.VERTICAL, false)
val adapter = StatsListAdapter(_binding.stats) val adapter = StatsListAdapter(_binding.stats, _binding.name)
_binding.stats.adapter = adapter _binding.stats.adapter = adapter
_binding.pager.adapter = object : FragmentStateAdapter(supportFragmentManager, lifecycle) { _binding.pager.adapter = object : FragmentStateAdapter(supportFragmentManager, lifecycle) {
override fun getItemCount(): Int = 3 override fun getItemCount(): Int = if (Settings.isRegional(applicationContext)) 3 else 2
override fun createFragment(position: Int): Fragment = Fragment() override fun createFragment(position: Int): Fragment = Fragment()
} }
TabLayoutMediator(_binding.tab, _binding.pager) { tab, position -> TabLayoutMediator(_binding.tab, _binding.pager) { tab, position ->
@ -46,7 +50,7 @@ class StatActivity : AppCompatActivity() {
adapter.refreshMode(activeMode) adapter.refreshMode(activeMode)
} }
}) })
adapter.refreshMode(activeMode) //adapter.refreshMode(activeMode)
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {

View File

@ -153,14 +153,7 @@ class GeolocListAdapter(
val numerator = val numerator =
geoLoc.children.map { Data.visits.getVisited(it) != NO_GROUP }.count { it } geoLoc.children.map { Data.visits.getVisited(it) != NO_GROUP }.count { it }
val denominator = geoLoc.children.size val denominator = geoLoc.children.size
_binding.count.text = when (Settings.getStatPref(ctx)) { _binding.count.text = Settings.getStats(ctx, numerator, denominator)
ctx.getString(R.string.percentages) -> ctx.getString(
R.string.percentage,
(100 * (numerator.toFloat() / denominator.toFloat())).toInt()
)
else -> ctx.getString(R.string.rate, numerator, denominator)
}
} }
private fun refresh(geoLoc: GeoLoc) { private fun refresh(geoLoc: GeoLoc) {

View File

@ -1,40 +1,80 @@
package net.helcel.beans.activity.adapter package net.helcel.beans.activity.adapter
import android.annotation.SuppressLint
import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.textview.MaterialTextView
import net.helcel.beans.R
import net.helcel.beans.activity.COUNTRY
import net.helcel.beans.activity.REGION
import net.helcel.beans.activity.WORLD
import net.helcel.beans.countries.GeoLoc import net.helcel.beans.countries.GeoLoc
import net.helcel.beans.countries.World import net.helcel.beans.countries.World
import net.helcel.beans.databinding.ItemListGroupBinding import net.helcel.beans.databinding.ItemListGroupBinding
import net.helcel.beans.helper.AUTO_GROUP
import net.helcel.beans.helper.Data import net.helcel.beans.helper.Data
import net.helcel.beans.helper.Groups import net.helcel.beans.helper.Groups
import net.helcel.beans.helper.Settings
import net.helcel.beans.helper.Theme.getContrastColor import net.helcel.beans.helper.Theme.getContrastColor
class StatsListAdapter(private val stats: RecyclerView) : class StatsListAdapter(private val stats: RecyclerView, private val total: MaterialTextView) :
RecyclerView.Adapter<StatsListAdapter.StatsViewHolder>() { RecyclerView.Adapter<StatsListAdapter.StatsViewHolder>() {
private var locMode: String = WORLD
private lateinit var ctx: Context
private var countMode: Boolean = true
private var initialSum: Int = 0
private val wwwTotal: List<GeoLoc> = World.WWW.children.toList()
private val countryTotal: List<GeoLoc> = World.WWW.children.flatMap { it.children }
private val stateTotal: List<GeoLoc> = World.WWW.children.flatMap{ it.children.flatMap { itt -> itt.children } }
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StatsViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StatsViewHolder {
ctx = parent.context
val binding = val binding =
ItemListGroupBinding.inflate(LayoutInflater.from(parent.context), parent, false) ItemListGroupBinding.inflate(LayoutInflater.from(ctx), parent, false)
return StatsViewHolder(binding) return StatsViewHolder(binding)
} }
override fun onBindViewHolder(holder: StatsViewHolder, pos: Int) { override fun onBindViewHolder(holder: StatsViewHolder, pos: Int) {
initialSum += if (pos == itemCount - 1) {
holder.bind(Pair(AUTO_GROUP, Groups.Group(AUTO_GROUP, ctx.getString(R.string.uncategorized))))
} else {
holder.bind(Data.groups.getGroupFromPos(pos)) holder.bind(Data.groups.getGroupFromPos(pos))
} }
total.text = Settings.getStats(ctx, initialSum, getTotal())
}
override fun getItemCount(): Int { override fun getItemCount(): Int {
return Data.groups.size() return Data.groups.size() + 1
}
private fun getTotal(): Int {
return if (countMode) {
when (locMode) {
WORLD -> wwwTotal.size
COUNTRY -> countryTotal.size
REGION -> stateTotal.size
else -> 0
}
} else {
when (locMode) {
WORLD -> wwwTotal.sumOf { it.area }
COUNTRY -> countryTotal.sumOf { it.area }
REGION -> stateTotal.sumOf { it.area }
else -> 0
}
}
} }
fun refreshMode(mode: String) { fun refreshMode(mode: String) {
for (i in 0 until itemCount) { val sum = (0 until itemCount).map {
val viewHolder = stats.findViewHolderForAdapterPosition(i) as? StatsViewHolder val viewHolder = stats.findViewHolderForAdapterPosition(it) as? StatsViewHolder
viewHolder?.refresh(mode) viewHolder?.refresh(mode)
} }.reduce { acc, i -> acc?.plus((i ?: 0)) }
total.text = Settings.getStats(ctx, sum, getTotal())
} }
inner class StatsViewHolder( inner class StatsViewHolder(
@ -42,13 +82,12 @@ class StatsListAdapter(private val stats: RecyclerView) :
) : RecyclerView.ViewHolder(_binding.root) { ) : RecyclerView.ViewHolder(_binding.root) {
private lateinit var data: Pair<Int, Groups.Group> private lateinit var data: Pair<Int, Groups.Group>
private var countMode: Boolean = true
private var locMode: String = "World"
private lateinit var wwwCount: List<GeoLoc> private lateinit var wwwCount: List<GeoLoc>
private lateinit var countryCount: List<GeoLoc> private lateinit var countryCount: List<GeoLoc>
private lateinit var stateCount: List<GeoLoc> private lateinit var stateCount: List<GeoLoc>
fun bind(entry: Pair<Int, Groups.Group>) {
fun bind(entry: Pair<Int, Groups.Group>): Int {
data = entry data = entry
_binding.groupColor.text = entry.second.name _binding.groupColor.text = entry.second.name
@ -60,10 +99,10 @@ class StatsListAdapter(private val stats: RecyclerView) :
_binding.groupColor.setOnClickListener { _binding.groupColor.setOnClickListener {
countMode = !countMode countMode = !countMode
refresh(locMode) refreshMode(locMode)
} }
compute() compute()
refresh(locMode) return refresh(locMode)
} }
private fun compute() { private fun compute() {
@ -77,24 +116,27 @@ class StatsListAdapter(private val stats: RecyclerView) :
.flatten().flatten() .flatten().flatten()
} }
fun refresh(mode: String) { fun refresh(mode: String): Int {
locMode = mode locMode = mode
if (countMode) { return if (countMode) {
_binding.name.text = when (locMode) { val count = when (locMode) {
"World" -> wwwCount.size WORLD -> wwwCount.size
"Country" -> countryCount.size COUNTRY -> countryCount.size
"Region" -> stateCount.size REGION -> stateCount.size
else -> "?" else -> -1
}.toString() }
} else { _binding.name.text = count.toString()
_binding.name.text = when (locMode) { count
"World" -> wwwCount.sumOf { it.area } } else {
"Country" -> countryCount.sumOf { it.area } val area = when (locMode) {
"Region" -> stateCount.sumOf { it.area } WORLD -> wwwCount.sumOf { it.area }
else -> "?" COUNTRY -> countryCount.sumOf { it.area }
}.toString() REGION -> stateCount.sumOf { it.area }
else -> -1
}
_binding.name.text = area.toString()
area
} }
} }
} }

View File

@ -2,11 +2,14 @@ package net.helcel.beans.helper
import android.graphics.Color import android.graphics.Color
import android.graphics.drawable.ColorDrawable import android.graphics.drawable.ColorDrawable
import androidx.core.content.ContextCompat
import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.Serializer import kotlinx.serialization.Serializer
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import net.helcel.beans.R
import java.io.InputStream import java.io.InputStream
import kotlin.coroutines.coroutineContext
import kotlin.random.Random import kotlin.random.Random
@ -65,10 +68,6 @@ class Groups(val id: Int, private val grps: HashMap<Int, Group>) {
return grps.keys.toList().indexOf(key) return grps.keys.toList().indexOf(key)
} }
fun forEach(action: (Map.Entry<Int, Group>) -> Unit) {
grps.forEach { action(it) }
}
class EmptyGroup : Group(0, "") class EmptyGroup : Group(0, "")
@Serializable @Serializable
@ -76,7 +75,7 @@ class Groups(val id: Int, private val grps: HashMap<Int, Group>) {
val key: Int, val key: Int,
val name: String, val name: String,
@Serializable(with = Theme.ColorDrawableSerializer::class) val color: ColorDrawable = ColorDrawable( @Serializable(with = Theme.ColorDrawableSerializer::class) val color: ColorDrawable = ColorDrawable(
Color.TRANSPARENT Color.GRAY
) )
) )

View File

@ -44,4 +44,18 @@ object Settings {
else -> false else -> false
} }
} }
fun getStats(ctx: Context, numerator: Int?, denominator: Int?): String {
if (numerator == null || denominator == null || denominator == 0) {
return ""
}
return when (getStatPref(ctx)) {
ctx.getString(R.string.percentages) -> ctx.getString(
R.string.percentage,
(100 * (numerator.toFloat() / denominator.toFloat())).toInt()
)
else -> ctx.getString(R.string.rate, numerator, denominator)
}
}
} }

View File

@ -49,7 +49,7 @@
android:gravity="start|center_vertical" android:gravity="start|center_vertical"
android:paddingStart="20dp" android:paddingStart="20dp"
android:paddingEnd="52dp" android:paddingEnd="52dp"
android:text="TODO" android:text=""
android:textColor="?attr/colorOnPrimary" android:textColor="?attr/colorOnPrimary"
app:layout_constraintBottom_toBottomOf="@id/group_color" app:layout_constraintBottom_toBottomOf="@id/group_color"
app:layout_constraintEnd_toEndOf="@id/group_color" app:layout_constraintEnd_toEndOf="@id/group_color"

View File

@ -36,4 +36,5 @@
<string name="cancel">Cancel</string> <string name="cancel">Cancel</string>
<string name="ok">Ok</string> <string name="ok">Ok</string>
<string name="total">Total</string> <string name="total">Total</string>
<string name="uncategorized">Uncategorized</string>
</resources> </resources>