@ -178,6 +178,19 @@ class HistoryAdapter(
}
}
private fun createExpandedContent ( context : Context ) : View
private fun createExpandedContent ( context : Context ) : View
{
// Create placeholder that will be replaced when content is needed
return android . widget . FrameLayout ( context ) . apply {
layoutParams = LinearLayout . LayoutParams (
LinearLayout . LayoutParams . MATCH_PARENT ,
LinearLayout . LayoutParams . WRAP_CONTENT
)
tag = " expanded_content "
visibility = View . GONE
}
}
private fun createPopulatedExpandedContent ( context : Context , result : DetectionResult ) : View
{
{
return androidx . core . widget . NestedScrollView ( context ) . apply {
return androidx . core . widget . NestedScrollView ( context ) . apply {
// Set a reasonable fixed height for the scrollable area
// Set a reasonable fixed height for the scrollable area
@ -189,6 +202,7 @@ class HistoryAdapter(
// Configure scrolling behavior optimized for nested scrolling
// Configure scrolling behavior optimized for nested scrolling
isFillViewport = false
isFillViewport = false
isNestedScrollingEnabled = true
isNestedScrollingEnabled = true
tag = " expanded_content "
val contentContainer = LinearLayout ( context ) . apply {
val contentContainer = LinearLayout ( context ) . apply {
orientation = LinearLayout . VERTICAL
orientation = LinearLayout . VERTICAL
@ -200,6 +214,16 @@ class HistoryAdapter(
tag = " expanded_container "
tag = " expanded_container "
}
}
// Populate content immediately - this is the key fix!
if ( result . success && result . pokemonInfo != null ) {
populatePokemonInfoViewsForContainer ( result . pokemonInfo , contentContainer , context )
} else {
populateErrorInfoViewsForContainer ( result , contentContainer , context )
}
// Technical info
populateTechnicalInfoViewsForContainer ( result , contentContainer , context )
addView ( contentContainer )
addView ( contentContainer )
}
}
}
}
@ -223,469 +247,478 @@ class HistoryAdapter(
) . toInt ( )
) . toInt ( )
}
}
inner class HistoryViewHolder ( itemView : View ) : RecyclerView . ViewHolder ( itemView )
private fun populatePokemonInfoViewsForContainer ( pokemonInfo : com . quillstudios . pokegoalshelper . PokemonInfo , container : LinearLayout , context : Context )
{
{
private val cardContainer = itemView as LinearLayout
// Basic Pokemon Info Section
private val collapsedContent = itemView . findViewWithTag < LinearLayout > ( " collapsed_content " )
container . addView ( createSectionHeaderForContainer ( " Pokemon Info " , context ) )
private val expandedContent = itemView . findViewWithTag < androidx . core . widget . NestedScrollView > ( " expanded_content " )
container . addView ( createTwoColumnRowForContainer (
private val expandedContainer = expandedContent . findViewWithTag < LinearLayout > ( " expanded_container " )
leftLabel = " Species " , leftValue = pokemonInfo . species ?: " Unknown " ,
rightLabel = " Dex # " , rightValue = pokemonInfo . nationalDexNumber ?. let { " # $it " } ?: " N/A " ,
// Collapsed content views
context = context
private val statusIcon = collapsedContent . findViewWithTag < ImageView > ( " status_icon " )
) )
private val titleText = collapsedContent . findViewWithTag < TextView > ( " title_text " )
private val subtitleText = collapsedContent . findViewWithTag < TextView > ( " subtitle_text " )
container . addView ( createTwoColumnRowForContainer (
private val chevronIcon = collapsedContent . findViewWithTag < TextView > ( " chevron_icon " )
leftLabel = " Nickname " , leftValue = pokemonInfo . nickname ?: " None " ,
rightLabel = " Gender " , rightValue = pokemonInfo . gender ?: " Unknown " ,
context = context
) )
container . addView ( createTwoColumnRowForContainer (
leftLabel = " Level " , leftValue = pokemonInfo . level ?. toString ( ) ?: " N/A " ,
rightLabel = " Nature " , rightValue = pokemonInfo . nature ?: " Unknown " ,
context = context
) )
// Types Section
container . addView ( createSectionHeaderForContainer ( " Types " , context ) )
val typeDisplay = when {
pokemonInfo . primaryType != null && pokemonInfo . secondaryType != null ->
" ${pokemonInfo.primaryType} / ${pokemonInfo.secondaryType} "
pokemonInfo . primaryType != null -> pokemonInfo . primaryType
else -> " Unknown "
}
container . addView ( createTwoColumnRowForContainer (
leftLabel = " Type " , leftValue = typeDisplay ,
rightLabel = " Tera " , rightValue = pokemonInfo . teraType ?: " N/A " ,
context = context
) )
// Stats Section (if available)
pokemonInfo . stats ?. let { stats ->
container . addView ( createSectionHeaderForContainer ( " Base Stats " , context ) )
container . addView ( createThreeColumnRowForContainer (
leftLabel = " HP " , leftValue = stats . hp ?. toString ( ) ?: " ? " ,
middleLabel = " ATK " , middleValue = stats . attack ?. toString ( ) ?: " ? " ,
rightLabel = " DEF " , rightValue = stats . defense ?. toString ( ) ?: " ? " ,
context = context
) )
container . addView ( createThreeColumnRowForContainer (
leftLabel = " SP.ATK " , leftValue = stats . spAttack ?. toString ( ) ?: " ? " ,
middleLabel = " SP.DEF " , middleValue = stats . spDefense ?. toString ( ) ?: " ? " ,
rightLabel = " SPEED " , rightValue = stats . speed ?. toString ( ) ?: " ? " ,
context = context
) )
}
fun bind ( item : HistoryItem , position : Int )
// Special Properties Section
{
container . addView ( createSectionHeaderForContainer ( " Properties " , context ) )
val result = item . result
container . addView ( createCheckboxRowForContainer (
val context = itemView . context
leftLabel = " Shiny " , leftChecked = pokemonInfo . isShiny ,
rightLabel = " Alpha " , rightChecked = pokemonInfo . isAlpha ,
context = context
) )
container . addView ( createMixedRowForContainer (
leftLabel = " Favorited " , leftChecked = pokemonInfo . isFavorited ,
rightLabel = " Pokeball " , rightValue = pokemonInfo . pokeballType ?: " Unknown " ,
context = context
) )
// Game Origin Section
container . addView ( createSectionHeaderForContainer ( " Origin " , context ) )
container . addView ( createTwoColumnRowForContainer (
leftLabel = " Game " , leftValue = pokemonInfo . gameSource ?: " Unknown " ,
rightLabel = " Language " , rightValue = pokemonInfo . language ?: " Unknown " ,
context = context
) )
pokemonInfo . originalTrainerName ?. let { trainerName ->
container . addView ( createTwoColumnRowForContainer (
leftLabel = " OT Name " , leftValue = trainerName ,
rightLabel = " OT ID " , rightValue = pokemonInfo . originalTrainerId ?: " Unknown " ,
context = context
) )
}
// Update collapsed content
// Ability & Moves
updateCollapsedContent ( result , context )
pokemonInfo . ability ?. let { ability ->
container . addView ( createSectionHeaderForContainer ( " Ability & Moves " , context ) )
container . addView ( createInfoRowForContainer ( " Ability " , ability , context ) )
}
// Update expanded content if needed
if ( pokemonInfo . moves . isNotEmpty ( ) ) {
if ( item . isExpanded ) {
if ( pokemonInfo . ability == null ) {
updateExpandedContent ( result , context )
container . addView ( createSectionHeaderForContainer ( " Moves " , context ) )
showExpandedContent ( )
} else {
hideExpandedContent ( )
}
}
container . addView ( createInfoRowForContainer ( " Moves " , pokemonInfo . moves . take ( 4 ) . joinToString ( " , " ) , context ) )
}
// Set click listeners
// Additional Data
collapsedContent . setOnClickListener {
if ( pokemonInfo . stamps . isNotEmpty ( ) || pokemonInfo . labels . isNotEmpty ( ) || pokemonInfo . marks . isNotEmpty ( ) ) {
onItemClick ( result , position )
container . addView ( createSectionHeaderForContainer ( " Additional " , context ) )
if ( pokemonInfo . stamps . isNotEmpty ( ) ) {
container . addView ( createInfoRowForContainer ( " Stamps " , pokemonInfo . stamps . joinToString ( " , " ) , context ) )
}
}
if ( pokemonInfo . labels . isNotEmpty ( ) ) {
// Add delete button in expanded state
container . addView ( createInfoRowForContainer ( " Labels " , pokemonInfo . labels . joinToString ( " , " ) , context ) )
if ( item . isExpanded ) {
}
addDeleteButton ( result , position , context )
if ( pokemonInfo . marks . isNotEmpty ( ) ) {
container . addView ( createInfoRowForContainer ( " Marks " , pokemonInfo . marks . joinToString ( " , " ) , context ) )
}
}
}
}
}
private fun updateCollapsedContent ( result : DetectionResult , context : Context )
private fun populateErrorInfoViewsForContainer ( result : DetectionResult , container : LinearLayout , context : Context )
{
{
// Status icon
container . addView ( createSectionHeaderForContainer ( " Error Details " , context ) )
statusIcon . setImageResource (
container . addView ( createInfoRowForContainer ( " Status " , if ( result . success ) " No Pokemon found " else " Detection failed " , context ) )
if ( result . success ) android . R . drawable . ic_menu_myplaces
result . errorMessage ?. let { container . addView ( createInfoRowForContainer ( " Error " , it , context ) ) }
else android . R . drawable . ic_dialog_alert
}
)
statusIcon . setColorFilter (
ContextCompat . getColor (
context ,
if ( result . success ) android . R . color . holo_green_light
else android . R . color . holo_red_light
)
)
// Title (Pokemon name or status)
titleText . text = when {
result . success && result . pokemonInfo ?. species != null -> {
result . pokemonInfo . species +
( result . pokemonInfo . nationalDexNumber ?. let { " (# $it ) " } ?: " " )
}
result . success -> " Pokemon Detected "
else -> " Detection Failed "
}
// Subtitle (timestamp and processing time)
private fun populateTechnicalInfoViewsForContainer ( result : DetectionResult , container : LinearLayout , context : Context )
val formatter = DateTimeFormatter . ofPattern ( " MMM dd, HH:mm " )
{
subtitleText . text = " ${result.timestamp.format(formatter)} • ${result.processingTimeMs} ms "
container . addView ( createSectionHeaderForContainer ( " Technical Info " , context ) )
container . addView ( createInfoRowForContainer ( " Processing Time " , " ${result.processingTimeMs} ms " , context ) )
container . addView ( createInfoRowForContainer ( " Detections Found " , result . detections . size . toString ( ) , context ) )
container . addView ( createInfoRowForContainer ( " Timestamp " , result . timestamp . format ( DateTimeFormatter . ofPattern ( " yyyy-MM-dd HH:mm:ss " ) ) , context ) )
}
// Chevron rotation based on expansion state
// Helper methods for container population - using the methods from the inner class
val rotation = if ( expandedContent . visibility == View . VISIBLE ) 180f else 0f
private fun createSectionHeaderForContainer ( title : String , context : Context ) : TextView
chevronIcon . rotation = rotation
{
return TextView ( context ) . apply {
text = title
setTextSize ( TypedValue . COMPLEX_UNIT_SP , 14f )
setTextColor ( ContextCompat . getColor ( context , android . R . color . holo_blue_light ) )
typeface = android . graphics . Typeface . DEFAULT_BOLD
setPadding ( 0 , dpToPx ( context , 8 ) , 0 , dpToPx ( context , 4 ) )
}
}
}
private fun updateExpandedContent ( result : DetectionResult , context : Context )
private fun createInfoRowForContainer ( label : String , value : String , context : Context ) : LinearLayout
{
{
expandedContainer . removeAllViews ( )
val row = LinearLayout ( context ) . apply {
orientation = LinearLayout . HORIZONTAL
if ( result . success && result . pokemonInfo != null ) {
setPadding ( 0 , dpToPx ( context , 2 ) , 0 , dpToPx ( context , 2 ) )
addPokemonInfoViews ( result . pokemonInfo , context )
} else {
addErrorInfoViews ( result , context )
}
// Technical info
addTechnicalInfoViews ( result , context )
}
}
private fun addPokemonInfoViews ( pokemonInfo : com . quillstudios . pokegoalshelper . PokemonInfo , context : Context )
val labelView = TextView ( context ) . apply {
{
text = " $label : "
// Basic Pokemon Info Section
setTextSize ( TypedValue . COMPLEX_UNIT_SP , 12f )
addSectionHeader ( " Pokemon Info " , context )
setTextColor ( ContextCompat . getColor ( context , android . R . color . darker_gray ) )
addTwoColumnRow (
layoutParams = LinearLayout . LayoutParams (
leftLabel = " Species " , leftValue = pokemonInfo . species ?: " Unknown " ,
dpToPx ( context , 100 ) ,
rightLabel = " Dex # " , rightValue = pokemonInfo . nationalDexNumber ?. let { " # $it " } ?: " N/A " ,
ViewGroup . LayoutParams . WRAP_CONTENT
context = context
)
)
}
addTwoColumnRow (
val valueView = TextView ( context ) . apply {
leftLabel = " Nickname " , leftValue = pokemonInfo . nickname ?: " None " ,
text = value
rightLabel = " Gender " , rightValue = pokemonInfo . gender ?: " Unknown " ,
setTextSize ( TypedValue . COMPLEX_UNIT_SP , 12f )
context = context
setTextColor ( ContextCompat . getColor ( context , android . R . color . white ) )
layoutParams = LinearLayout . LayoutParams (
0 ,
ViewGroup . LayoutParams . WRAP_CONTENT ,
1f
)
)
}
addTwoColumnRow (
row . addView ( labelView )
leftLabel = " Level " , leftValue = pokemonInfo . level ?. toString ( ) ?: " N/A " ,
row . addView ( valueView )
rightLabel = " Nature " , rightValue = pokemonInfo . nature ?: " Unknown " ,
return row
context = context
}
)
// Types Section
private fun createTwoColumnRowForContainer ( leftLabel : String , leftValue : String , rightLabel : String , rightValue : String , context : Context ) : LinearLayout
addSectionHeader ( " Types " , context )
{
val typeDisplay = when {
val row = LinearLayout ( context ) . apply {
pokemonInfo . primaryType != null && pokemonInfo . secondaryType != null ->
orientation = LinearLayout . HORIZONTAL
" ${pokemonInfo.primaryType} / ${pokemonInfo.secondaryType} "
setPadding ( 0 , dpToPx ( context , 2 ) , 0 , dpToPx ( context , 2 ) )
pokemonInfo . primaryType != null -> pokemonInfo . primaryType
}
else -> " Unknown "
}
addTwoColumnRow (
leftLabel = " Type " , leftValue = typeDisplay ,
rightLabel = " Tera " , rightValue = pokemonInfo . teraType ?: " N/A " ,
context = context
)
// Stats Section (if available)
// Left column
pokemonInfo . stats ?. let { stats ->
val leftColumn = createColumnItemForContainer ( leftLabel , leftValue , context )
addSectionHeader ( " Base Stats " , context )
leftColumn . layoutParams = LinearLayout . LayoutParams (
addThreeColumnRow (
0 ,
leftLabel = " HP " , leftValue = stats . hp ?. toString ( ) ?: " ? " ,
ViewGroup . LayoutParams . WRAP_CONTENT ,
middleLabel = " ATK " , middleValue = stats . attack ?. toString ( ) ?: " ? " ,
1f
rightLabel = " DEF " , rightValue = stats . defense ?. toString ( ) ?: " ? " ,
)
context = context
)
addThreeColumnRow (
leftLabel = " SP.ATK " , leftValue = stats . spAttack ?. toString ( ) ?: " ? " ,
middleLabel = " SP.DEF " , middleValue = stats . spDefense ?. toString ( ) ?: " ? " ,
rightLabel = " SPEED " , rightValue = stats . speed ?. toString ( ) ?: " ? " ,
context = context
)
}
// Special Properties Section
// Right column
addSectionHeader ( " Properties " , context )
val rightColumn = createColumnItemForContainer ( rightLabel , rightValue , context )
addCheckboxRow (
rightColumn . layoutParams = LinearLayout . LayoutParams (
leftLabel = " Shiny " , leftChecked = pokemonInfo . isShiny ,
0 ,
rightLabel = " Alpha " , rightChecked = pokemonInfo . isAlpha ,
ViewGroup . LayoutParams . WRAP_CONTENT ,
context = context
1f
)
) . apply {
addMixedRow (
setMargins ( dpToPx ( context , 8 ) , 0 , 0 , 0 )
leftLabel = " Favorited " , leftChecked = pokemonInfo . isFavorited ,
}
rightLabel = " Pokeball " , rightValue = pokemonInfo . pokeballType ?: " Unknown " ,
context = context
)
// Game Origin Section
row . addView ( leftColumn )
addSectionHeader ( " Origin " , context )
row . addView ( rightColumn )
addTwoColumnRow (
return row
leftLabel = " Game " , leftValue = pokemonInfo . gameSource ?: " Unknown " ,
}
rightLabel = " Language " , rightValue = pokemonInfo . language ?: " Unknown " ,
context = context
)
pokemonInfo . originalTrainerName ?. let { trainerName ->
private fun createThreeColumnRowForContainer ( leftLabel : String , leftValue : String , middleLabel : String , middleValue : String , rightLabel : String , rightValue : String , context : Context ) : LinearLayout
addTwoColumnRow (
{
leftLabel = " OT Name " , leftValue = trainerName ,
val row = LinearLayout ( context ) . apply {
rightLabel = " OT ID " , rightValue = pokemonInfo . originalTrainerId ?: " Unknown " ,
orientation = LinearLayout . HORIZONTAL
context = context
setPadding ( 0 , dpToPx ( context , 2 ) , 0 , dpToPx ( context , 2 ) )
)
}
}
// Ability & Moves
// Left column
pokemonInfo . ability ?. let { ability ->
val leftColumn = createColumnItemForContainer ( leftLabel , leftValue , context )
addSectionHeader ( " Ability & Moves " , context )
leftColumn . layoutParams = LinearLayout . LayoutParams (
addInfoRow ( " Ability " , ability , context )
0 ,
}
ViewGroup . LayoutParams . WRAP_CONTENT ,
1f
)
if ( pokemonInfo . moves . isNotEmpty ( ) ) {
// Middle column
if ( pokemonInfo . ability == null ) {
val middleColumn = createColumnItemForContainer ( middleLabel , middleValue , context )
addSectionHeader ( " Moves " , context )
middleColumn . layoutParams = LinearLayout . LayoutParams (
}
0 ,
addInfoRow ( " Moves " , pokemonInfo . moves . take ( 4 ) . joinToString ( " , " ) , context )
ViewGroup . LayoutParams . WRAP_CONTENT ,
}
1f
) . apply {
setMargins ( dpToPx ( context , 4 ) , 0 , dpToPx ( context , 4 ) , 0 )
}
// Additional Data
// Right column
if ( pokemonInfo . stamps . isNotEmpty ( ) || pokemonInfo . labels . isNotEmpty ( ) || pokemonInfo . marks . isNotEmpty ( ) ) {
val rightColumn = createColumnItemForContainer ( rightLabel , rightValue , context )
addSectionHeader ( " Additional " , context )
rightColumn . layoutParams = LinearLayout . LayoutParams (
if ( pokemonInfo . stamps . isNotEmpty ( ) ) {
0 ,
addInfoRow ( " Stamps " , pokemonInfo . stamps . joinToString ( " , " ) , context )
ViewGroup . LayoutParams . WRAP_CONTENT ,
}
1f
if ( pokemonInfo . labels . isNotEmpty ( ) ) {
)
addInfoRow ( " Labels " , pokemonInfo . labels . joinToString ( " , " ) , context )
}
row . addView ( leftColumn )
if ( pokemonInfo . marks . isNotEmpty ( ) ) {
row . addView ( middleColumn )
addInfoRow ( " Marks " , pokemonInfo . marks . joinToString ( " , " ) , context )
row . addView ( rightColumn )
}
return row
}
}
private fun createCheckboxRowForContainer ( leftLabel : String , leftChecked : Boolean , rightLabel : String , rightChecked : Boolean , context : Context ) : LinearLayout
{
val row = LinearLayout ( context ) . apply {
orientation = LinearLayout . HORIZONTAL
setPadding ( 0 , dpToPx ( context , 2 ) , 0 , dpToPx ( context , 2 ) )
}
}
private fun addErrorInfoViews ( result : DetectionResult , context : Context )
// Left checkbox
{
val leftCheckbox = createCheckboxItemForContainer ( leftLabel , leftChecked , context )
addSectionHeader ( " Error Details " , context )
leftCheckbox . layoutParams = LinearLayout . LayoutParams (
addInfoRow ( " Status " , if ( result . success ) " No Pokemon found " else " Detection failed " , context )
0 ,
result . errorMessage ?. let { addInfoRow ( " Error " , it , context ) }
ViewGroup . LayoutParams . WRAP_CONTENT ,
1f
)
// Right checkbox
val rightCheckbox = createCheckboxItemForContainer ( rightLabel , rightChecked , context )
rightCheckbox . layoutParams = LinearLayout . LayoutParams (
0 ,
ViewGroup . LayoutParams . WRAP_CONTENT ,
1f
) . apply {
setMargins ( dpToPx ( context , 8 ) , 0 , 0 , 0 )
}
}
private fun addTechnicalInfoViews ( result : DetectionResult , context : Context )
row . addView ( leftCheckbox )
{
row . addView ( rightCheckbox )
addSectionHeader ( " Technical Info " , context )
return row
addInfoRow ( " Processing Time " , " ${result.processingTimeMs} ms " , context )
}
addInfoRow ( " Detections Found " , result . detections . size . toString ( ) , context )
addInfoRow ( " Timestamp " , result . timestamp . format ( DateTimeFormatter . ofPattern ( " yyyy-MM-dd HH:mm:ss " ) ) , context )
private fun createMixedRowForContainer ( leftLabel : String , leftChecked : Boolean , rightLabel : String , rightValue : String , context : Context ) : LinearLayout
{
val row = LinearLayout ( context ) . apply {
orientation = LinearLayout . HORIZONTAL
setPadding ( 0 , dpToPx ( context , 2 ) , 0 , dpToPx ( context , 2 ) )
}
}
private fun addSectionHeader ( title : String , context : Context )
// Left checkbox
{
val leftCheckbox = createCheckboxItemForContainer ( leftLabel , leftChecked , context )
val header = TextView ( context ) . apply {
leftCheckbox . layoutParams = LinearLayout . LayoutParams (
text = title
0 ,
setTextSize ( TypedValue . COMPLEX_UNIT_SP , 14f )
ViewGroup . LayoutParams . WRAP_CONTENT ,
setTextColor ( ContextCompat . getColor ( context , android . R . color . holo_blue_light ) )
1f
typeface = android . graphics . Typeface . DEFAULT_BOLD
)
setPadding ( 0 , dpToPx ( context , 8 ) , 0 , dpToPx ( context , 4 ) )
}
// Right text item
expandedContainer . addView ( header )
val rightItem = createColumnItemForContainer ( rightLabel , rightValue , context )
rightItem . layoutParams = LinearLayout . LayoutParams (
0 ,
ViewGroup . LayoutParams . WRAP_CONTENT ,
1f
) . apply {
setMargins ( dpToPx ( context , 8 ) , 0 , 0 , 0 )
}
}
private fun addInfoRow ( label : String , value : String , context : Context )
row . addView ( leftCheckbox )
{
row . addView ( rightItem )
val row = LinearLayout ( context ) . apply {
return row
orientation = LinearLayout . HORIZONTAL
}
setPadding ( 0 , dpToPx ( context , 2 ) , 0 , dpToPx ( context , 2 ) )
}
private fun createColumnItemForContainer ( label : String , value : String , context : Context ) : LinearLayout
{
return LinearLayout ( context ) . apply {
orientation = LinearLayout . VERTICAL
gravity = android . view . Gravity . START
val labelView = TextView ( context ) . apply {
val labelView = TextView ( context ) . apply {
text = " $label : "
text = " $label : "
setTextSize ( TypedValue . COMPLEX_UNIT_SP , 12f )
setTextSize ( TypedValue . COMPLEX_UNIT_SP , 9 f)
setTextColor ( ContextCompat . getColor ( context , android . R . color . darker_gray ) )
setTextColor ( ContextCompat . getColor ( context , android . R . color . darker_gray ) )
layoutParams = LinearLayout . LayoutParams (
dpToPx ( context , 100 ) ,
ViewGroup . LayoutParams . WRAP_CONTENT
)
}
}
val valueView = TextView ( context ) . apply {
val valueView = TextView ( context ) . apply {
text = value
text = value
setTextSize ( TypedValue . COMPLEX_UNIT_SP , 12 f )
setTextSize ( TypedValue . COMPLEX_UNIT_SP , 11 f )
setTextColor ( ContextCompat . getColor ( context , android . R . color . white ) )
setTextColor ( ContextCompat . getColor ( context , android . R . color . white ) )
layoutParams = LinearLayout . LayoutParams (
typeface = android . graphics . Typeface . DEFAULT_BOLD
0 ,
ViewGroup . LayoutParams . WRAP_CONTENT ,
1f
)
}
}
row . addView ( labelView )
addView ( labelView )
row . addView ( valueView )
addView ( valueView )
expandedContainer . addView ( row )
}
}
}
private fun addTwoColumnRow (
private fun createCheckboxItemForContainer ( label : String , checked : Boolean , context : Context ) : LinearLayout
leftLabel : String , leftValue : String ,
{
rightLabel : String , rightValue : String ,
return LinearLayout ( context ) . apply {
context : Context
orientation = LinearLayout . HORIZONTAL
)
gravity = android . view . Gravity . CENTER_VERTICAL
{
val row = LinearLayout ( context ) . apply {
orientation = LinearLayout . HORIZONTAL
setPadding ( 0 , dpToPx ( context , 2 ) , 0 , dpToPx ( context , 2 ) )
}
// Left column
// Checkbox symbol
val leftColumn = createColumnItem ( leftLabel , leftValue , context )
val checkboxView = TextView ( context ) . apply {
leftColumn . layoutParams = LinearLayout . LayoutParams (
text = if ( checked ) " ☑ " else " ☐ "
0 ,
setTextSize ( TypedValue . COMPLEX_UNIT_SP , 14f )
ViewGroup . LayoutParams . WRAP_CONTENT ,
setTextColor (
1f
if ( checked ) ContextCompat . getColor ( context , android . R . color . holo_green_light )
)
else ContextCompat . getColor ( context , android . R . color . darker_gray )
)
layoutParams = LinearLayout . LayoutParams (
ViewGroup . LayoutParams . WRAP_CONTENT ,
ViewGroup . LayoutParams . WRAP_CONTENT
) . apply {
setMargins ( 0 , 0 , dpToPx ( context , 6 ) , 0 )
}
}
// Right column
// Label
val rightColumn = createColumnItem ( rightLabel , rightValue , context )
val labelView = TextView ( context ) . apply {
rightColumn . layoutParams = LinearLayout . LayoutParams (
text = label
0 ,
setTextSize ( TypedValue . COMPLEX_UNIT_SP , 11f )
ViewGroup . LayoutParams . WRAP_CONTENT ,
setTextColor ( ContextCompat . getColor ( context , android . R . color . white ) )
1f
) . apply {
setMargins ( dpToPx ( context , 8 ) , 0 , 0 , 0 )
}
}
row . addView ( leftColumn )
addView ( checkboxView )
row . addView ( rightColumn )
addView ( labelView )
expandedContainer . addView ( row )
}
}
}
private fun addThreeColumnRow (
inner class HistoryViewHolder ( itemView : View ) : RecyclerView . ViewHolder ( itemView )
leftLabel : String , leftValue : String ,
{
middleLabel : String , middleValue : String ,
private val cardContainer = itemView as LinearLayout
rightLabel : String , rightValue : String ,
private val collapsedContent = itemView . findViewWithTag < LinearLayout > ( " collapsed_content " )
context : Context
private var expandedContent : View ? = null // Will be created when needed
)
// Collapsed content views
private val statusIcon = collapsedContent . findViewWithTag < ImageView > ( " status_icon " )
private val titleText = collapsedContent . findViewWithTag < TextView > ( " title_text " )
private val subtitleText = collapsedContent . findViewWithTag < TextView > ( " subtitle_text " )
private val chevronIcon = collapsedContent . findViewWithTag < TextView > ( " chevron_icon " )
fun bind ( item : HistoryItem , position : Int )
{
{
val row = LinearLayout ( context ) . apply {
val result = item . result
orientation = LinearLayout . HORIZONTAL
val context = itemView . context
setPadding ( 0 , dpToPx ( context , 2 ) , 0 , dpToPx ( context , 2 ) )
}
// Left column
// Update collapsed content
val leftColumn = createColumnItem ( leftLabel , leftValue , context )
updateCollapsedContent ( result , context )
leftColumn . layoutParams = LinearLayout . LayoutParams (
0 ,
ViewGroup . LayoutParams . WRAP_CONTENT ,
1f
)
// Middle column
// Handle expanded content
val middleColumn = createColumnItem ( middleLabel , middleValue , context )
if ( item . isExpanded ) {
middleColumn . layoutParams = LinearLayout . LayoutParams (
ensureExpandedContent ( result , context )
0 ,
showExpandedContent ( )
ViewGroup . LayoutParams . WRAP_CONTENT ,
} else {
1f
hideExpandedContent ( )
) . apply {
setMargins ( dpToPx ( context , 4 ) , 0 , dpToPx ( context , 4 ) , 0 )
}
}
// Right column
// Set click listeners
val rightColumn = createColumnItem ( rightLabel , rightValue , context )
collapsedContent . setOnClickListener {
rightColumn . layoutParams = LinearLayout . LayoutParams (
onItemClick ( result , position )
0 ,
}
ViewGroup . LayoutParams . WRAP_CONTENT ,
1f
)
row . addView ( leftColumn )
row . addView ( middleColumn )
row . addView ( rightColumn )
expandedContainer . addView ( row )
}
}
private fun createColumnItem ( label : String , value : String , context : Context ) : LinearLayout
private fun ensureExpandedContent ( result : DetectionResult , context : Context )
{
{
return LinearLayout ( context ) . apply {
// Remove existing expanded content if any
orientation = LinearLayout . VERTICAL
expandedContent ?. let { existing ->
gravity = android . view . Gravity . START
if ( existing . parent == cardContainer ) {
cardContainer . removeView ( existing )
val labelView = TextView ( context ) . apply {
text = " $label : "
setTextSize ( TypedValue . COMPLEX_UNIT_SP , 9f )
setTextColor ( ContextCompat . getColor ( context , android . R . color . darker_gray ) )
}
}
}
val valueView = TextView ( context ) . apply {
// Create new expanded content with actual data
text = value
expandedContent = createPopulatedExpandedContent ( context , result ) . apply {
setTextSize ( TypedValue . COMPLEX_UNIT_SP , 11f )
// Add delete button to the content
setTextColor ( ContextCompat . getColor ( context , android . R . color . white ) )
val scrollView = this as androidx . core . widget . NestedScrollView
typeface = android . graphics . Typeface . DEFAULT_BOLD
val container = scrollView . findViewWithTag < LinearLayout > ( " expanded_container " )
container ?. let {
addDeleteButton ( result , adapterPosition , context , it )
}
}
addView ( labelView )
addView ( valueView )
}
}
// Add to card container
expandedContent ?. let { cardContainer . addView ( it ) }
}
}
private fun addCheckboxRow (
private fun updateCollapsedContent ( result : DetectionResult , context : Context )
leftLabel : String , leftChecked : Boolean ,
rightLabel : String , rightChecked : Boolean ,
context : Context
)
{
{
val row = LinearLayout ( context ) . apply {
// Status icon
orientation = LinearLayout . HORIZONTAL
statusIcon . setImageResource (
setPadding ( 0 , dpToPx ( context , 2 ) , 0 , dpToPx ( context , 2 ) )
if ( result . success ) android . R . drawable . ic_menu_myplaces
}
else android . R . drawable . ic_dialog_alert
)
// Left checkbox
statusIcon . setColorFilter (
val leftCheckbox = createCheckboxItem ( leftLabel , leftChecked , context )
ContextCompat . getColor (
leftCheckbox . layoutParams = LinearLayout . LayoutParams (
context ,
0 ,
if ( result . success ) android . R . color . holo_green_light
ViewGroup . LayoutParams . WRAP_CONTENT ,
else android . R . color . holo_red_light
1f
)
)
)
// Right checkbox
// Title (Pokemon name or status)
val rightCheckbox = createCheckboxItem ( rightLabel , rightChecked , context )
titleText . text = when {
rightCheckbox . layoutParams = LinearLayout . LayoutParams (
result . success && result . pokemonInfo ?. species != null -> {
0 ,
result . pokemonInfo . species +
ViewGroup . LayoutParams . WRAP_CONTENT ,
( result . pokemonInfo . nationalDexNumber ?. let { " (# $it ) " } ?: " " )
1f
}
) . apply {
result . success -> " Pokemon Detected "
setMargins ( dpToPx ( context , 8 ) , 0 , 0 , 0 )
else -> " Detection Failed "
}
}
row . addView ( leftCheckbox )
// Subtitle (timestamp and processing time)
row . addView ( rightCheckbox )
val formatter = DateTimeFormatter . ofPattern ( " MMM dd, HH:mm " )
expandedContainer . addView ( row )
subtitleText . text = " ${result.timestamp.format(formatter)} • ${result.processingTimeMs} ms "
// Chevron rotation based on expansion state
val rotation = if ( expandedContent ?. visibility == View . VISIBLE ) 180f else 0f
chevronIcon . rotation = rotation
}
}
private fun addMixedRow (
leftLabel : String , leftChecked : Boolean ,
rightLabel : String , rightValue : String ,
context : Context
)
{
val row = LinearLayout ( context ) . apply {
orientation = LinearLayout . HORIZONTAL
setPadding ( 0 , dpToPx ( context , 2 ) , 0 , dpToPx ( context , 2 ) )
}
// Left checkbox
private fun showExpandedContent ( )
val leftCheckbox = createCheckboxItem ( leftLabel , leftChecked , context )
{
leftCheckbox . layoutParams = LinearLayout . LayoutParams (
expandedContent ?. visibility = View . VISIBLE
0 ,
ViewGroup . LayoutParams . WRAP_CONTENT ,
1f
)
// Right text item
// Animate chevron rotation
val rightItem = createColumnItem ( rightLabel , rightValue , context )
ObjectAnimator . ofFloat ( chevronIcon , " rotation " , 0f , 180f ) . apply {
rightItem . layoutParams = LinearLayout . LayoutParams (
duration = 200L
0 ,
start ( )
ViewGroup . LayoutParams . WRAP_CONTENT ,
1f
) . apply {
setMargins ( dpToPx ( context , 8 ) , 0 , 0 , 0 )
}
}
row . addView ( leftCheckbox )
row . addView ( rightItem )
expandedContainer . addView ( row )
}
}
private fun createCheckboxItem ( label : String , checked : Boolean , context : Context ) : LinearLayout
private fun hideExpandedContent ( )
{
{
return LinearLayout ( context ) . apply {
expandedContent ?. visibility = View . GONE
orientation = LinearLayout . HORIZONTAL
gravity = android . view . Gravity . CENTER_VERTICAL
// Checkbox symbol
val checkboxView = TextView ( context ) . apply {
text = if ( checked ) " ☑ " else " ☐ "
setTextSize ( TypedValue . COMPLEX_UNIT_SP , 14f )
setTextColor (
if ( checked ) ContextCompat . getColor ( context , android . R . color . holo_green_light )
else ContextCompat . getColor ( context , android . R . color . darker_gray )
)
layoutParams = LinearLayout . LayoutParams (
ViewGroup . LayoutParams . WRAP_CONTENT ,
ViewGroup . LayoutParams . WRAP_CONTENT
) . apply {
setMargins ( 0 , 0 , dpToPx ( context , 6 ) , 0 )
}
}
// Label
val labelView = TextView ( context ) . apply {
text = label
setTextSize ( TypedValue . COMPLEX_UNIT_SP , 11f )
setTextColor ( ContextCompat . getColor ( context , android . R . color . white ) )
}
addView ( checkboxView )
// Animate chevron rotation
addView ( labelView )
ObjectAnimator . ofFloat ( chevronIcon , " rotation " , 180f , 0f ) . apply {
duration = 200L
start ( )
}
}
}
}
private fun addDeleteButton ( result : DetectionResult , position : Int , context : Context )
private fun addDeleteButton ( result : DetectionResult , position : Int , context : Context , container : LinearLayout )
{
{
val deleteButton = Button ( context ) . apply {
val deleteButton = Button ( context ) . apply {
text = " Delete "
text = " Delete "
@ -708,29 +741,7 @@ class HistoryAdapter(
}
}
}
}
expandedContainer . addView ( deleteButton )
container . addView ( deleteButton )
}
private fun showExpandedContent ( )
{
expandedContent . visibility = View . VISIBLE
// Animate chevron rotation
ObjectAnimator . ofFloat ( chevronIcon , " rotation " , 0f , 180f ) . apply {
duration = 200L
start ( )
}
}
private fun hideExpandedContent ( )
{
expandedContent . visibility = View . GONE
// Animate chevron rotation
ObjectAnimator . ofFloat ( chevronIcon , " rotation " , 180f , 0f ) . apply {
duration = 200L
start ( )
}
}
}
}
}
}
}