mirror of
https://github.com/owenlejeune/MYDex.git
synced 2026-02-09 23:17:18 -05:00
base stats view
This commit is contained in:
17
.idea/deploymentTargetDropDown.xml
generated
Normal file
17
.idea/deploymentTargetDropDown.xml
generated
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="deploymentTargetDropDown">
|
||||||
|
<targetSelectedWithDropDown>
|
||||||
|
<Target>
|
||||||
|
<type value="QUICK_BOOT_TARGET" />
|
||||||
|
<deviceKey>
|
||||||
|
<Key>
|
||||||
|
<type value="VIRTUAL_DEVICE_PATH" />
|
||||||
|
<value value="$USER_HOME$/.android/avd/Pixel_3a_API_33.avd" />
|
||||||
|
</Key>
|
||||||
|
</deviceKey>
|
||||||
|
</Target>
|
||||||
|
</targetSelectedWithDropDown>
|
||||||
|
<timeTargetWasSelectedWithDropDown value="2022-09-19T16:50:11.183898Z" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -80,9 +80,9 @@ dependencies {
|
|||||||
implementation "androidx.compose.ui:ui-tooling-preview:$compose"
|
implementation "androidx.compose.ui:ui-tooling-preview:$compose"
|
||||||
implementation "androidx.activity:activity-compose:$compose_activity"
|
implementation "androidx.activity:activity-compose:$compose_activity"
|
||||||
implementation "com.google.accompanist:accompanist-systemuicontroller:$compose_accompanist"
|
implementation "com.google.accompanist:accompanist-systemuicontroller:$compose_accompanist"
|
||||||
// implementation "com.google.accompanist:accompanist-pager:$compose_accompanist"
|
implementation "com.google.accompanist:accompanist-pager:$compose_accompanist"
|
||||||
// implementation "com.google.accompanist:accompanist-pager-indicators:$compose_accompanist"
|
// implementation "com.google.accompanist:accompanist-pager-indicators:$compose_accompanist"
|
||||||
// implementation "com.google.accompanist:accompanist-flowlayout:$compose_accompanist"
|
implementation "com.google.accompanist:accompanist-flowlayout:$compose_accompanist"
|
||||||
// implementation "com.google.accompanist:accompanist-insets:$compose_accompanist"
|
// implementation "com.google.accompanist:accompanist-insets:$compose_accompanist"
|
||||||
implementation "androidx.navigation:navigation-compose:$compose_navigation"
|
implementation "androidx.navigation:navigation-compose:$compose_navigation"
|
||||||
implementation "androidx.paging:paging-compose:$compose_paging"
|
implementation "androidx.paging:paging-compose:$compose_paging"
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.owenlejeune.mydex.api.pokeapi.v2
|
|||||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.misc.PaginatedResponse
|
import com.owenlejeune.mydex.api.pokeapi.v2.model.misc.PaginatedResponse
|
||||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.Pokemon
|
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.Pokemon
|
||||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonSpecies
|
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonSpecies
|
||||||
|
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonStat
|
||||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonType
|
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonType
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import retrofit2.http.GET
|
import retrofit2.http.GET
|
||||||
@@ -23,4 +24,7 @@ interface PokemonApi {
|
|||||||
@GET("type/{id}")
|
@GET("type/{id}")
|
||||||
suspend fun getPokemonType(@Path("id") id: Int): Response<PokemonType>
|
suspend fun getPokemonType(@Path("id") id: Int): Response<PokemonType>
|
||||||
|
|
||||||
|
@GET("stat/{id}")
|
||||||
|
suspend fun getPokemonStat(@Path("id") id: Int): Response<PokemonStat>
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -4,6 +4,7 @@ import android.util.Log
|
|||||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.misc.PaginatedResponse
|
import com.owenlejeune.mydex.api.pokeapi.v2.model.misc.PaginatedResponse
|
||||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.Pokemon
|
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.Pokemon
|
||||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonSpecies
|
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonSpecies
|
||||||
|
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonStat
|
||||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonType
|
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonType
|
||||||
import org.koin.core.component.KoinComponent
|
import org.koin.core.component.KoinComponent
|
||||||
import org.koin.core.component.inject
|
import org.koin.core.component.inject
|
||||||
@@ -42,4 +43,8 @@ class PokemonService: KoinComponent {
|
|||||||
return service.getPokemonType(id)
|
return service.getPokemonType(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getPokemonStat(id: Int): Response<PokemonStat> {
|
||||||
|
return service.getPokemonStat(id)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -21,6 +21,6 @@ class Pokemon(
|
|||||||
@SerializedName("moves") val moves: List<PokemonMove>,
|
@SerializedName("moves") val moves: List<PokemonMove>,
|
||||||
@SerializedName("species") val species: NameAndUrl,
|
@SerializedName("species") val species: NameAndUrl,
|
||||||
@SerializedName("sprites") val sprites: Sprites,
|
@SerializedName("sprites") val sprites: Sprites,
|
||||||
@SerializedName("stats") val state: List<Stat>,
|
@SerializedName("stats") val stats: List<Stat>,
|
||||||
@SerializedName("types") val types: List<Type>
|
@SerializedName("types") val types: List<Type>
|
||||||
)
|
)
|
||||||
@@ -14,5 +14,5 @@ class PokemonStat(
|
|||||||
@SerializedName("is_battle_only") val isBattleOnly: Boolean,
|
@SerializedName("is_battle_only") val isBattleOnly: Boolean,
|
||||||
@SerializedName("move_damage_class") val moveDamageClass: NameAndUrl,
|
@SerializedName("move_damage_class") val moveDamageClass: NameAndUrl,
|
||||||
@SerializedName("name") val name: String,
|
@SerializedName("name") val name: String,
|
||||||
@SerializedName("names") val names: NameAndLanguage
|
@SerializedName("names") val names: List<NameAndLanguage>
|
||||||
)
|
)
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
package com.owenlejeune.mydex.extensions
|
||||||
|
|
||||||
|
import androidx.annotation.FloatRange
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
|
||||||
|
fun Color.adjustBy(@FloatRange(from = (-1f).toDouble(), to = 1f.toDouble()) x: Float): Color {
|
||||||
|
return copy(
|
||||||
|
red = (red+x).coerceIn(minimumValue = 0f, maximumValue = 1f),
|
||||||
|
green = (green+x).coerceIn(minimumValue = 0f, maximumValue = 1f),
|
||||||
|
blue = (blue+x).coerceIn(minimumValue = 0f, maximumValue = 1f)
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package com.owenlejeune.mydex.ui.components
|
||||||
|
|
||||||
|
import androidx.compose.ui.layout.MeasurePolicy
|
||||||
|
|
||||||
|
//fun flowLayoutMeasurePolicy() = MeasurePolicy { measurables, constraints ->
|
||||||
|
// layout(constraints.maxWidth, constraints.maxHeight) {
|
||||||
|
// val placeables = measurables.map { measurable ->
|
||||||
|
// measurable.measure(constraints)
|
||||||
|
// }
|
||||||
|
// var yPos = 0
|
||||||
|
// var xPos = 0
|
||||||
|
// var maxY = 0
|
||||||
|
// placeables.forEach { placeable ->
|
||||||
|
// if (xPos + placeable.width >
|
||||||
|
// constraints.maxWidth
|
||||||
|
// ) {
|
||||||
|
// xPos = 0
|
||||||
|
// yPos += maxY
|
||||||
|
// maxY = 0
|
||||||
|
// }
|
||||||
|
// placeable.placeRelative(
|
||||||
|
// x = xPos,
|
||||||
|
// y = yPos
|
||||||
|
// )
|
||||||
|
// xPos += placeable.width
|
||||||
|
// if (maxY < placeable.height) {
|
||||||
|
// maxY = placeable.height
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
@@ -28,6 +28,7 @@ import androidx.compose.ui.graphics.Color
|
|||||||
import androidx.compose.ui.graphics.ColorFilter
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
import androidx.compose.ui.graphics.Shape
|
import androidx.compose.ui.graphics.Shape
|
||||||
import androidx.compose.ui.graphics.SolidColor
|
import androidx.compose.ui.graphics.SolidColor
|
||||||
|
import androidx.compose.ui.layout.Layout
|
||||||
import androidx.compose.ui.modifier.modifierLocalConsumer
|
import androidx.compose.ui.modifier.modifierLocalConsumer
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
@@ -212,4 +213,30 @@ fun PokemonTypeLabel(
|
|||||||
color = Color.White
|
color = Color.White
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SmallTabIndicator(
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
color: Color = MaterialTheme.colorScheme.primary
|
||||||
|
) {
|
||||||
|
Spacer(
|
||||||
|
modifier
|
||||||
|
.padding(horizontal = 28.dp)
|
||||||
|
.height(2.dp)
|
||||||
|
.background(color, RoundedCornerShape(topStartPercent = 100, topEndPercent = 100))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
//@Composable
|
||||||
|
//fun FlowLayout(
|
||||||
|
// modifier: Modifier = Modifier,
|
||||||
|
// content: @Composable () -> Unit
|
||||||
|
//) {
|
||||||
|
// val measurePolicy = flowLayoutMeasurePolicy()
|
||||||
|
// Layout(
|
||||||
|
// measurePolicy = measurePolicy,
|
||||||
|
// content = content,
|
||||||
|
// modifier = modifier
|
||||||
|
// )
|
||||||
|
//}
|
||||||
@@ -15,7 +15,7 @@ sealed class DataNavItem(
|
|||||||
): KoinComponent {
|
): KoinComponent {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val Pages by lazy { listOf(Pokedex, Moves, Abilities, Items, Locations, TypeCharts) }
|
val Pages by lazy { listOf(Pokedex, Moves, Abilities, Items, Locations, TypeCharts, Settings) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private val resourceUtils: ResourceUtils by inject()
|
private val resourceUtils: ResourceUtils by inject()
|
||||||
@@ -28,5 +28,6 @@ sealed class DataNavItem(
|
|||||||
object Items: DataNavItem("items_route", PokeYellow, R.string.items_nav_title)
|
object Items: DataNavItem("items_route", PokeYellow, R.string.items_nav_title)
|
||||||
object Locations: DataNavItem("locations_route", PokePurple, R.string.locations_nav_title)
|
object Locations: DataNavItem("locations_route", PokePurple, R.string.locations_nav_title)
|
||||||
object TypeCharts: DataNavItem("type_charts_route", PokeBrown, R.string.type_charts_nav_title)
|
object TypeCharts: DataNavItem("type_charts_route", PokeBrown, R.string.type_charts_nav_title)
|
||||||
|
object Settings: DataNavItem("settings_route", PokeGrey, R.string.settings_nav_title)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -16,4 +16,25 @@ val PokeLightYellow = Color(0xFFFFCE4B)
|
|||||||
val PokePurple = Color(0xFF7C538C)
|
val PokePurple = Color(0xFF7C538C)
|
||||||
val PokeRed = Color(0xFFFA6555)
|
val PokeRed = Color(0xFFFA6555)
|
||||||
val PokeGreen = Color(0xFF4FC1A6)
|
val PokeGreen = Color(0xFF4FC1A6)
|
||||||
val PokeYellow = Color(0xFFF6C747)
|
val PokeYellow = Color(0xFFF6C747)
|
||||||
|
|
||||||
|
val BugType = Color(0xFF98AD19)
|
||||||
|
val DarkType = Color(0xFF5C4638)
|
||||||
|
val DragonType = Color(0xFF5B10F6)
|
||||||
|
val ElectricType = Color(0xFFF7C726)
|
||||||
|
val FairyType = Color(0xFFF389D9)
|
||||||
|
val FightingType = Color(0xFF7B211E)
|
||||||
|
val FireType = Color(0xFFEA3825)
|
||||||
|
val FlyingType = Color(0xFF8C73C6)
|
||||||
|
val GhostType = Color(0xFF5C4386)
|
||||||
|
val GrassType = Color(0xFF68C13F)
|
||||||
|
val GroundType = Color(0xFFD7B456)
|
||||||
|
val IceType = Color(0xFF88D0CF)
|
||||||
|
val NormalType = Color(0xFF979965)
|
||||||
|
val PoisonType = Color(0xFF8C288E)
|
||||||
|
val PsychicType = Color(0xFFF33D75)
|
||||||
|
val RockType = Color(0xFFA8912C)
|
||||||
|
val ShadowType = Color(0xFF312536)
|
||||||
|
val SteelType = Color(0xFFAAA8C5)
|
||||||
|
val UnknownType = Color(0xFF56917E)
|
||||||
|
val WaterType = Color(0xFF5579EC)
|
||||||
@@ -2,7 +2,9 @@ package com.owenlejeune.mydex.ui.views
|
|||||||
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
@@ -14,6 +16,7 @@ import androidx.compose.ui.draw.rotate
|
|||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.ColorFilter
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.TextStyle
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.intl.Locale
|
import androidx.compose.ui.text.intl.Locale
|
||||||
@@ -21,15 +24,25 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import coil.compose.AsyncImage
|
import coil.compose.AsyncImage
|
||||||
|
import com.google.accompanist.flowlayout.FlowRow
|
||||||
|
import com.google.accompanist.pager.ExperimentalPagerApi
|
||||||
|
import com.google.accompanist.pager.HorizontalPager
|
||||||
|
import com.google.accompanist.pager.rememberPagerState
|
||||||
import com.owenlejeune.mydex.R
|
import com.owenlejeune.mydex.R
|
||||||
import com.owenlejeune.mydex.api.pokeapi.v2.PokemonService
|
import com.owenlejeune.mydex.api.pokeapi.v2.PokemonService
|
||||||
|
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.Pokemon
|
||||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonSpecies
|
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonSpecies
|
||||||
|
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonStat
|
||||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonType
|
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonType
|
||||||
|
import com.owenlejeune.mydex.extensions.adjustBy
|
||||||
import com.owenlejeune.mydex.extensions.getIdFromUrl
|
import com.owenlejeune.mydex.extensions.getIdFromUrl
|
||||||
import com.owenlejeune.mydex.extensions.getNameForLanguage
|
import com.owenlejeune.mydex.extensions.getNameForLanguage
|
||||||
import com.owenlejeune.mydex.ui.components.PokemonTypeLabel
|
import com.owenlejeune.mydex.ui.components.PokemonTypeLabel
|
||||||
import com.owenlejeune.mydex.utils.AppCache
|
import com.owenlejeune.mydex.ui.components.SmallTabIndicator
|
||||||
import com.owenlejeune.mydex.utils.ColorUtils
|
import com.owenlejeune.mydex.utils.*
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.koin.core.component.KoinComponent
|
||||||
|
import org.koin.core.component.inject
|
||||||
import org.koin.java.KoinJavaComponent.get
|
import org.koin.java.KoinJavaComponent.get
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@@ -69,19 +82,19 @@ fun PokemonDetailView(
|
|||||||
Card(
|
Card(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(top = 140.dp),
|
.padding(top = 150.dp),
|
||||||
shape = RoundedCornerShape(topStart = 32.dp, topEnd = 32.dp),
|
shape = RoundedCornerShape(topStart = 32.dp, topEnd = 32.dp),
|
||||||
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.background)
|
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.background)
|
||||||
) {
|
) {
|
||||||
|
Details(pokemon = pokemon, pokemonSpecies = pokemonSpecies)
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = pokemon.sprites.frontDefault,
|
model = PokeUtils.spriteFromId(pokemonId),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
contentScale = ContentScale.FillBounds,
|
contentScale = ContentScale.FillBounds,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(250.dp)
|
.size(200.dp)
|
||||||
.align(Alignment.TopCenter)
|
.align(Alignment.TopCenter)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -98,7 +111,7 @@ fun PokemonDetailView(
|
|||||||
title = {},
|
title = {},
|
||||||
colors = TopAppBarDefaults
|
colors = TopAppBarDefaults
|
||||||
.smallTopAppBarColors(
|
.smallTopAppBarColors(
|
||||||
containerColor = Color.Transparent//.copy(alpha = 0.4f)
|
containerColor = Color.Transparent
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -177,6 +190,234 @@ private fun Header(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalPagerApi::class)
|
||||||
|
@Composable
|
||||||
|
private fun ColumnScope.Details(
|
||||||
|
pokemon: Pokemon,
|
||||||
|
pokemonSpecies: PokemonSpecies,
|
||||||
|
service: PokemonService = get(PokemonService::class.java)
|
||||||
|
) {
|
||||||
|
val scope = rememberCoroutineScope()
|
||||||
|
val pagerState = rememberPagerState()
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(50.dp))
|
||||||
|
|
||||||
|
val tabs = DetailTab.Items
|
||||||
|
TabRow(
|
||||||
|
selectedTabIndex = pagerState.currentPage,
|
||||||
|
divider = {}
|
||||||
|
) {
|
||||||
|
tabs.forEachIndexed { index, tab ->
|
||||||
|
Tab(
|
||||||
|
selected = pagerState.currentPage == index,
|
||||||
|
onClick = {
|
||||||
|
scope.launch { pagerState.animateScrollToPage(index) }
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Spacer(modifier = Modifier.height(18.dp))
|
||||||
|
Text(text = tab.name, color = MaterialTheme.colorScheme.onBackground)
|
||||||
|
Spacer(modifier = Modifier.height(18.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
HorizontalPager(count = tabs.size, state = pagerState) { page ->
|
||||||
|
tabs[page].screen(pokemon, pokemonSpecies, service)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun AboutView(
|
||||||
|
pokemon: Pokemon,
|
||||||
|
pokemonSpecies: PokemonSpecies,
|
||||||
|
service: PokemonService
|
||||||
|
) {
|
||||||
|
val scrollState = rememberScrollState()
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(all = 24.dp)
|
||||||
|
.verticalScroll(state = scrollState)
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
private fun BaseStatsView(
|
||||||
|
pokemon: Pokemon,
|
||||||
|
pokemonSpecies: PokemonSpecies,
|
||||||
|
service: PokemonService
|
||||||
|
) {
|
||||||
|
val scrollState = rememberScrollState()
|
||||||
|
Column(
|
||||||
|
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(all = 24.dp)
|
||||||
|
.verticalScroll(state = scrollState)
|
||||||
|
) {
|
||||||
|
pokemon.stats.forEach { stat ->
|
||||||
|
val statId = stat.stat.url.getIdFromUrl()
|
||||||
|
|
||||||
|
val fullStat = remember { mutableStateOf<PokemonStat?>(null) }
|
||||||
|
|
||||||
|
LaunchedEffect(key1 = fullStat.value) {
|
||||||
|
fetchPokemonStat(statId, fullStat, service)
|
||||||
|
}
|
||||||
|
fullStat.value?.let {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = it.names.getNameForLanguage() ?: stat.stat.name,
|
||||||
|
modifier = Modifier.fillMaxWidth(.2f)
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = stat.baseStat.toString(),
|
||||||
|
fontWeight = FontWeight.Bold,
|
||||||
|
color = MaterialTheme.colorScheme.tertiary
|
||||||
|
)
|
||||||
|
LinearProgressIndicator(
|
||||||
|
progress = (stat.baseStat.toFloat() / 255f),
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clip(RoundedCornerShape(5.dp))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val doubleDamageRelations = remember { mutableSetOf<String>() }
|
||||||
|
val halfDamageRelations = remember { mutableSetOf<String>() }
|
||||||
|
val noDamageRelations = remember { mutableSetOf<String>() }
|
||||||
|
|
||||||
|
val common = doubleDamageRelations.intersect(halfDamageRelations)
|
||||||
|
val common2 = doubleDamageRelations.intersect(noDamageRelations)
|
||||||
|
doubleDamageRelations.removeAll { it in common || it in common2 }
|
||||||
|
halfDamageRelations.removeAll { it in common || it in common2 }
|
||||||
|
noDamageRelations.removeAll { it in common || it in common2 }
|
||||||
|
|
||||||
|
if (doubleDamageRelations.isNotEmpty()) {
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.type_weaknesses_title),
|
||||||
|
fontSize = 18.sp,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(text = stringResource(R.string.type_weaknesses_description))
|
||||||
|
}
|
||||||
|
|
||||||
|
FlowRow(
|
||||||
|
mainAxisSpacing = 8.dp,
|
||||||
|
crossAxisSpacing = 8.dp
|
||||||
|
) {
|
||||||
|
if (doubleDamageRelations.isEmpty()) {
|
||||||
|
pokemon.types.forEach { t ->
|
||||||
|
val typeId = t.type.url.getIdFromUrl()
|
||||||
|
DataManager.getTypeById(typeId) { type ->
|
||||||
|
type.damageRelations.doubleDamageFrom.forEach {
|
||||||
|
val id = it.url.getIdFromUrl()
|
||||||
|
DataManager.getTypeById(id) { t ->
|
||||||
|
doubleDamageRelations.add(t.names.getNameForLanguage() ?: it.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
doubleDamageRelations.forEach { tr ->
|
||||||
|
TypeRelationChip(type = tr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (halfDamageRelations.isNotEmpty() || noDamageRelations.isNotEmpty()) {
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.type_defences_title),
|
||||||
|
fontSize = 18.sp,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (halfDamageRelations.isNotEmpty()) {
|
||||||
|
Text(text = stringResource(R.string.type_defences_half_description))
|
||||||
|
}
|
||||||
|
|
||||||
|
FlowRow(
|
||||||
|
mainAxisSpacing = 8.dp,
|
||||||
|
crossAxisSpacing = 8.dp
|
||||||
|
) {
|
||||||
|
if (halfDamageRelations.isEmpty()) {
|
||||||
|
pokemon.types.forEach { t ->
|
||||||
|
val typeId = t.type.url.getIdFromUrl()
|
||||||
|
DataManager.getTypeById(typeId) { type ->
|
||||||
|
type.damageRelations.halfDamageFrom.forEach {
|
||||||
|
val id = it.url.getIdFromUrl()
|
||||||
|
DataManager.getTypeById(id) { t ->
|
||||||
|
halfDamageRelations.add(t.names.getNameForLanguage() ?: it.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
halfDamageRelations.forEach { tr ->
|
||||||
|
TypeRelationChip(type = tr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (noDamageRelations.isNotEmpty()) {
|
||||||
|
Text(text = stringResource(R.string.type_defences_no_description))
|
||||||
|
}
|
||||||
|
|
||||||
|
FlowRow(
|
||||||
|
mainAxisSpacing = 8.dp,
|
||||||
|
crossAxisSpacing = 8.dp
|
||||||
|
) {
|
||||||
|
if (noDamageRelations.isEmpty()) {
|
||||||
|
pokemon.types.forEach { t ->
|
||||||
|
val typeId = t.type.url.getIdFromUrl()
|
||||||
|
DataManager.getTypeById(typeId) { type ->
|
||||||
|
type.damageRelations.noDamageFrom.forEach {
|
||||||
|
val id = it.url.getIdFromUrl()
|
||||||
|
DataManager.getTypeById(id) { t ->
|
||||||
|
noDamageRelations.add(t.names.getNameForLanguage() ?: it.name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
noDamageRelations.forEach { tr ->
|
||||||
|
TypeRelationChip(type = tr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
@Composable
|
||||||
|
private fun TypeRelationChip (
|
||||||
|
type: String
|
||||||
|
) {
|
||||||
|
val color = ColorUtils.pokeTypeNameToComposeColor(type)
|
||||||
|
Card(
|
||||||
|
modifier = Modifier
|
||||||
|
.clip(RoundedCornerShape(5.dp)),
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = color
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = type,
|
||||||
|
color = color.adjustBy(-.4f),
|
||||||
|
modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private suspend fun fetchPokemonType(
|
private suspend fun fetchPokemonType(
|
||||||
id: Int,
|
id: Int,
|
||||||
pokemonType: MutableState<PokemonType?>,
|
pokemonType: MutableState<PokemonType?>,
|
||||||
@@ -192,4 +433,67 @@ private suspend fun fetchPokemonType(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private suspend fun fetchPokemonStat(
|
||||||
|
statId: Int,
|
||||||
|
pokemonStat: MutableState<PokemonStat?>,
|
||||||
|
service: PokemonService
|
||||||
|
) {
|
||||||
|
if (pokemonStat.value == null) {
|
||||||
|
service.getPokemonStat(statId).apply {
|
||||||
|
if (isSuccessful) {
|
||||||
|
body()?.let {
|
||||||
|
pokemonStat.value = it
|
||||||
|
AppCache.cachedStats.put(it.id, it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class DetailTab(
|
||||||
|
stringRes: Int,
|
||||||
|
route: String,
|
||||||
|
val screen: @Composable (Pokemon, PokemonSpecies, PokemonService) -> Unit
|
||||||
|
): KoinComponent {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val Items by lazy { listOf(About, BaseStats, Evolution, Moves) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private val resourceUtils: ResourceUtils by inject()
|
||||||
|
|
||||||
|
val name: String = resourceUtils.getString(stringRes)
|
||||||
|
|
||||||
|
object About: DetailTab(
|
||||||
|
stringRes = R.string.about_tab_title,
|
||||||
|
route = "about_tab",
|
||||||
|
screen = @Composable { pokemon, pokemonSpecies, service ->
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
object BaseStats: DetailTab(
|
||||||
|
stringRes = R.string.base_stats_tab_title,
|
||||||
|
route = "base_stats_tab",
|
||||||
|
screen = @Composable { p, ps, s -> BaseStatsView(p, ps, s) }
|
||||||
|
)
|
||||||
|
|
||||||
|
object Evolution: DetailTab(
|
||||||
|
stringRes = R.string.evolution_tab_title,
|
||||||
|
route = "evolution_tab",
|
||||||
|
screen = @Composable { pokemon, pokemonSpecies, service ->
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
object Moves: DetailTab(
|
||||||
|
stringRes = R.string.moves_tab_title,
|
||||||
|
route = "moves_tab",
|
||||||
|
screen = @Composable { pokemon, pokemonSpecies, service ->
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -24,6 +24,7 @@ import com.owenlejeune.mydex.ui.components.MenuItemButton
|
|||||||
import com.owenlejeune.mydex.ui.components.SearchBar
|
import com.owenlejeune.mydex.ui.components.SearchBar
|
||||||
import com.owenlejeune.mydex.ui.navigation.DataNavItem
|
import com.owenlejeune.mydex.ui.navigation.DataNavItem
|
||||||
import org.koin.java.KoinJavaComponent
|
import org.koin.java.KoinJavaComponent
|
||||||
|
import kotlin.math.ceil
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@@ -105,14 +106,14 @@ private fun BoxScope.SingleColumnMainContent(appNavController: NavHostController
|
|||||||
Spacer(modifier = Modifier.height(32.dp))
|
Spacer(modifier = Modifier.height(32.dp))
|
||||||
|
|
||||||
val cols = 2
|
val cols = 2
|
||||||
val rows = DataNavItem.Pages.size/cols
|
val rows = ceil(DataNavItem.Pages.size.toFloat()/cols.toFloat()).toInt()
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
) {
|
) {
|
||||||
for (i in 0 until rows) {
|
for (i in 0 until rows) {
|
||||||
val first = DataNavItem.Pages[2*i]
|
val first = DataNavItem.Pages[2*i]
|
||||||
val second = if (2*i+1 <= DataNavItem.Pages.size) {
|
val second = if (2*i+1 < DataNavItem.Pages.size) {
|
||||||
DataNavItem.Pages[2*i+1]
|
DataNavItem.Pages[2*i+1]
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import com.owenlejeune.mydex.ui.components.PokemonTypeLabel
|
|||||||
import com.owenlejeune.mydex.ui.navigation.MainNavItem
|
import com.owenlejeune.mydex.ui.navigation.MainNavItem
|
||||||
import com.owenlejeune.mydex.utils.AppCache
|
import com.owenlejeune.mydex.utils.AppCache
|
||||||
import com.owenlejeune.mydex.utils.ColorUtils
|
import com.owenlejeune.mydex.utils.ColorUtils
|
||||||
|
import com.owenlejeune.mydex.utils.PokeUtils
|
||||||
import org.koin.java.KoinJavaComponent.get
|
import org.koin.java.KoinJavaComponent.get
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -167,8 +168,6 @@ fun PokedexCard(
|
|||||||
bgColor.value = ColorUtils.pokeColorToComposeColor(color = species.color.name)
|
bgColor.value = ColorUtils.pokeColorToComposeColor(color = species.color.name)
|
||||||
|
|
||||||
val name = species.names.getNameForLanguage() ?: species.name
|
val name = species.names.getNameForLanguage() ?: species.name
|
||||||
val dexNumber = pokemon.value!!.id.toString().padStart(3, '0')
|
|
||||||
|
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.TopStart),
|
.align(Alignment.TopStart),
|
||||||
@@ -199,13 +198,13 @@ fun PokedexCard(
|
|||||||
Text(
|
Text(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.TopEnd),
|
.align(Alignment.TopEnd),
|
||||||
text = "#${dexNumber}",
|
text = PokeUtils.idToDexNumber(pokemonId),
|
||||||
style = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.Bold),
|
style = MaterialTheme.typography.bodyLarge.copy(fontWeight = FontWeight.Bold),
|
||||||
color = Color.Unspecified.copy(alpha = 0.3f)
|
color = Color.Unspecified.copy(alpha = 0.3f)
|
||||||
)
|
)
|
||||||
|
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
model = pokemon.value?.sprites?.frontDefault,
|
model = PokeUtils.spriteFromId(pokemonId),
|
||||||
contentDescription = name,
|
contentDescription = name,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.BottomEnd)
|
.align(Alignment.BottomEnd)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package com.owenlejeune.mydex.utils
|
|||||||
import android.util.SparseArray
|
import android.util.SparseArray
|
||||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.Pokemon
|
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.Pokemon
|
||||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonSpecies
|
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonSpecies
|
||||||
|
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonStat
|
||||||
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonType
|
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonType
|
||||||
|
|
||||||
object AppCache {
|
object AppCache {
|
||||||
@@ -10,5 +11,6 @@ object AppCache {
|
|||||||
var cachedSpecies = SparseArray<PokemonSpecies>()
|
var cachedSpecies = SparseArray<PokemonSpecies>()
|
||||||
var cachedPokemon = SparseArray<Pokemon>()
|
var cachedPokemon = SparseArray<Pokemon>()
|
||||||
var cachedTypes = SparseArray<PokemonType>()
|
var cachedTypes = SparseArray<PokemonType>()
|
||||||
|
var cachedStats = SparseArray<PokemonStat>()
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ object ColorUtils {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun pokeColorToComposeColor(color: String): Color {
|
fun pokeColorToComposeColor(color: String): Color {
|
||||||
return when (color) {
|
return when (color.lowercase()) {
|
||||||
"green" -> PokeGreen
|
"green" -> PokeGreen
|
||||||
"red" -> PokeRed
|
"red" -> PokeRed
|
||||||
"blue" -> PokeBlue
|
"blue" -> PokeBlue
|
||||||
@@ -24,4 +24,29 @@ object ColorUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun pokeTypeNameToComposeColor(type: String): Color {
|
||||||
|
return when (type.lowercase()) {
|
||||||
|
"bug" -> BugType
|
||||||
|
"dark" -> DarkType
|
||||||
|
"dragon" -> DragonType
|
||||||
|
"electric" -> ElectricType
|
||||||
|
"fairy" -> FairyType
|
||||||
|
"fighting" -> FightingType
|
||||||
|
"fire" -> FireType
|
||||||
|
"flying" -> FlyingType
|
||||||
|
"ghost" -> GhostType
|
||||||
|
"grass" -> GrassType
|
||||||
|
"ground" -> GroundType
|
||||||
|
"ice" -> IceType
|
||||||
|
"normal" -> NormalType
|
||||||
|
"poison" -> PoisonType
|
||||||
|
"psychic" -> PsychicType
|
||||||
|
"rock" -> RockType
|
||||||
|
"shadow" -> ShadowType
|
||||||
|
"steel" -> SteelType
|
||||||
|
"water" -> WaterType
|
||||||
|
else -> UnknownType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
95
app/src/main/java/com/owenlejeune/mydex/utils/DataManager.kt
Normal file
95
app/src/main/java/com/owenlejeune/mydex/utils/DataManager.kt
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
package com.owenlejeune.mydex.utils
|
||||||
|
|
||||||
|
import com.owenlejeune.mydex.api.pokeapi.v2.PokemonService
|
||||||
|
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.Pokemon
|
||||||
|
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonSpecies
|
||||||
|
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonStat
|
||||||
|
import com.owenlejeune.mydex.api.pokeapi.v2.model.pokemon.PokemonType
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.koin.core.component.KoinComponent
|
||||||
|
import org.koin.core.component.inject
|
||||||
|
import retrofit2.Response
|
||||||
|
|
||||||
|
object DataManager: KoinComponent {
|
||||||
|
|
||||||
|
private val service: PokemonService by inject()
|
||||||
|
|
||||||
|
fun getPokemonById(id: Int, callback: (Pokemon) -> Unit) {
|
||||||
|
AppCache.cachedPokemon[id]?.let(callback) ?: run {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
service.getPokemon(id).apply {
|
||||||
|
if (isSuccessful) {
|
||||||
|
body()?.let {
|
||||||
|
AppCache.cachedPokemon.put(it.id, it)
|
||||||
|
callback(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getPokemonSpeciesById(id: Int, callback: (PokemonSpecies) -> Unit) {
|
||||||
|
AppCache.cachedSpecies[id]?.let(callback) ?: run {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
service.getPokemonSpecies(id).apply {
|
||||||
|
if (isSuccessful) {
|
||||||
|
body()?.let {
|
||||||
|
AppCache.cachedSpecies.put(it.id, it)
|
||||||
|
callback(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getTypeById(id: Int, callback: (PokemonType) -> Unit) {
|
||||||
|
getById(
|
||||||
|
id = id,
|
||||||
|
callback = callback,
|
||||||
|
retriever = { AppCache.cachedTypes[it] },
|
||||||
|
fetcher = { service.getPokemonType(it) },
|
||||||
|
storer = { AppCache.cachedTypes.put(it.id, it) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getStatById(id: Int, callback: (PokemonStat) -> Unit) {
|
||||||
|
AppCache.cachedStats[id]?.let(callback) ?: run {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
service.getPokemonStat(id).apply {
|
||||||
|
if (isSuccessful) {
|
||||||
|
body()?.let {
|
||||||
|
AppCache.cachedStats.put(it.id, it)
|
||||||
|
callback(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun <T> getById(
|
||||||
|
id: Int,
|
||||||
|
callback: (T) -> Unit,
|
||||||
|
retriever: (Int) -> T?,
|
||||||
|
fetcher: suspend (Int) -> Response<T>,
|
||||||
|
storer: (T) -> Unit
|
||||||
|
) {
|
||||||
|
retriever(id)?.let(callback) ?: run {
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
fetcher(id).apply {
|
||||||
|
if (isSuccessful) {
|
||||||
|
body()?.let {
|
||||||
|
storer(it)
|
||||||
|
callback(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
49
app/src/main/java/com/owenlejeune/mydex/utils/PokeUtils.kt
Normal file
49
app/src/main/java/com/owenlejeune/mydex/utils/PokeUtils.kt
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package com.owenlejeune.mydex.utils
|
||||||
|
|
||||||
|
import kotlin.math.ceil
|
||||||
|
import kotlin.math.floor
|
||||||
|
|
||||||
|
object PokeUtils {
|
||||||
|
|
||||||
|
private val HEC_TO_LBS = 0.22f
|
||||||
|
private val HEC_TO_KG = 0.1f
|
||||||
|
private val DEC_TO_CM = 10
|
||||||
|
private val CM_TO_IN = 2.54
|
||||||
|
private val IN_TO_FT = 12
|
||||||
|
|
||||||
|
fun idToDexNumber(id: Int, includeNumberSign: Boolean = true): String {
|
||||||
|
val padded = id.toString().padStart(3, '0')
|
||||||
|
return if (includeNumberSign) {
|
||||||
|
"#$padded"
|
||||||
|
} else {
|
||||||
|
padded
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun spriteFromId(id: Int): String {
|
||||||
|
val paddedNumber = idToDexNumber(id, false)
|
||||||
|
return "https://assets.pokemon.com/assets/cms2/img/pokedex/full/${paddedNumber}.png"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun weightInPounds(weight: Int): Float {
|
||||||
|
return weight * HEC_TO_LBS
|
||||||
|
}
|
||||||
|
|
||||||
|
fun weightInKg(weight: Int): Float {
|
||||||
|
return weight * HEC_TO_KG
|
||||||
|
}
|
||||||
|
|
||||||
|
fun heightToCm(height: Int): Int {
|
||||||
|
return height * DEC_TO_CM
|
||||||
|
}
|
||||||
|
|
||||||
|
fun heightToFtIn(height: Int): Pair<Int, Int> {
|
||||||
|
val heightCm = height * DEC_TO_CM
|
||||||
|
|
||||||
|
val feet = floor((height / CM_TO_IN) / IN_TO_FT).toInt()
|
||||||
|
val inches = ceil((height / CM_TO_IN) - (feet * IN_TO_FT)).toInt()
|
||||||
|
|
||||||
|
return Pair(feet, inches)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -12,4 +12,14 @@
|
|||||||
<string name="items_nav_title">Items</string>
|
<string name="items_nav_title">Items</string>
|
||||||
<string name="locations_nav_title">Locations</string>
|
<string name="locations_nav_title">Locations</string>
|
||||||
<string name="type_charts_nav_title">Type charts</string>
|
<string name="type_charts_nav_title">Type charts</string>
|
||||||
|
<string name="about_tab_title">About</string>
|
||||||
|
<string name="base_stats_tab_title">Base Stats</string>
|
||||||
|
<string name="evolution_tab_title">Evolution</string>
|
||||||
|
<string name="moves_tab_title">Moves</string>
|
||||||
|
<string name="settings_nav_title">Settings</string>
|
||||||
|
<string name="type_weaknesses_title">Type weaknesses</string>
|
||||||
|
<string name="type_weaknesses_description">Pokémon of these types deal double damage to this Pokémon</string>
|
||||||
|
<string name="type_defences_title">Type defences</string>
|
||||||
|
<string name="type_defences_half_description">Pokémon of these types deal half damage to this Pokémon</string>
|
||||||
|
<string name="type_defences_no_description">Pokémon of these types deal no damage to this Pokémon</string>
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user