Skip to content

Commit bad17b3

Browse files
committed
Add Spanish language support
2 parents 340b23a + 433280f commit bad17b3

File tree

24 files changed

+388
-48
lines changed

24 files changed

+388
-48
lines changed

app/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,9 @@ android {
6060
jvmTarget = '1.8'
6161
freeCompilerArgs += ["-opt-in=kotlin.RequiresOptIn"]
6262
}
63+
androidResources {
64+
generateLocaleConfig true
65+
}
6366
namespace 'com.sample.tmdb'
6467
}
6568

app/src/main/java/com/sample/tmdb/ui/MainActivity.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
package com.sample.tmdb.ui
22

33
import android.os.Bundle
4-
import androidx.activity.ComponentActivity
54
import androidx.activity.SystemBarStyle
65
import androidx.activity.compose.setContent
76
import androidx.activity.enableEdgeToEdge
7+
import androidx.appcompat.app.AppCompatActivity
88
import androidx.compose.foundation.isSystemInDarkTheme
99
import androidx.compose.material.MaterialTheme
1010
import androidx.compose.runtime.Composable
@@ -17,7 +17,8 @@ import com.sample.tmdb.common.ui.theme.TmdbPagingComposeTheme
1717
import dagger.hilt.android.AndroidEntryPoint
1818

