-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #20 from PeriodPals/feat/profile/create
Feat/profile/create
- Loading branch information
Showing
9 changed files
with
305 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
71 changes: 71 additions & 0 deletions
71
app/src/androidTest/java/com/android/periodpals/ui/profile/CreateProfileTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package com.android.periodpals.ui.profile | ||
|
||
import androidx.compose.ui.test.assertIsDisplayed | ||
import androidx.compose.ui.test.junit4.createComposeRule | ||
import androidx.compose.ui.test.onNodeWithTag | ||
import androidx.compose.ui.test.performClick | ||
import androidx.compose.ui.test.performTextInput | ||
import androidx.test.ext.junit.runners.AndroidJUnit4 | ||
import junit.framework.TestCase.assertFalse | ||
import junit.framework.TestCase.assertTrue | ||
import org.junit.Rule | ||
import org.junit.Test | ||
import org.junit.runner.RunWith | ||
|
||
@RunWith(AndroidJUnit4::class) | ||
class CreateProfileTest { | ||
|
||
@get:Rule val composeTestRule = createComposeRule() | ||
|
||
@Test | ||
fun testProfileImageDisplayed() { | ||
composeTestRule.setContent { CreateProfile() } | ||
|
||
// Check if the profile image is displayed | ||
composeTestRule.onNodeWithTag("profile_image").assertIsDisplayed() | ||
} | ||
|
||
@Test | ||
fun testFormFieldsDisplayed() { | ||
composeTestRule.setContent { CreateProfile() } | ||
|
||
// Check if the form fields are displayed | ||
composeTestRule.onNodeWithTag("email_field").assertIsDisplayed() | ||
composeTestRule.onNodeWithTag("dob_field").assertIsDisplayed() | ||
composeTestRule.onNodeWithTag("name_field").assertIsDisplayed() | ||
composeTestRule.onNodeWithTag("description_field").assertIsDisplayed() | ||
} | ||
|
||
@Test | ||
fun testSaveButtonClickWithValidDate() { | ||
composeTestRule.setContent { CreateProfile() } | ||
|
||
// Input valid date | ||
composeTestRule.onNodeWithTag("dob_field").performTextInput("01/01/2000") | ||
|
||
// Perform click on the save button | ||
composeTestRule.onNodeWithTag("save_button").performClick() | ||
composeTestRule.waitForIdle() | ||
|
||
assertTrue(validateDate("01/01/2000")) | ||
assertTrue(validateDate("31/12/1999")) | ||
} | ||
|
||
@Test | ||
fun testSaveButtonClickWithInvalidDate() { | ||
composeTestRule.setContent { CreateProfile() } | ||
|
||
// Input invalid date | ||
composeTestRule.onNodeWithTag("dob_field").performTextInput("invalid_date") | ||
|
||
// Perform click on the save button | ||
composeTestRule.onNodeWithTag("save_button").performClick() | ||
composeTestRule.waitForIdle() | ||
|
||
assertFalse(validateDate("32/01/2000")) // Invalid day | ||
assertFalse(validateDate("01/13/2000")) // Invalid month | ||
assertFalse(validateDate("01/01/abcd")) // Invalid year | ||
assertFalse(validateDate("01-01-2000")) // Invalid format | ||
assertFalse(validateDate("01/01")) // Incomplete date | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
198 changes: 198 additions & 0 deletions
198
app/src/main/java/com/android/periodpals/ui/profile/CreateProfile.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
package com.android.periodpals.ui.profile | ||
|
||
import android.app.Activity | ||
import android.content.Intent | ||
import android.icu.util.GregorianCalendar | ||
import android.net.Uri | ||
import android.widget.Toast | ||
import androidx.activity.compose.rememberLauncherForActivityResult | ||
import androidx.activity.result.contract.ActivityResultContracts | ||
import androidx.compose.foundation.background | ||
import androidx.compose.foundation.clickable | ||
import androidx.compose.foundation.layout.Arrangement | ||
import androidx.compose.foundation.layout.Box | ||
import androidx.compose.foundation.layout.Column | ||
import androidx.compose.foundation.layout.fillMaxSize | ||
import androidx.compose.foundation.layout.fillMaxWidth | ||
import androidx.compose.foundation.layout.height | ||
import androidx.compose.foundation.layout.padding | ||
import androidx.compose.foundation.layout.size | ||
import androidx.compose.foundation.layout.width | ||
import androidx.compose.foundation.shape.CircleShape | ||
import androidx.compose.foundation.shape.RoundedCornerShape | ||
import androidx.compose.material3.Button | ||
import androidx.compose.material3.ButtonDefaults | ||
import androidx.compose.material3.MaterialTheme | ||
import androidx.compose.material3.OutlinedTextField | ||
import androidx.compose.material3.Scaffold | ||
import androidx.compose.material3.Text | ||
import androidx.compose.runtime.Composable | ||
import androidx.compose.runtime.getValue | ||
import androidx.compose.runtime.mutableStateOf | ||
import androidx.compose.runtime.remember | ||
import androidx.compose.runtime.setValue | ||
import androidx.compose.ui.Alignment | ||
import androidx.compose.ui.Modifier | ||
import androidx.compose.ui.draw.clip | ||
import androidx.compose.ui.graphics.Color | ||
import androidx.compose.ui.layout.ContentScale | ||
import androidx.compose.ui.platform.LocalContext | ||
import androidx.compose.ui.platform.testTag | ||
import androidx.compose.ui.text.TextStyle | ||
import androidx.compose.ui.text.font.FontWeight | ||
import androidx.compose.ui.unit.dp | ||
import androidx.compose.ui.unit.sp | ||
import com.android.periodpals.R | ||
import com.bumptech.glide.integration.compose.ExperimentalGlideComposeApi | ||
import com.bumptech.glide.integration.compose.GlideImage | ||
|
||
@OptIn(ExperimentalGlideComposeApi::class) | ||
@Composable | ||
fun CreateProfile() { | ||
var name by remember { mutableStateOf("") } | ||
var email by remember { mutableStateOf("") } | ||
var age by remember { mutableStateOf("") } | ||
var description by remember { mutableStateOf("") } | ||
|
||
var profileImageUri by remember { | ||
mutableStateOf<Uri?>( | ||
Uri.parse("android.resource://com.android.periodpals/" + R.drawable.generic_avatar)) | ||
} | ||
var context = LocalContext.current | ||
|
||
val launcher = | ||
rememberLauncherForActivityResult( | ||
contract = ActivityResultContracts.StartActivityForResult()) { result -> | ||
if (result.resultCode == Activity.RESULT_OK) { | ||
profileImageUri = result.data?.data | ||
} | ||
} | ||
|
||
Scaffold( | ||
modifier = Modifier.fillMaxSize(), | ||
content = { padding -> | ||
Column( | ||
modifier = Modifier.fillMaxSize().padding(16.dp).padding(padding), | ||
verticalArrangement = Arrangement.spacedBy(16.dp), | ||
horizontalAlignment = Alignment.CenterHorizontally, | ||
) { | ||
Box( | ||
modifier = | ||
Modifier.size(124.dp) | ||
.clip(shape = RoundedCornerShape(100.dp)) | ||
.background( | ||
color = MaterialTheme.colorScheme.background, | ||
shape = RoundedCornerShape(100.dp)) | ||
.testTag("profile_image") | ||
.clickable { | ||
val pickImageIntent = Intent(Intent.ACTION_PICK).apply { type = "image/*" } | ||
launcher.launch(pickImageIntent) | ||
}) { | ||
GlideImage( | ||
model = profileImageUri, | ||
contentDescription = "profile picture", | ||
contentScale = ContentScale.Crop, | ||
modifier = | ||
Modifier.size(124.dp) | ||
.background( | ||
color = MaterialTheme.colorScheme.background, shape = CircleShape)) | ||
} | ||
|
||
Box(modifier = Modifier.fillMaxWidth()) { | ||
Text( | ||
text = "Mandatory", | ||
style = | ||
TextStyle( | ||
fontSize = 20.sp, | ||
lineHeight = 20.sp, | ||
fontWeight = FontWeight(500), | ||
letterSpacing = 0.2.sp, | ||
), | ||
) | ||
} | ||
|
||
OutlinedTextField( | ||
value = email, | ||
onValueChange = { email = it }, | ||
label = { Text("Email") }, | ||
placeholder = { Text("Enter your email") }, | ||
modifier = Modifier.testTag("email_field")) | ||
|
||
OutlinedTextField( | ||
value = age, | ||
onValueChange = { age = it }, | ||
label = { Text("Date of Birth") }, | ||
placeholder = { Text("DD/MM/YYYY") }, | ||
modifier = Modifier.testTag("dob_field")) | ||
|
||
Box(modifier = Modifier.fillMaxWidth()) { | ||
Text( | ||
text = "Your profile", | ||
style = | ||
TextStyle( | ||
fontSize = 20.sp, | ||
lineHeight = 20.sp, | ||
fontWeight = FontWeight(500), | ||
letterSpacing = 0.2.sp, | ||
), | ||
) | ||
} | ||
|
||
OutlinedTextField( | ||
value = name, | ||
onValueChange = { name = it }, | ||
label = { Text("Displayed Name") }, | ||
placeholder = { Text("Enter your name") }, | ||
modifier = Modifier.testTag("name_field"), | ||
) | ||
|
||
OutlinedTextField( | ||
value = description, | ||
onValueChange = { description = it }, | ||
label = { Text("Description") }, | ||
placeholder = { Text("Enter a description") }, | ||
modifier = Modifier.height(124.dp).testTag("description_field"), | ||
) | ||
|
||
Button( | ||
onClick = { | ||
if (validateDate(age)) { | ||
// Save the profile (future implementation) | ||
Toast.makeText(context, "Profile saved", Toast.LENGTH_SHORT).show() | ||
} else { | ||
Toast.makeText(context, "Invalid date", Toast.LENGTH_SHORT).show() | ||
} | ||
}, | ||
enabled = true, | ||
modifier = | ||
Modifier.padding(0.dp) | ||
.width(84.dp) | ||
.height(40.dp) | ||
.testTag("save_button") | ||
.background( | ||
color = Color(0xFF65558F), shape = RoundedCornerShape(size = 100.dp)), | ||
colors = ButtonDefaults.buttonColors(Color(0xFF65558F))) { | ||
Text( | ||
"Save", | ||
color = Color.White, | ||
) | ||
} | ||
} | ||
}) | ||
} | ||
|
||
fun validateDate(date: String): Boolean { | ||
val parts = date.split("/") | ||
val calendar = GregorianCalendar.getInstance() | ||
calendar.isLenient = false | ||
if (parts.size == 3) { | ||
return try { | ||
calendar.set(parts[2].toInt(), parts[1].toInt() - 1, parts[0].toInt()) | ||
calendar.time | ||
true | ||
} catch (e: Exception) { | ||
false | ||
} | ||
} | ||
return false | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.