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
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 = { _, _ -> }
|
|
)
|
|
}
|
|
}
|