Skip to content

Commit 1cd10bc

Browse files
authored
Merge pull request #42 from gravatar/hamorillo/26-demo-app-first-version
DemoApp - First interation
2 parents 891395c + 3dfc24f commit 1cd10bc

File tree

7 files changed

+264
-32
lines changed

7 files changed

+264
-32
lines changed

app/build.gradle.kts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ dependencies {
6363
implementation("androidx.compose.ui:ui-graphics")
6464
implementation("androidx.compose.ui:ui-tooling-preview")
6565
implementation("androidx.compose.material3:material3")
66+
implementation(project(":gravatar"))
67+
implementation("io.coil-kt:coil-compose:2.5.0")
68+
69+
debugImplementation("androidx.compose.ui:ui-tooling:1.6.0")
6670

6771
// Unit Test dependencies
6872
testImplementation("junit:junit:4.13.2")

app/src/main/AndroidManifest.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
33
xmlns:tools="http://schemas.android.com/tools">
44

5+
<uses-permission android:name="android.permission.INTERNET"/>
6+
57
<application
68
android:allowBackup="true"
79
android:dataExtractionRules="@xml/data_extraction_rules"

app/src/main/java/com/gravatar/demoapp/MainActivity.kt

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -3,43 +3,13 @@ package com.gravatar.demoapp
33
import android.os.Bundle
44
import androidx.activity.ComponentActivity
55
import androidx.activity.compose.setContent
6-
import androidx.compose.foundation.layout.fillMaxSize
7-
import androidx.compose.material3.MaterialTheme
8-
import androidx.compose.material3.Surface
9-
import androidx.compose.material3.Text
10-
import androidx.compose.runtime.Composable
11-
import androidx.compose.ui.Modifier
12-
import androidx.compose.ui.tooling.preview.Preview
13-
import com.gravatar.demoapp.theme.GravatarDemoAppTheme
6+
import com.gravatar.demoapp.ui.DemoGravatarApp
147

158
class MainActivity : ComponentActivity() {
169
override fun onCreate(savedInstanceState: Bundle?) {
1710
super.onCreate(savedInstanceState)
1811
setContent {
19-
GravatarDemoAppTheme {
20-
Surface(
21-
modifier = Modifier.fillMaxSize(),
22-
color = MaterialTheme.colorScheme.background,
23-
) {
24-
Greeting("Android")
25-
}
26-
}
12+
DemoGravatarApp()
2713
}
2814
}
2915
}
30-
31-
@Composable
32-
fun Greeting(name: String, modifier: Modifier = Modifier) {
33-
Text(
34-
text = "Hello $name!",
35-
modifier = modifier,
36-
)
37-
}
38-
39-
@Preview(showBackground = true)
40-
@Composable
41-
fun GreetingPreview() {
42-
GravatarDemoAppTheme {
43-
Greeting("Android")
44-
}
45-
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package com.gravatar.demoapp.ui
2+
3+
import androidx.compose.foundation.layout.Row
4+
import androidx.compose.foundation.layout.fillMaxWidth
5+
import androidx.compose.material3.Checkbox
6+
import androidx.compose.material3.DropdownMenuItem
7+
import androidx.compose.material3.ExperimentalMaterial3Api
8+
import androidx.compose.material3.ExposedDropdownMenuBox
9+
import androidx.compose.material3.ExposedDropdownMenuDefaults
10+
import androidx.compose.material3.Text
11+
import androidx.compose.material3.TextField
12+
import androidx.compose.runtime.Composable
13+
import androidx.compose.runtime.getValue
14+
import androidx.compose.runtime.mutableStateOf
15+
import androidx.compose.runtime.remember
16+
import androidx.compose.runtime.setValue
17+
import androidx.compose.ui.Modifier
18+
import androidx.compose.ui.res.stringResource
19+
import com.gravatar.DefaultAvatarImage
20+
import com.gravatar.R
21+
22+
@OptIn(ExperimentalMaterial3Api::class)
23+
@Composable
24+
fun DefaultAvatarImageDropdown(
25+
enabled: Boolean,
26+
onEnabledChanged: (Boolean) -> Unit,
27+
selectedOption: DefaultAvatarImage,
28+
onSelectedOptionChange: (DefaultAvatarImage) -> Unit,
29+
defaultAvatarOptions: List<DefaultAvatarImage>,
30+
modifier: Modifier = Modifier,
31+
) {
32+
var expanded by remember { mutableStateOf(false) }
33+
Row(modifier = modifier) {
34+
Checkbox(
35+
checked = enabled,
36+
onCheckedChange = {
37+
if (enabled) {
38+
expanded = false
39+
}
40+
onEnabledChanged(!enabled)
41+
},
42+
)
43+
ExposedDropdownMenuBox(
44+
expanded = expanded,
45+
onExpandedChange = { expanded = !expanded },
46+
modifier = Modifier.fillMaxWidth(),
47+
) {
48+
TextField(
49+
readOnly = true,
50+
value = selectedOption.style,
51+
onValueChange = { },
52+
label = { Text(stringResource(R.string.default_avatar_image_label)) },
53+
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = expanded) },
54+
modifier = Modifier
55+
.fillMaxWidth()
56+
.menuAnchor(),
57+
)
58+
ExposedDropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
59+
defaultAvatarOptions.forEach { selectionOption ->
60+
DropdownMenuItem(text = { Text(text = selectionOption.style) }, onClick = {
61+
onSelectedOptionChange.invoke(selectionOption)
62+
expanded = false
63+
})
64+
}
65+
}
66+
}
67+
}
68+
}
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package com.gravatar.demoapp.ui
2+
3+
import androidx.compose.foundation.background
4+
import androidx.compose.foundation.layout.Column
5+
import androidx.compose.foundation.layout.fillMaxSize
6+
import androidx.compose.foundation.layout.padding
7+
import androidx.compose.material3.Divider
8+
import androidx.compose.material3.MaterialTheme
9+
import androidx.compose.material3.Surface
10+
import androidx.compose.material3.Text
11+
import androidx.compose.material3.TextField
12+
import androidx.compose.runtime.Composable
13+
import androidx.compose.runtime.getValue
14+
import androidx.compose.runtime.mutableStateOf
15+
import androidx.compose.runtime.remember
16+
import androidx.compose.runtime.setValue
17+
import androidx.compose.ui.Alignment
18+
import androidx.compose.ui.Modifier
19+
import androidx.compose.ui.res.stringResource
20+
import androidx.compose.ui.text.font.FontWeight
21+
import androidx.compose.ui.unit.dp
22+
import coil.compose.AsyncImage
23+
import com.gravatar.DefaultAvatarImage
24+
import com.gravatar.R
25+
import com.gravatar.demoapp.theme.GravatarDemoAppTheme
26+
import com.gravatar.emailAddressToGravatarUrl
27+
28+
@Composable
29+
fun DemoGravatarApp() {
30+
var email by remember { mutableStateOf("[email protected]") }
31+
var gravatarUrl by remember { mutableStateOf("") }
32+
var avatarSize by remember { mutableStateOf<Int?>(null) }
33+
var defaultAvatarImageEnabled by remember { mutableStateOf(false) }
34+
var selectedDefaultAvatar by remember { mutableStateOf(DefaultAvatarImage.MONSTER) }
35+
val defaultAvatarOptions = DefaultAvatarImage.entries
36+
37+
GravatarDemoAppTheme {
38+
Surface {
39+
Column(
40+
modifier = Modifier
41+
.fillMaxSize()
42+
.background(MaterialTheme.colorScheme.background)
43+
.padding(16.dp),
44+
horizontalAlignment = Alignment.CenterHorizontally,
45+
) {
46+
GravatarImageSettings(
47+
email = email,
48+
size = avatarSize,
49+
onEmailChanged = { email = it },
50+
onSizeChange = { avatarSize = it },
51+
onLoadGravatarClicked = {
52+
gravatarUrl = emailAddressToGravatarUrl(
53+
email = email,
54+
size = avatarSize,
55+
defaultAvatarImage = if (defaultAvatarImageEnabled) selectedDefaultAvatar else null,
56+
)
57+
},
58+
onDefaultAvatarImageEnabledChanged = {
59+
defaultAvatarImageEnabled = it
60+
},
61+
defaultAvatarImageEnabled = defaultAvatarImageEnabled,
62+
selectedDefaultAvatarImage = selectedDefaultAvatar,
63+
onDefaultAvatarImageChanged = { selectedDefaultAvatar = it },
64+
defaultAvatarOptions = defaultAvatarOptions,
65+
)
66+
67+
if (gravatarUrl.isNotEmpty()) {
68+
GravatarDivider()
69+
70+
GravatarGeneratedUrl(gravatarUrl = gravatarUrl)
71+
72+
GravatarDivider()
73+
74+
GravatarImage(gravatarUrl = gravatarUrl)
75+
}
76+
}
77+
}
78+
}
79+
}
80+
81+
@Composable
82+
fun GravatarDivider() =
83+
Divider(thickness = 1.dp, color = MaterialTheme.colorScheme.primary, modifier = Modifier.padding(vertical = 8.dp))
84+
85+
@Composable
86+
fun GravatarGeneratedUrl(gravatarUrl: String) {
87+
Text(
88+
text = stringResource(R.string.gravatar_generated_url_label),
89+
fontWeight = FontWeight.Bold,
90+
)
91+
Text(text = gravatarUrl)
92+
}
93+
94+
@Composable
95+
fun GravatarImage(gravatarUrl: String) = AsyncImage(model = gravatarUrl, contentDescription = null)
96+
97+
@Composable
98+
fun GravatarEmailInput(email: String, onValueChange: (String) -> Unit, modifier: Modifier = Modifier) {
99+
TextField(
100+
value = email,
101+
onValueChange = onValueChange,
102+
label = { Text(stringResource(R.string.gravatar_email_input_label)) },
103+
maxLines = 1,
104+
modifier = modifier,
105+
)
106+
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
package com.gravatar.demoapp.ui
2+
3+
import androidx.compose.foundation.layout.Column
4+
import androidx.compose.foundation.layout.fillMaxWidth
5+
import androidx.compose.foundation.layout.padding
6+
import androidx.compose.foundation.text.KeyboardOptions
7+
import androidx.compose.material3.Button
8+
import androidx.compose.material3.Text
9+
import androidx.compose.material3.TextField
10+
import androidx.compose.runtime.Composable
11+
import androidx.compose.ui.Alignment
12+
import androidx.compose.ui.Modifier
13+
import androidx.compose.ui.res.stringResource
14+
import androidx.compose.ui.text.input.KeyboardType
15+
import androidx.compose.ui.tooling.preview.Preview
16+
import androidx.compose.ui.unit.dp
17+
import com.gravatar.DefaultAvatarImage
18+
import com.gravatar.R
19+
import com.gravatar.demoapp.theme.GravatarDemoAppTheme
20+
21+
@Composable
22+
fun GravatarImageSettings(
23+
email: String,
24+
size: Int?,
25+
onEmailChanged: (String) -> Unit,
26+
onSizeChange: (Int?) -> Unit,
27+
onLoadGravatarClicked: () -> Unit,
28+
defaultAvatarImageEnabled: Boolean,
29+
onDefaultAvatarImageEnabledChanged: (Boolean) -> Unit,
30+
selectedDefaultAvatarImage: DefaultAvatarImage,
31+
onDefaultAvatarImageChanged: (DefaultAvatarImage) -> Unit,
32+
defaultAvatarOptions: List<DefaultAvatarImage>,
33+
) {
34+
Column(
35+
modifier = Modifier
36+
.fillMaxWidth(),
37+
horizontalAlignment = Alignment.CenterHorizontally,
38+
) {
39+
GravatarEmailInput(email = email, onValueChange = onEmailChanged, Modifier.fillMaxWidth())
40+
DefaultAvatarImageDropdown(
41+
enabled = defaultAvatarImageEnabled,
42+
selectedOption = selectedDefaultAvatarImage,
43+
onEnabledChanged = onDefaultAvatarImageEnabledChanged,
44+
onSelectedOptionChange = onDefaultAvatarImageChanged,
45+
defaultAvatarOptions = defaultAvatarOptions,
46+
modifier = Modifier
47+
.fillMaxWidth()
48+
.padding(vertical = 8.dp),
49+
)
50+
TextField(
51+
value = size?.toString() ?: "",
52+
onValueChange = { value -> onSizeChange(value.toIntOrNull()) },
53+
label = { Text(stringResource(R.string.gravatar_size_input_label)) },
54+
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
55+
modifier = Modifier.padding(vertical = 8.dp),
56+
)
57+
Button(onClick = onLoadGravatarClicked) { Text(text = "Load Gravatar") }
58+
}
59+
}
60+
61+
@Preview
62+
@Composable
63+
fun GravatarImageSettingsPreview() {
64+
GravatarDemoAppTheme {
65+
GravatarImageSettings(
66+
email = "[email protected]",
67+
size = null,
68+
onEmailChanged = {},
69+
onSizeChange = {},
70+
onLoadGravatarClicked = {},
71+
defaultAvatarImageEnabled = true,
72+
onDefaultAvatarImageEnabledChanged = {},
73+
selectedDefaultAvatarImage = DefaultAvatarImage.BLANK,
74+
onDefaultAvatarImageChanged = {},
75+
defaultAvatarOptions = DefaultAvatarImage.entries,
76+
)
77+
}
78+
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
<resources>
22
<string name="app_name">Gravatar Demo App</string>
3+
<string name="gravatar_email_input_label">Gravatar E-Mail</string>
4+
<string name="gravatar_generated_url_label">Gravatar URL</string>
5+
<string name="gravatar_size_input_label">Avatar Size</string>
6+
<string name="default_avatar_image_label">Default Avatar</string>
37
</resources>

0 commit comments

Comments
 (0)