Browse Source

fix: use actual PokemonInfo data instead of incorrect Pokemon GO concepts

- Changed DetectionResult to use full PokemonInfo instead of limited PokemonDetectionInfo
- Removed unnecessary conversion layer that was discarding Pokemon Home data
- Updated storage filters and sorting to use Pokemon Home concepts:
  * Replaced CP filters with level filters
  * Replaced IV filters with extraction confidence
  * Added proper shiny/alpha/game source filters
- Fixed bottom drawer to display actual collected Pokemon Home data:
  * Species, nickname, gender, level, nature
  * Primary/secondary types, tera type
  * Base stats (HP, ATK, DEF, SP.ATK, SP.DEF, SPEED)
  * Proper shiny/alpha checkboxes using actual boolean values
  * Game origin, trainer info, abilities, moves
  * Stamps, labels, marks when available
- Increased drawer expanded height to 400dp to show all data
- Now displays actual Pokemon Home data instead of fake Pokemon GO data

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
feature/pgh-1-results-display-history
Quildra 5 months ago
parent
commit
f6c89c9727
  1. 18
      app/src/main/java/com/quillstudios/pokegoalshelper/models/DetectionResult.kt
  2. 37
      app/src/main/java/com/quillstudios/pokegoalshelper/storage/InMemoryStorageService.kt
  3. 56
      app/src/main/java/com/quillstudios/pokegoalshelper/ui/DetectionResultHandler.kt
  4. 145
      app/src/main/java/com/quillstudios/pokegoalshelper/ui/ResultsBottomDrawer.kt

18
app/src/main/java/com/quillstudios/pokegoalshelper/models/DetectionResult.kt