1919
@AndroidEntryPoint
20-
class MainActivity : ComponentActivity() {
20+
class MainActivity : AppCompatActivity() {
21+
2122
override fun onCreate(savedInstanceState: Bundle?) {
2223
super.onCreate(savedInstanceState)
2324

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
unqualifiedResLocale=en-GB
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<resources>
3+
<string name="cast">Actores</string>
4+
<string name="crew">Equipo</string>
5+
<string name="favorite">Favorito</string>
6+
<string name="movie">Película</string>
7+
<string name="tv_show">Serie</string>
8+
<string name="setting">Configuración</string>
9+
</resources>

app/src/main/res/values/strings.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<resources>
2-
<string name="app_name">TMDb</string>
2+
<string name="app_name" translatable="false">TMDb</string>
33
<string name="cast">Cast</string>
44
<string name="crew">Crew</string>
55
<string name="favorite">Favorite</string>

app/src/main/res/values/themes.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<resources>
33

4-
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight">
4+
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
55
<item name="android:statusBarColor">@color/purple_700</item>
66
</style>
77
</resources>

buildSrc/src/main/java/Dependencies.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ object Deps {
7171
// Activity Compose
7272
const val composeActivity = "androidx.activity:activity-compose:${Versions.compose_activity}"
7373

74+
// Appcompat
75+
const val appcompat = "androidx.appcompat:appcompat:${Versions.appCompat}"
76+
7477
// UI
7578
const val material = "com.google.android.material:material:${Versions.material}"
7679
const val palette = "androidx.palette:palette-ktx:${Versions.palette}"
Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
package com.sample.tmdb.common.ui.component
2+
3+
import androidx.compose.animation.core.animateFloatAsState
4+
import androidx.compose.foundation.background
5+
import androidx.compose.foundation.border
6+
import androidx.compose.foundation.clickable
7+
import androidx.compose.foundation.layout.Box
8+
import androidx.compose.foundation.layout.Column
9+
import androidx.compose.foundation.layout.IntrinsicSize
10+
import androidx.compose.foundation.layout.fillMaxWidth
11+
import androidx.compose.foundation.layout.padding
12+
import androidx.compose.foundation.layout.width
13+
import androidx.compose.foundation.shape.ZeroCornerSize
14+
import androidx.compose.material.ContentAlpha
15+
import androidx.compose.material.DropdownMenu
16+
import androidx.compose.material.DropdownMenuItem
17+
import androidx.compose.material.Icon
18+
import androidx.compose.material.MaterialTheme
19+
import androidx.compose.material.ProvideTextStyle
20+
import androidx.compose.material.Text
21+
import androidx.compose.material.TextFieldDefaults
22+
import androidx.compose.material.icons.Icons
23+
import androidx.compose.material.icons.filled.ArrowDropDown
24+
import androidx.compose.runtime.Composable
25+
import androidx.compose.runtime.getValue
26+
import androidx.compose.runtime.mutableStateOf
27+
import androidx.compose.runtime.remember
28+
import androidx.compose.runtime.rememberCoroutineScope
29+
import androidx.compose.runtime.setValue
30+
import androidx.compose.ui.Alignment
31+
import androidx.compose.ui.Modifier
32+
import androidx.compose.ui.draw.clip
33+
import androidx.compose.ui.draw.drawBehind
34+
import androidx.compose.ui.draw.rotate
35+
import androidx.compose.ui.geometry.Offset
36+
import androidx.compose.ui.geometry.Size
37+
import androidx.compose.ui.graphics.Color
38+
import androidx.compose.ui.graphics.Shape
39+
import androidx.compose.ui.layout.onGloballyPositioned
40+
import androidx.compose.ui.platform.LocalDensity
41+
import androidx.compose.ui.platform.LocalFocusManager
42+
import androidx.compose.ui.unit.Dp
43+
import androidx.compose.ui.unit.dp
44+
import androidx.compose.ui.unit.toSize
45+
import kotlinx.coroutines.delay
46+
import kotlinx.coroutines.launch
47+
48+
// ExposedDropDownMenu will be added in Jetpack Compose 1.1.0.
49+
// This is a reimplementation while waiting.
50+
// See https://stackoverflow.com/questions/67111020/exposed-drop-down-menu-for-jetpack-compose/6904285
51+
52+
@Composable
53+
fun SimpleExposedDropDownMenu(
54+
values: List<String>,
55+
selectedIndex: Int,
56+
onChange: (Int) -> Unit,
57+
label: @Composable () -> Unit,
58+
modifier: Modifier = Modifier,
59+
backgroundColor: Color = MaterialTheme.colors.onSurface.copy(alpha = TextFieldDefaults.BackgroundOpacity),
60+
shape: Shape = MaterialTheme.shapes.small.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize),
61+
) {
62+
SimpleExposedDropDownMenuImpl(
63+
values = values,
64+
selectedIndex = selectedIndex,
65+
onChange = onChange,
66+
label = label,
67+
modifier = modifier,
68+
backgroundColor = backgroundColor,
69+
shape = shape,
70+
decorator = { color, width, content ->
71+
Box(
72+
Modifier
73+
.drawBehind {
74+
val strokeWidth = width.value * density
75+
val y = size.height - strokeWidth / 2
76+
drawLine(
77+
color,
78+
Offset(0f, y),
79+
Offset(size.width, y),
80+
strokeWidth,
81+
)
82+
},
83+
) {
84+
content()
85+
}
86+
},
87+
)
88+
}
89+
90+
@Composable
91+
fun SimpleOutlinedExposedDropDownMenu(
92+
values: List<String>,
93+
selectedIndex: Int,
94+
onChange: (Int) -> Unit,
95+
label: @Composable () -> Unit,
96+
modifier: Modifier = Modifier,
97+
backgroundColor: Color = MaterialTheme.colors.onSurface.copy(alpha = TextFieldDefaults.BackgroundOpacity),
98+
shape: Shape = MaterialTheme.shapes.small,
99+
) {
100+
SimpleExposedDropDownMenuImpl(
101+
values = values,
102+
selectedIndex = selectedIndex,
103+
onChange = onChange,
104+
label = label,
105+
modifier = modifier,
106+
backgroundColor = backgroundColor,
107+
shape = shape,
108+
decorator = { color, width, content ->
109+
Box(
110+
Modifier
111+
.border(width, color, shape),
112+
) {
113+
content()
114+
}
115+
},
116+
)
117+
}
118+
119+
@Composable
120+
private fun SimpleExposedDropDownMenuImpl(
121+
values: List<String>,
122+
selectedIndex: Int,
123+
onChange: (Int) -> Unit,
124+
label: @Composable () -> Unit,
125+
modifier: Modifier,
126+
backgroundColor: Color,
127+
shape: Shape,
128+
decorator: @Composable (Color, Dp, @Composable () -> Unit) -> Unit,
129+
) {
130+
var expanded by remember { mutableStateOf(false) }
131+
var textfieldSize by remember { mutableStateOf(Size.Zero) }
132+
133+
val indicatorColor =
134+
if (expanded) {
135+
MaterialTheme.colors.primary.copy(alpha = ContentAlpha.high)
136+
} else {
137+
MaterialTheme.colors.onSurface.copy(alpha = TextFieldDefaults.UnfocusedIndicatorLineOpacity)
138+
}
139+
val indicatorWidth = (if (expanded) 2 else 1).dp
140+
val labelColor =
141+
if (expanded) {
142+
MaterialTheme.colors.primary.copy(alpha = ContentAlpha.high)
143+
} else {
144+
MaterialTheme.colors.onSurface.copy(ContentAlpha.medium)
145+
}
146+
val trailingIconColor = MaterialTheme.colors.onSurface.copy(alpha = TextFieldDefaults.IconOpacity)
147+
148+
val rotation: Float by animateFloatAsState(if (expanded) 180f else 0f)
149+
150+
val focusManager = LocalFocusManager.current
151+
152+
Column(modifier = modifier.width(IntrinsicSize.Max)) {
153+
decorator(indicatorColor, indicatorWidth) {
154+
Box(
155+
Modifier
156+
.fillMaxWidth()
157+
.background(color = backgroundColor, shape = shape)
158+
.onGloballyPositioned { textfieldSize = it.size.toSize() }
159+
.clip(shape)
160+
.clickable {
161+
expanded = !expanded
162+
focusManager.clearFocus()
163+
}
164+
.padding(start = 16.dp, end = 12.dp, top = 7.dp, bottom = 10.dp),
165+
) {
166+
Column(Modifier.padding(end = 32.dp)) {
167+
ProvideTextStyle(value = MaterialTheme.typography.caption.copy(color = labelColor)) {
168+
label()
169+
}
170+
Text(
171+
text = values[selectedIndex],
172+
modifier = Modifier.padding(top = 1.dp),
173+
)
174+
}
175+
Icon(
176+
imageVector = Icons.Filled.ArrowDropDown,
177+
contentDescription = "Change",
178+
tint = trailingIconColor,
179+
modifier = Modifier
180+
.align(Alignment.CenterEnd)
181+
.padding(top = 4.dp)
182+
.rotate(rotation),
183+
)
184+
}
185+
}
186+
187+
DropdownMenu(
188+
expanded = expanded,
189+
onDismissRequest = { expanded = false },
190+
modifier = Modifier
191+
.width(with(LocalDensity.current) { textfieldSize.width.toDp() }),
192+
) {
193+
values.forEachIndexed { i, v ->
194+
val scope = rememberCoroutineScope()
195+
DropdownMenuItem(
196+
onClick = {
197+
onChange(i)
198+
scope.launch {
199+
delay(150)
200+
expanded = false
201+
}
202+
},
203+
) {
204+
Text(v)
205+
}
206+
}
207+
}
208+
}
209+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<resources>
3+
<string name="movies">Películas</string>
4+
<string name="tv_series">Series</string>
5+
<string name="failed_loading_msg">Algo fue mal...</string>
6+
<string name="retry">Reintentar</string>
7+
<string name="search">Buscar</string>
8+
<string name="back">Volver</string>
9+
<string name="person_content_description">Foto %1$s de %2$s</string>
10+
<string name="poster_content_description">Póster</string>
11+
</resources>

core/data/src/main/java/com/sample/tmdb/data/utils/NetworkUtils.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.sample.tmdb.data.utils
22

3+
import androidx.compose.ui.text.intl.Locale
34
import com.sample.tmdb.data.BuildConfig
45
import com.squareup.moshi.Moshi
56
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
@@ -22,11 +23,10 @@ private fun httpClient(): OkHttpClient {
2223
val original = chain.request()
2324
val originalHttpUrl = original.url
2425

25-
val url =
26-
originalHttpUrl
27-
.newBuilder()
28-
.addQueryParameter("api_key", BuildConfig.TMDB_API_KEY)
29-
.build()
26+
val url = originalHttpUrl.newBuilder()
27+
.addQueryParameter("api_key", BuildConfig.TMDB_API_KEY)
28+
.addQueryParameter("language", Locale.current.toLanguageTag())
29+
.build()
3030

3131
val request = original.newBuilder().url(url).build()
3232
chain.proceed(request)

0 commit comments

Comments
 (0)