This is a dating app
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

426 lines
16 KiB

package com.example.dattingapp.registration
import android.content.Context
import android.net.Uri
import android.os.Bundle
import android.widget.Toast
import androidx.activity.ComponentActivity
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.FileUpload
import androidx.compose.material.icons.filled.PhotoCamera
import androidx.compose.material.icons.filled.Videocam
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.FileProvider
import androidx.media3.common.MediaItem
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.ui.PlayerView
import coil.compose.rememberAsyncImagePainter
import com.example.dattingapp.ui.theme.DattingAPPTheme
import java.io.File
enum class DisplayMode {
IMAGE,
VIDEO,
BIO
}
class UploadIntroVideoActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
// Retrieve Profile Image URI string
val profileImageUriString = intent.getStringExtra("EXTRA_PROFILE_IMAGE_URI")
val profileImageUri: Uri? = profileImageUriString?.let { Uri.parse(it) }
setContent {
DattingAPPTheme {
UploadIntroVideoScreen(
profileImageUri = profileImageUri,
onBackClick = { finish() },
onSkipClick = { /* Handle skip */ },
onSubmit = { videoUri, bioText ->
// Final registration submission logic
Toast.makeText(this, "Registration Data Ready!", Toast.LENGTH_SHORT).show()
}
)
}
}
}
}
@Composable
fun UploadIntroVideoScreen(
profileImageUri: Uri?,
onBackClick: () -> Unit = {},
onSkipClick: () -> Unit = {},
onSubmit: (videoUri: Uri?, bioText: String) -> Unit
) {
val context = LocalContext.current
var displayMode by remember { mutableStateOf(DisplayMode.IMAGE) }
var recordedVideoUri by remember { mutableStateOf<Uri?>(null) }
var bioText by remember { mutableStateOf("") }
// Launchers remain the same...
val videoPickerLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.GetContent()
) { uri: Uri? ->
if (uri != null) {
recordedVideoUri = uri
displayMode = DisplayMode.VIDEO
}
}
val recordVideoLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.CaptureVideo()
) { success ->
if (success && recordedVideoUri != null) {
displayMode = DisplayMode.VIDEO
}
}
var tempCameraPhotoUri by remember { mutableStateOf<Uri?>(null) }
var capturedPhotoUri by remember { mutableStateOf<Uri?>(null) }
val takePhotoLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.TakePicture()
) { success ->
if (success && tempCameraPhotoUri != null) {
capturedPhotoUri = tempCameraPhotoUri
displayMode = DisplayMode.IMAGE
}
}
val buttonGradient = Brush.horizontalGradient(
colors = listOf(Color(0xFFB70054), Color(0xFF6700C7))
)
Column(
modifier = Modifier
.fillMaxSize()
.background(Color(0xFFF9F9F9))
.systemBarsPadding()
// 👇 ADD imePadding() HERE TO RESIZE WHEN KEYBOARD OPENS 👇
.imePadding()
.padding(horizontal = 20.dp, vertical = 12.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceBetween
) {
// --- Header Section ---
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
IconButton(onClick = onBackClick) {
Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = "Back", tint = Color.Black)
}
Text(
text = "VibeCheck",
fontSize = 22.sp,
fontWeight = FontWeight.Bold,
color = Color(0xFF9C27B0)
)
TextButton(onClick = onSkipClick) {
Text(text = "SKIP", color = Color.Gray, fontWeight = FontWeight.Bold, fontSize = 12.sp)
}
}
// --- Header Title ---
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = if (displayMode == DisplayMode.BIO) "Your Bio" else "Video Introduction",
fontSize = 22.sp,
fontWeight = FontWeight.Bold,
color = Color(0xFF1E293B)
)
Spacer(modifier = Modifier.height(4.dp))
Text(
text = if (displayMode == DisplayMode.BIO)
"Write a short intro about yourself (up to 200 words)."
else
"Say hello! Share a bit about yourself in 60 seconds.",
fontSize = 13.sp,
color = Color.Gray
)
}
// --- Central Media / Text Container ---
Box(
modifier = Modifier
.fillMaxWidth()
.weight(1f)
.padding(vertical = 12.dp)
.clip(RoundedCornerShape(20.dp))
.background(Color.LightGray),
contentAlignment = Alignment.Center
) {
when (displayMode) {
DisplayMode.IMAGE -> {
if (capturedPhotoUri != null || profileImageUri != null) {
Image(
painter = rememberAsyncImagePainter(model = capturedPhotoUri ?: profileImageUri),
contentDescription = "Profile Image Preview",
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
}
}
DisplayMode.VIDEO -> {
recordedVideoUri?.let { uri ->
VideoPlayer(videoUri = uri)
}
}
DisplayMode.BIO -> {
OutlinedTextField(
value = bioText,
onValueChange = { input ->
val words = input.trim().split("\\s+".toRegex())
if (input.isEmpty() || words.size <= 200) bioText = input
},
modifier = Modifier
.fillMaxSize()
.background(Color.White)
.padding(12.dp),
placeholder = { Text("Write something about yourself (max 200 words)...") },
colors = TextFieldDefaults.colors(
unfocusedContainerColor = Color.White,
focusedContainerColor = Color.White
)
)
}
}
// Overlay timer & action icons (hidden during Bio input)
if (displayMode != DisplayMode.BIO) {
Box(
modifier = Modifier
.padding(12.dp)
.align(Alignment.TopEnd)
.clip(RoundedCornerShape(12.dp))
.background(Color.Black.copy(alpha = 0.5f))
.padding(horizontal = 10.dp, vertical = 4.dp)
) {
Text(
text = "00:00 / 01:00",
color = Color.White,
fontSize = 11.sp,
fontWeight = FontWeight.SemiBold
)
}
Row(
modifier = Modifier
.align(Alignment.BottomCenter)
.padding(bottom = 20.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Box(
modifier = Modifier
.size(48.dp)
.clip(CircleShape)
.background(Color.White.copy(alpha = 0.85f))
.clickable { videoPickerLauncher.launch("video/*") },
contentAlignment = Alignment.Center
) {
Icon(
imageVector = Icons.Default.FileUpload,
contentDescription = "Upload Gallery Video",
tint = Color(0xFF475569),
modifier = Modifier.size(24.dp)
)
}
Box(
modifier = Modifier
.size(64.dp)
.clip(CircleShape)
.background(Color.White.copy(alpha = 0.9f))
.border(2.dp, Color(0xFFE11D48), CircleShape)
.clickable {
val tempUri = createVideoUri(context)
recordedVideoUri = tempUri
recordVideoLauncher.launch(tempUri)
},
contentAlignment = Alignment.Center
) {
Box(
modifier = Modifier
.size(48.dp)
.clip(CircleShape)
.background(Color(0xFFE11D48)),
contentAlignment = Alignment.Center
) {
Icon(
imageVector = Icons.Default.Videocam,
contentDescription = "Record Video",
tint = Color.White,
modifier = Modifier.size(26.dp)
)
}
}
Box(
modifier = Modifier
.size(48.dp)
.clip(CircleShape)
.background(Color.White.copy(alpha = 0.85f))
.clickable {
val tempUri = createImageUri(context)
tempCameraPhotoUri = tempUri
takePhotoLauncher.launch(tempUri)
},
contentAlignment = Alignment.Center
) {
Icon(
imageVector = Icons.Default.PhotoCamera,
contentDescription = "Take Photo",
tint = Color(0xFF475569),
modifier = Modifier.size(24.dp)
)
}
}
}
}
// --- Bottom Buttons ---
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(10.dp)
) {
Button(
onClick = { onSubmit(recordedVideoUri, bioText) },
modifier = Modifier
.fillMaxWidth()
.height(50.dp)
.background(buttonGradient, shape = RoundedCornerShape(12.dp)),
colors = ButtonDefaults.buttonColors(containerColor = Color.Transparent),
shape = RoundedCornerShape(12.dp)
) {
Text(text = "UPLOAD", color = Color.White, fontWeight = FontWeight.Bold, fontSize = 16.sp)
}
OutlinedButton(
onClick = {
displayMode = if (displayMode == DisplayMode.BIO) {
if (recordedVideoUri != null) DisplayMode.VIDEO else DisplayMode.IMAGE
} else {
DisplayMode.BIO
}
},
modifier = Modifier
.fillMaxWidth()
.height(50.dp),
shape = RoundedCornerShape(12.dp),
border = ButtonDefaults.outlinedButtonBorder.copy(
brush = Brush.horizontalGradient(listOf(Color(0xFFE11D48), Color(0xFFE11D48)))
)
) {
Text(
text = if (displayMode == DisplayMode.BIO) "VIDEO INTRO" else "ADD BIO",
color = Color(0xFFE11D48),
fontWeight = FontWeight.Bold,
fontSize = 14.sp
)
}
}
}
}
// Media3 ExoPlayer composable
@Composable
fun VideoPlayer(videoUri: Uri) {
val context = LocalContext.current
val exoPlayer = remember(videoUri) {
ExoPlayer.Builder(context).build().apply {
setMediaItem(MediaItem.fromUri(videoUri))
prepare()
playWhenReady = true
}
}
DisposableEffect(Unit) {
onDispose {
exoPlayer.release()
}
}
AndroidView(
factory = { ctx ->
PlayerView(ctx).apply {
player = exoPlayer
useController = true
}
},
modifier = Modifier.fillMaxSize()
)
}
// Helper functions to create temporary File Uris for Video and Image captures
fun createVideoUri(context: Context): Uri {
val tempFile = File.createTempFile("intro_video_", ".mp4", context.cacheDir).apply {
createNewFile()
deleteOnExit()
}
return FileProvider.getUriForFile(
context,
"${context.packageName}.provider",
tempFile
)
}
fun createImageUrl(context: Context): Uri {
val tempFile = File.createTempFile("intro_photo_", ".jpg", context.cacheDir).apply {
createNewFile()
deleteOnExit()
}
return FileProvider.getUriForFile(
context,
"${context.packageName}.provider",
tempFile
)
}
// --- Composable Previews ---
@Preview(
name = "Upload Intro Screen - Full Preview",
showBackground = true,
showSystemUi = true
)
@Composable
fun UploadIntroVideoScreenPreview() {
DattingAPPTheme {
UploadIntroVideoScreen(
profileImageUri = null,
onSubmit = { _, _ -> }
)
}
}