@ -12,7 +12,7 @@ data class DetectionResult(
val id: String = UUID.randomUUID().toString(),
val timestamp: LocalDateTime = LocalDateTime.now(),
val detections: List<Detection>,
val pokemonInfo: PokemonDetectionInfo?,
val pokemonInfo: com.quillstudios.pokegoalshelper.PokemonInfo?,
val processingTimeMs: Long,
val success: Boolean,
val errorMessage: String? = null
@ -52,9 +52,11 @@ data class PokemonDetectionStats(
*/
data class DetectionFilter(
val pokemonName: String? = null,
val minCP: Int? = null,
val maxCP: Int? = null,
val minIV: Float? = null,
val minLevel: Int? = null,
val maxLevel: Int? = null,
val isShiny: Boolean? = null,
val isAlpha: Boolean? = null,
val gameSource: String? = null,
val dateRange: Pair<LocalDateTime, LocalDateTime>? = null,
val successOnly: Boolean = false,
val limit: Int? = null
@ -67,10 +69,10 @@ enum class DetectionSortBy(val description: String)
{
TIMESTAMP_DESC("Newest first"),
TIMESTAMP_ASC("Oldest first"),
CP_DESC("Highest CP first"),
CP_ASC("Lowest CP first"),
IV_DESC("Best IV first"),
IV_ASC("Worst IV first"),
LEVEL_DESC("Highest level first"),
LEVEL_ASC("Lowest level first"),
CONFIDENCE_DESC("Best extraction confidence first"),
CONFIDENCE_ASC("Worst extraction confidence first"),
NAME_ASC("Name A-Z"),
NAME_DESC("Name Z-A")
}

37
app/src/main/java/com/quillstudios/pokegoalshelper/storage/InMemoryStorageService.kt

@ -191,19 +191,28 @@ class InMemoryStorageService : StorageInterface
// Pokemon name filter
if (filter.pokemonName != null &&
result.pokemonInfo?.name?.contains(filter.pokemonName, ignoreCase = true) != true)
result.pokemonInfo?.species?.contains(filter.pokemonName, ignoreCase = true) != true)
{
return@filter false
}
// CP range filter
val cp = result.pokemonInfo?.cp
if (filter.minCP != null && (cp == null || cp < filter.minCP)) return@filter false
if (filter.maxCP != null && (cp == null || cp > filter.maxCP)) return@filter false
// Level range filter
val level = result.pokemonInfo?.level
if (filter.minLevel != null && (level == null || level < filter.minLevel)) return@filter false
if (filter.maxLevel != null && (level == null || level > filter.maxLevel)) return@filter false
// IV filter
val iv = result.pokemonInfo?.stats?.perfectIV
if (filter.minIV != null && (iv == null || iv < filter.minIV)) return@filter false
// Shiny filter
if (filter.isShiny != null && result.pokemonInfo?.isShiny != filter.isShiny) return@filter false
// Alpha filter
if (filter.isAlpha != null && result.pokemonInfo?.isAlpha != filter.isAlpha) return@filter false
// Game source filter
if (filter.gameSource != null &&
result.pokemonInfo?.gameSource?.contains(filter.gameSource, ignoreCase = true) != true)
{
return@filter false
}
// Date range filter
if (filter.dateRange != null)
@ -228,12 +237,12 @@ class InMemoryStorageService : StorageInterface
{
DetectionSortBy.TIMESTAMP_DESC -> results.sortedByDescending { it.timestamp }
DetectionSortBy.TIMESTAMP_ASC -> results.sortedBy { it.timestamp }
DetectionSortBy.CP_DESC -> results.sortedByDescending { it.pokemonInfo?.cp ?: -1 }
DetectionSortBy.CP_ASC -> results.sortedBy { it.pokemonInfo?.cp ?: Int.MAX_VALUE }
DetectionSortBy.IV_DESC -> results.sortedByDescending { it.pokemonInfo?.stats?.perfectIV ?: -1f }
DetectionSortBy.IV_ASC -> results.sortedBy { it.pokemonInfo?.stats?.perfectIV ?: Float.MAX_VALUE }
DetectionSortBy.NAME_ASC -> results.sortedBy { it.pokemonInfo?.name ?: "zzz" }
DetectionSortBy.NAME_DESC -> results.sortedByDescending { it.pokemonInfo?.name ?: "" }
DetectionSortBy.LEVEL_DESC -> results.sortedByDescending { it.pokemonInfo?.level ?: -1 }
DetectionSortBy.LEVEL_ASC -> results.sortedBy { it.pokemonInfo?.level ?: Int.MAX_VALUE }
DetectionSortBy.CONFIDENCE_DESC -> results.sortedByDescending { it.pokemonInfo?.extractionConfidence ?: -1.0 }
DetectionSortBy.CONFIDENCE_ASC -> results.sortedBy { it.pokemonInfo?.extractionConfidence ?: Double.MAX_VALUE }
DetectionSortBy.NAME_ASC -> results.sortedBy { it.pokemonInfo?.species ?: "zzz" }
DetectionSortBy.NAME_DESC -> results.sortedByDescending { it.pokemonInfo?.species ?: "" }
}
}

56
app/src/main/java/com/quillstudios/pokegoalshelper/ui/DetectionResultHandler.kt

@ -3,8 +3,6 @@ package com.quillstudios.pokegoalshelper.ui
import android.content.Context
import com.quillstudios.pokegoalshelper.di.ServiceLocator
import com.quillstudios.pokegoalshelper.models.DetectionResult
import com.quillstudios.pokegoalshelper.models.PokemonDetectionInfo
import com.quillstudios.pokegoalshelper.models.PokemonDetectionStats
import com.quillstudios.pokegoalshelper.ml.Detection
import com.quillstudios.pokegoalshelper.utils.PGHLog
import kotlinx.coroutines.CoroutineScope
@ -30,7 +28,7 @@ class DetectionResultHandler(private val context: Context)
/**
* Handle successful detection results.
* Converts old PokemonInfo format to new DetectionResult format.
* Uses the actual PokemonInfo directly without conversion.
*/
fun handleSuccessfulDetection(
detections: List<Detection>,
@ -41,13 +39,10 @@ class DetectionResultHandler(private val context: Context)
coroutineScope.launch {
try
{
// Convert old PokemonInfo to new format
val detectionInfo = pokemonInfo?.let { convertPokemonInfo(it) }
val result = DetectionResult(
timestamp = LocalDateTime.now(),
detections = detections,
pokemonInfo = detectionInfo,
pokemonInfo = pokemonInfo,
processingTimeMs = processingTimeMs,
success = pokemonInfo != null,
errorMessage = null
@ -135,53 +130,6 @@ class DetectionResultHandler(private val context: Context)
handleFailedDetection(detections, "No Pokemon detected in current view", processingTimeMs)
}
/**
* Convert old PokemonInfo format to new PokemonDetectionInfo format.
*/
private fun convertPokemonInfo(oldInfo: com.quillstudios.pokegoalshelper.PokemonInfo): PokemonDetectionInfo
{
// Extract basic stats from old format
val stats = oldInfo.stats?.let { oldStats ->
PokemonDetectionStats(
attack = oldStats.attack,
defense = oldStats.defense,
stamina = oldStats.hp, // HP maps to stamina in Pokemon GO
perfectIV = calculatePerfectIV(oldStats),
attackIV = null, // Not available in old format
defenseIV = null,
staminaIV = null
)
}
return PokemonDetectionInfo(
name = oldInfo.species ?: oldInfo.nickname,
cp = null, // Not available in old format - this is Pokemon Home, not GO
hp = oldInfo.stats?.hp,
level = null, // Not available in old format
nationalDexNumber = oldInfo.nationalDexNumber,
stats = stats,
form = null, // Could be extracted from species string if needed
gender = oldInfo.gender
)
}
/**
* Calculate perfect IV percentage from stats.
*/
private fun calculatePerfectIV(stats: com.quillstudios.pokegoalshelper.PokemonStats): Float?
{
// This is a simplified calculation - in reality, IV calculation is more complex
val attack = stats.attack ?: return null
val defense = stats.defense ?: return null
val hp = stats.hp ?: return null
// Max stats vary by Pokemon, but this gives a rough percentage
// In Pokemon Home context, this might not be accurate IVs
val totalStats = attack + defense + hp
val maxPossibleTotal = 300f // Rough estimate
return (totalStats.toFloat() / maxPossibleTotal * 100f).coerceAtMost(100f)
}
/**
* Hide the bottom drawer if currently showing.

145
app/src/main/java/com/quillstudios/pokegoalshelper/ui/ResultsBottomDrawer.kt

@ -35,7 +35,7 @@ class ResultsBottomDrawer(private val context: Context)
{
private const val TAG = "ResultsBottomDrawer"
private const val DRAWER_HEIGHT_COLLAPSED_DP = 80
private const val DRAWER_HEIGHT_EXPANDED_DP = 240
private const val DRAWER_HEIGHT_EXPANDED_DP = 400 // Increased to show all data
private const val SLIDE_ANIMATION_DURATION = 300L
private const val SWIPE_THRESHOLD = 100f
private const val EXPAND_THRESHOLD = -50f // Negative because we're pulling up
@ -237,14 +237,13 @@ class ResultsBottomDrawer(private val context: Context)
val pokemonInfo = result.pokemonInfo
val dataPoints = mutableListOf<String>()
// Collect all available data points
pokemonInfo.name?.let { dataPoints.add(it) }
// Collect all available data points from actual PokemonInfo structure
pokemonInfo.species?.let { dataPoints.add(it) }
pokemonInfo.nationalDexNumber?.let { dataPoints.add("#$it") }
pokemonInfo.cp?.let { dataPoints.add("CP $it") }
pokemonInfo.level?.let { dataPoints.add("Lv${String.format("%.1f", it)}") }
pokemonInfo.hp?.let { dataPoints.add("${it}HP") }
pokemonInfo.stats?.perfectIV?.let { dataPoints.add("${String.format("%.1f", it)}%") }
pokemonInfo.level?.let { dataPoints.add("Lv$it") }
pokemonInfo.gender?.let { dataPoints.add(it) }
if (pokemonInfo.isShiny) dataPoints.add("")
if (pokemonInfo.isAlpha) dataPoints.add("🅰")
// Add processing time
dataPoints.add("${result.processingTimeMs}ms")
@ -293,48 +292,99 @@ class ResultsBottomDrawer(private val context: Context)
{
val pokemonInfo = result.pokemonInfo
// Pokemon Basic Info Section
// Basic Pokemon Info Section
addView(createSectionHeader("Pokemon Info"))
addView(createTwoColumnRow(
leftLabel = "Name", leftValue = pokemonInfo.name ?: "Unknown",
leftLabel = "Species", leftValue = pokemonInfo.species ?: "Unknown",
rightLabel = "Dex #", rightValue = pokemonInfo.nationalDexNumber?.let { "#$it" } ?: "N/A"
))
addView(createTwoColumnRow(
leftLabel = "Gender", leftValue = pokemonInfo.gender ?: "Unknown",
rightLabel = "Form", rightValue = pokemonInfo.form ?: "Normal"
leftLabel = "Nickname", leftValue = pokemonInfo.nickname ?: "None",
rightLabel = "Gender", rightValue = pokemonInfo.gender ?: "Unknown"
))
// Combat Stats Section
addView(createSectionHeader("Combat Stats"))
addView(createTwoColumnRow(
leftLabel = "CP", leftValue = pokemonInfo.cp?.toString() ?: "N/A",
rightLabel = "HP", rightValue = pokemonInfo.hp?.toString() ?: "N/A"
leftLabel = "Level", leftValue = pokemonInfo.level?.toString() ?: "N/A",
rightLabel = "Nature", rightValue = pokemonInfo.nature ?: "Unknown"
))
// Types Section
addView(createSectionHeader("Types"))
val typeDisplay = when {
pokemonInfo.primaryType != null && pokemonInfo.secondaryType != null ->
"${pokemonInfo.primaryType} / ${pokemonInfo.secondaryType}"
pokemonInfo.primaryType != null -> pokemonInfo.primaryType
else -> "Unknown"
}
addView(createTwoColumnRow(
leftLabel = "Level", leftValue = pokemonInfo.level?.let { String.format("%.1f", it) } ?: "N/A",
rightLabel = "IV %", rightValue = pokemonInfo.stats?.perfectIV?.let { "${String.format("%.1f", it)}%" } ?: "N/A"
leftLabel = "Type", leftValue = typeDisplay,
rightLabel = "Tera", rightValue = pokemonInfo.teraType ?: "N/A"
))
// Individual Stats Section (if available)
// Stats Section (if available)
pokemonInfo.stats?.let { stats ->
if (stats.attack != null || stats.defense != null || stats.stamina != null) {
addView(createSectionHeader("Individual Stats"))
addView(createThreeColumnRow(
leftLabel = "ATK", leftValue = stats.attack?.toString() ?: "?",
middleLabel = "DEF", middleValue = stats.defense?.toString() ?: "?",
rightLabel = "STA", rightValue = stats.stamina?.toString() ?: "?"
))
}
addView(createSectionHeader("Base Stats"))
addView(createThreeColumnRow(
leftLabel = "HP", leftValue = stats.hp?.toString() ?: "?",
middleLabel = "ATK", middleValue = stats.attack?.toString() ?: "?",
rightLabel = "DEF", rightValue = stats.defense?.toString() ?: "?"
))
addView(createThreeColumnRow(
leftLabel = "SP.ATK", leftValue = stats.spAttack?.toString() ?: "?",
middleLabel = "SP.DEF", middleValue = stats.spDefense?.toString() ?: "?",
rightLabel = "SPEED", rightValue = stats.speed?.toString() ?: "?"
))
}
// Special Properties Section
addView(createSectionHeader("Properties"))
addView(createCheckboxRow(
leftLabel = "Shiny", leftChecked = false, // TODO: get from pokemonInfo when available
rightLabel = "Alpha", rightChecked = false // TODO: get from pokemonInfo when available
leftLabel = "Shiny", leftChecked = pokemonInfo.isShiny,
rightLabel = "Alpha", rightChecked = pokemonInfo.isAlpha
))
addView(createMixedRow(
leftLabel = "Favorited", leftChecked = pokemonInfo.isFavorited,
rightLabel = "Pokeball", rightValue = pokemonInfo.pokeballType ?: "Unknown"
))
// Game Origin Section
addView(createSectionHeader("Origin"))
addView(createTwoColumnRow(
leftLabel = "Game", leftValue = pokemonInfo.gameSource ?: "Unknown",
rightLabel = "Language", rightValue = pokemonInfo.language ?: "Unknown"
))
pokemonInfo.originalTrainerName?.let { trainerName ->
addView(createTwoColumnRow(
leftLabel = "OT Name", leftValue = trainerName,
rightLabel = "OT ID", rightValue = pokemonInfo.originalTrainerId ?: "Unknown"
))
}
// Ability & Moves
pokemonInfo.ability?.let { ability ->
addView(createSectionHeader("Ability & Moves"))
addView(createDetailRow("Ability", ability))
}
if (pokemonInfo.moves.isNotEmpty()) {
addView(createDetailRow("Moves", pokemonInfo.moves.take(4).joinToString(", ")))
}
// Additional Data
if (pokemonInfo.stamps.isNotEmpty() || pokemonInfo.labels.isNotEmpty() || pokemonInfo.marks.isNotEmpty()) {
addView(createSectionHeader("Additional"))
if (pokemonInfo.stamps.isNotEmpty()) {
addView(createDetailRow("Stamps", pokemonInfo.stamps.joinToString(", ")))
}
if (pokemonInfo.labels.isNotEmpty()) {
addView(createDetailRow("Labels", pokemonInfo.labels.joinToString(", ")))
}
if (pokemonInfo.marks.isNotEmpty()) {
addView(createDetailRow("Marks", pokemonInfo.marks.joinToString(", ")))
}
}
}
else
{
@ -598,6 +648,45 @@ class ResultsBottomDrawer(private val context: Context)
}
}
private fun createMixedRow(
leftLabel: String, leftChecked: Boolean,
rightLabel: String, rightValue: String
): LinearLayout
{
return LinearLayout(context).apply {
orientation = LinearLayout.HORIZONTAL
gravity = Gravity.CENTER_VERTICAL
// Left checkbox
val leftCheckbox = createCheckboxItem(leftLabel, leftChecked)
leftCheckbox.layoutParams = LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
)
// Right text item
val rightItem = createColumnItem(rightLabel, rightValue)
rightItem.layoutParams = LinearLayout.LayoutParams(
0,
ViewGroup.LayoutParams.WRAP_CONTENT,
1f
).apply {
setMargins(dpToPx(8), 0, 0, 0)
}
addView(leftCheckbox)
addView(rightItem)
layoutParams = LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
).apply {
setMargins(0, dpToPx(2), 0, dpToPx(2))
}
}
}
private fun createDrawerBackground(): GradientDrawable
{
return GradientDrawable().apply {

Loading…
Cancel
Save