diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 18d8d0c..b5ac541 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -11,6 +11,7 @@ + - - - - - - - + LazyColumn( + modifier = Modifier + .fillMaxSize() + .background(Color(0xFFFAFAFA)), + contentPadding = PaddingValues( + top = innerPadding.calculateTopPadding(), + bottom = innerPadding.calculateBottomPadding() + 16.dp + ) + ) { + // Search Bar + item { + OutlinedTextField( + value = "", + onValueChange = {}, + placeholder = { Text("Search chats or matches...", fontSize = 14.sp) }, + leadingIcon = { + Icon( + Icons.Default.Search, + contentDescription = null, + tint = Color.Gray + ) + }, + trailingIcon = { + Icon( + Icons.Default.Mic, + contentDescription = null, + tint = Color.Gray + ) + }, + shape = RoundedCornerShape(24.dp), + colors = OutlinedTextFieldDefaults.colors( + focusedBorderColor = Color(0xFFFFCDD2), + unfocusedBorderColor = Color(0xFFFFCDD2), + focusedContainerColor = Color.White, + unfocusedContainerColor = Color.White + ), + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp) + ) + } + + // Active Sparks & Vibes Row + item { + Column(modifier = Modifier.padding(vertical = 8.dp)) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + "Active Sparks & Vibes", + fontWeight = FontWeight.Bold, + fontSize = 14.sp + ) + Text( + "View all", + color = Color(0xFFE91E63), + fontSize = 12.sp, + fontWeight = FontWeight.SemiBold + ) + } + + Spacer(modifier = Modifier.height(8.dp)) + + LazyRow( + contentPadding = PaddingValues(horizontal = 16.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + items(sampleSparks) { spark -> + ActiveSparkItem(spark = spark) + } + } + } + } + + // Filter Chips + item { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Button( + onClick = {}, + colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF9C27B0)), + contentPadding = PaddingValues(horizontal = 16.dp, vertical = 6.dp), + modifier = Modifier.height(32.dp) + ) { + Text("All", fontSize = 12.sp) + } + + OutlinedButton( + onClick = {}, + border = BorderStroke(1.dp, Color(0xFFFFCDD2)), + contentPadding = PaddingValues(horizontal = 12.dp, vertical = 6.dp), + modifier = Modifier.height(32.dp) + ) { + Text("Unread", color = Color.Black, fontSize = 12.sp) + Spacer(modifier = Modifier.width(4.dp)) + Text( + "3", + color = Color(0xFFE91E63), + fontSize = 11.sp, + fontWeight = FontWeight.Bold + ) + } + + OutlinedButton( + onClick = {}, + border = BorderStroke(1.dp, Color(0xFFFFCDD2)), + contentPadding = PaddingValues(horizontal = 12.dp, vertical = 6.dp), + modifier = Modifier.height(32.dp) + ) { + Text("Matches", color = Color.Black, fontSize = 12.sp) + } + + OutlinedButton( + onClick = {}, + border = BorderStroke(1.dp, Color(0xFFFFCDD2)), + contentPadding = PaddingValues(horizontal = 12.dp, vertical = 6.dp), + modifier = Modifier.height(32.dp) + ) { + Icon( + imageVector = Icons.Default.FlashOn, + contentDescription = null, + tint = Color(0xFFE91E63), + modifier = Modifier.size(14.dp) + ) + Spacer(modifier = Modifier.width(2.dp)) + Text("Vibes", color = Color.Black, fontSize = 12.sp) + } + } + } + + // Chat List Items + items(sampleChats) { chat -> + ChatItemCard(chat = chat) } } } } +@Composable +fun ActiveSparkItem(spark: ActiveSpark) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + Box(contentAlignment = Alignment.BottomEnd) { + Box( + modifier = Modifier + .size(56.dp) + .clip(CircleShape) + .background(Color(0xFFCE93D8)) + .border( + width = 2.dp, + color = if (spark.isUser) Color.Transparent else Color(0xFFE91E63), + shape = CircleShape + ) + ) + + if (spark.isUser) { + Box( + modifier = Modifier + .size(18.dp) + .background(Color(0xFFE91E63), CircleShape) + .border(1.5.dp, Color.White, CircleShape), + contentAlignment = Alignment.Center + ) { + Icon( + imageVector = Icons.Default.Add, + contentDescription = "Add", + tint = Color.White, + modifier = Modifier.size(12.dp) + ) + } + } else { + Box( + modifier = Modifier + .size(12.dp) + .background(Color(0xFF4CAF50), CircleShape) + .border(1.5.dp, Color.White, CircleShape) + ) + } + } + Text( + text = spark.name, + fontSize = 11.sp, + fontWeight = FontWeight.Medium + ) + } +} + +@Composable +fun ChatItemCard(chat: ChatMessage) { + Card( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 4.dp), + shape = RoundedCornerShape(16.dp), + colors = CardDefaults.cardColors(containerColor = Color.White), + border = BorderStroke(1.dp, Color(0xFFFFEBEE)) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + // Profile Avatar Box + Box(modifier = Modifier.size(52.dp)) { + Box( + modifier = Modifier + .fillMaxSize() + .clip(RoundedCornerShape(12.dp)) + .background(Color(0xFFBA68C8)) + ) + + if (chat.matchPercentage != null) { + Surface( + color = Color(0xFF9C27B0), + shape = RoundedCornerShape(8.dp), + modifier = Modifier.align(Alignment.BottomStart) + ) { + Text( + text = "${chat.matchPercentage}%", + color = Color.White, + fontSize = 8.sp, + fontWeight = FontWeight.Bold, + modifier = Modifier.padding(horizontal = 4.dp, vertical = 1.dp) + ) + } + } + + if (chat.isOnline) { + Box( + modifier = Modifier + .size(10.dp) + .background(Color(0xFF4CAF50), CircleShape) + .border(1.dp, Color.White, CircleShape) + .align(Alignment.BottomEnd) + ) + } + + if (chat.isAiBot) { + Box( + modifier = Modifier + .size(16.dp) + .background(Color(0xFF9C27B0), CircleShape) + .align(Alignment.TopEnd), + contentAlignment = Alignment.Center + ) { + Icon( + imageVector = Icons.Default.AutoAwesome, + contentDescription = null, + tint = Color.White, + modifier = Modifier.size(10.dp) + ) + } + } + } + + Spacer(modifier = Modifier.width(12.dp)) + + // Details Section + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + Text( + text = if (chat.age > 0) "${chat.name}, ${chat.age}" else chat.name, + fontWeight = FontWeight.Bold, + fontSize = 15.sp + ) + if (chat.isVerified) { + Icon( + imageVector = Icons.Default.CheckCircle, + contentDescription = "Verified", + tint = Color(0xFFE91E63), + modifier = Modifier.size(14.dp) + ) + } + if (chat.aiTag != null) { + Surface( + color = Color(0xFFFFEBEE), + shape = RoundedCornerShape(4.dp) + ) { + Text( + text = chat.aiTag, + color = Color(0xFFE91E63), + fontSize = 9.sp, + fontWeight = FontWeight.Bold, + modifier = Modifier.padding(horizontal = 4.dp, vertical = 1.dp) + ) + } + } + } + + // Message Content + when { + chat.messageText != null -> { + Text( + text = chat.messageText, + fontSize = 12.sp, + color = Color.Gray, + maxLines = 1 + ) + } + chat.isVoiceNote -> { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + Icon( + imageVector = Icons.Default.Mic, + contentDescription = null, + tint = Color(0xFFE91E63), + modifier = Modifier.size(14.dp) + ) + Surface( + color = Color(0xFFFFEBEE), + shape = RoundedCornerShape(8.dp) + ) { + Text( + text = "|||!|!|| ${chat.voiceDuration}", + color = Color(0xFFE91E63), + fontSize = 10.sp, + modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp) + ) + } + Text( + text = "Voice note", + fontSize = 12.sp, + fontWeight = FontWeight.SemiBold + ) + } + } + chat.isPhoto -> { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + Icon( + imageVector = Icons.Default.CameraAlt, + contentDescription = null, + tint = Color(0xFFE91E63), + modifier = Modifier.size(14.dp) + ) + Text( + text = chat.photoText ?: "Photo", + fontSize = 12.sp, + color = Color.Gray + ) + } + } + } + } + + // Time & Status Section + Column( + horizontalAlignment = Alignment.End, + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + Text( + text = chat.time, + fontSize = 11.sp, + color = if (chat.unreadCount > 0) Color(0xFFE91E63) else Color.Gray + ) + + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + if (chat.isPinned) { + Icon( + imageVector = Icons.Default.PushPin, + contentDescription = "Pinned", + tint = Color(0xFFE91E63), + modifier = Modifier.size(12.dp) + ) + } + + if (chat.unreadCount > 0) { + Box( + modifier = Modifier + .size(18.dp) + .background(Color(0xFF9C27B0), CircleShape), + contentAlignment = Alignment.Center + ) { + Text( + text = chat.unreadCount.toString(), + color = Color.White, + fontSize = 10.sp, + fontWeight = FontWeight.Bold + ) + } + } + } + } + } + } +} + +// Sample Data +val sampleSparks = listOf( + ActiveSpark("Your Note", isUser = true), + ActiveSpark("Elena"), + ActiveSpark("Sarah"), + ActiveSpark("Jordan"), + ActiveSpark("Marcus") +) + +val sampleChats = listOf( + ChatMessage( + id = 1, + name = "Elena", + age = 24, + time = "12:45 PM", + isVerified = true, + matchPercentage = 98, + messageText = "Are you free for that coffee in ...", + unreadCount = 2, + isPinned = true, + isOnline = true + ), + ChatMessage( + id = 2, + name = "Sarah", + age = 26, + time = "11:30 AM", + isVoiceNote = true, + voiceDuration = "0:24", + unreadCount = 1, + isOnline = true + ), + ChatMessage( + id = 3, + name = "Jordan", + age = 27, + time = "Yesterday", + isPhoto = true, + photoText = "Photo: Check out this spot!" + ), + ChatMessage( + id = 4, + name = "VibeBot Wingmate", + age = 0, + time = "Yesterday", + isAiBot = true, + aiTag = "AI", + messageText = "Elena liked your coffe..." + ), + ChatMessage( + id = 5, + name = "Marcus", + age = 28, + time = "Wednesday", + messageText = "Haha sounds like a plan, let me kn..." + ) +) + +@Preview(showBackground = true, showSystemUi = true) +@Composable +fun ChatScreenPreview() { + MaterialTheme { + ChatScreen() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/dattingapp/LikeAndMatchScreen.kt b/app/src/main/java/com/example/dattingapp/LikeAndMatchScreen.kt new file mode 100644 index 0000000..d614a48 --- /dev/null +++ b/app/src/main/java/com/example/dattingapp/LikeAndMatchScreen.kt @@ -0,0 +1,252 @@ +package com.example.dattingapp +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.foundation.lazy.grid.items +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Close +import androidx.compose.material.icons.filled.Favorite +import androidx.compose.material.icons.filled.LocationOn +import androidx.compose.material.icons.outlined.ChatBubbleOutline +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +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.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import coil.compose.AsyncImage + + + +class LikeAndMatchScreen { +} + +data class Profile( + val name: String, + val age: Int, + val distance: String, + val matchPercentage: Int, + val tags: List, + val imageUrl: String, + val hasUnread: Boolean = false +) + +@Composable +fun ProfileGrid(profiles: List, modifier: Modifier = Modifier) { + LazyVerticalGrid( + columns = GridCells.Fixed(2), + contentPadding = PaddingValues(12.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), + modifier = modifier.fillMaxSize() + ) { + items(profiles) { profile -> + ProfileCard(profile = profile) + } + } +} + +@Composable +fun ProfileCard(profile: Profile, modifier: Modifier = Modifier) { + Card( + shape = RoundedCornerShape(16.dp), + colors = CardDefaults.cardColors(containerColor = Color.White), + elevation = CardDefaults.cardElevation(defaultElevation = 2.dp), + modifier = modifier.fillMaxWidth() + ) { + Column( + modifier = Modifier.padding(8.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + // Image with Overlays + Box( + modifier = Modifier + .fillMaxWidth() + .height(180.dp) + .clip(RoundedCornerShape(12.dp)) + ) { + AsyncImage( + model = profile.imageUrl, + contentDescription = profile.name, + contentScale = ContentScale.Crop, + modifier = Modifier.fillMaxSize() + ) + + // Gradient Overlay for Text Readability + Box( + modifier = Modifier + .fillMaxSize() + .background( + Brush.verticalGradient( + colors = listOf(Color.Transparent, Color.Black.copy(alpha = 0.7f)), + startY = 100f + ) + ) + ) + + // Match Badge + Surface( + shape = RoundedCornerShape(20.dp), + color = Color.White.copy(alpha = 0.9f), + modifier = Modifier + .padding(8.dp) + .align(Alignment.TopStart) + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(4.dp), + modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp) + ) { + Icon( + imageVector = Icons.Default.Favorite, + contentDescription = null, + tint = Color(0xFF6C5CE7), + modifier = Modifier.size(12.dp) + ) + Text( + text = "${profile.matchPercentage}%", + fontSize = 11.sp, + fontWeight = FontWeight.Bold, + color = Color(0xFF2D3436) + ) + } + } + + // Notification Dot + if (profile.hasUnread) { + Box( + modifier = Modifier + .padding(8.dp) + .size(8.dp) + .clip(CircleShape) + .background(Color(0xFF6C5CE7)) + .align(Alignment.TopEnd) + ) + } + + // Name, Age & Distance + Column( + modifier = Modifier + .align(Alignment.BottomStart) + .padding(8.dp) + ) { + Text( + text = "${profile.name}, ${profile.age}", + color = Color.White, + fontWeight = FontWeight.Bold, + fontSize = 14.sp + ) + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.spacedBy(2.dp) + ) { + Icon( + imageVector = Icons.Default.LocationOn, + contentDescription = null, + tint = Color.White.copy(alpha = 0.8f), + modifier = Modifier.size(10.dp) + ) + Text( + text = profile.distance, + color = Color.White.copy(alpha = 0.8f), + fontSize = 10.sp + ) + } + } + } + + // Tags Row + Row( + horizontalArrangement = Arrangement.spacedBy(4.dp), + modifier = Modifier.fillMaxWidth() + ) { + profile.tags.take(2).forEach { tag -> + Surface( + shape = RoundedCornerShape(6.dp), + color = Color(0xFFF1F2F6), + modifier = Modifier.weight(1f) + ) { + Text( + text = tag, + fontSize = 10.sp, + color = Color(0xFF636E72), + modifier = Modifier.padding(vertical = 4.dp, horizontal = 4.dp), + maxLines = 1 + ) + } + } + } + + // Action Buttons + Row( + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.fillMaxWidth().padding(10.dp) + ) { + IconButton( + onClick = { }, + modifier = Modifier + .size(22.dp) + .background(Color(0xFFF8F9FA), CircleShape) + ) { + Icon( + imageVector = Icons.Default.Close, + contentDescription = "Pass", + tint = Color(0xFF636E72), + modifier = Modifier.size(16.dp) + ) + } + + IconButton( + onClick = { }, + modifier = Modifier + .size(22.dp) + .background(Color(0xFFF1EFFE), CircleShape) + ) { + Icon( + imageVector = Icons.Outlined.ChatBubbleOutline, + contentDescription = "Message", + tint = Color(0xFF6C5CE7), + modifier = Modifier.size(16.dp) + ) + } + + IconButton( + onClick = { }, + modifier = Modifier + .size(22.dp) + .background(Color(0xFFD63031), CircleShape) + ) { + Icon( + imageVector = Icons.Default.Favorite, + contentDescription = "Like", + tint = Color.White, + modifier = Modifier.size(16.dp) + ) + } + } + } + } +} + +@Preview(showBackground = true) +@Composable +fun ProfileGridPreview() { + val sampleProfiles = listOf( + Profile("Elena", 24, "2 miles away", 98, listOf("Photography", "Coffee"), "", true), + Profile("Sarah", 26, "4 miles away", 94, listOf("Travel", "Music"), ""), + Profile("Julian", 27, "3 miles away", 91, listOf("Music Prod", "Vinyl"), ""), + Profile("Maya", 25, "5 miles away", 89, listOf("Surfing", "Yoga"), "") + ) + + ProfileGrid(profiles = sampleProfiles) +} \ No newline at end of file diff --git a/app/src/main/java/com/example/dattingapp/Login/LoginActivity.kt b/app/src/main/java/com/example/dattingapp/Login/LoginActivity.kt index af091d0..0a25635 100644 --- a/app/src/main/java/com/example/dattingapp/Login/LoginActivity.kt +++ b/app/src/main/java/com/example/dattingapp/Login/LoginActivity.kt @@ -36,6 +36,7 @@ import androidx.compose.ui.unit.sp import com.example.dattingapp.homes.HomeActivity import com.example.dattingapp.R import com.example.dattingapp.homes.networkandapi.TokenManager +import com.example.dattingapp.permission.PermissionHelper import com.example.dattingapp.registration.LoginRequest import com.example.dattingapp.registration.NetworkModule import com.example.dattingapp.registration.RegistrationScreen @@ -44,9 +45,17 @@ import kotlinx.coroutines.launch class LoginActivity : ComponentActivity() { + + private lateinit var permissionHelper: PermissionHelper override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + permissionHelper = PermissionHelper(this) + + // 2. Call Notification Permission + permissionHelper.requestPermissions(PermissionHelper.PermissionType.NOTIFICATION) { isGranted -> + if (isGranted) { /* Handle granted */ } + } enableEdgeToEdge() setContent { DattingAPPTheme { @@ -66,7 +75,6 @@ class LoginActivity : ComponentActivity() { } } } - @Composable fun LoginScreenContent( onLoginSuccess: () -> Unit = {}, diff --git a/app/src/main/java/com/example/dattingapp/MainActivity.kt b/app/src/main/java/com/example/dattingapp/MainActivity.kt index 68d44b3..59698c1 100644 --- a/app/src/main/java/com/example/dattingapp/MainActivity.kt +++ b/app/src/main/java/com/example/dattingapp/MainActivity.kt @@ -2,35 +2,49 @@ package com.example.dattingapp import android.content.Intent import android.os.Bundle +import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge +import com.example.dattingapp.Login.LoginActivity import com.example.dattingapp.homes.HomeActivity import com.example.dattingapp.homes.networkandapi.TokenManager -import com.example.dattingapp.permission.NotificationPermission +import com.example.dattingapp.permission.PermissionHelper import com.example.dattingapp.ui.theme.DattingAPPTheme class MainActivity : ComponentActivity() { - override fun onCreate(savedInstanceState: Bundle?) { + // 1. Declare PermissionHelper at class level + private lateinit var permissionHelper: PermissionHelper + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() + + // 2. Initialize PermissionHelper in onCreate() BEFORE setContent + permissionHelper = PermissionHelper(this) + + val tokenManager = TokenManager.getInstance(this) + + if (tokenManager.isLoggedIn()) { + // User is logged in -> Navigate directly to Home and finish + startActivity(Intent(this, HomeActivity::class.java)) + finish() + return + }else{ + + startActivity(Intent(this, LoginActivity::class.java)) + finish() + } + + + setContent { DattingAPPTheme { - val tokenManager = TokenManager.getInstance(this) - - if (tokenManager.isLoggedIn()) { - // 1. User is logged in -> Navigate directly to Home - startActivity(Intent(this, HomeActivity::class.java)) - } else { - // 2. User is NOT logged in -> Navigate to Notification Permission or Login - startActivity(Intent(this, NotificationPermission::class.java)) - } - - // Close MainActivity so it doesn't stay on the backstack - finish() + // Render your main login/welcome UI screen here instead of finishing immediately } } } -} + + +} \ No newline at end of file diff --git a/app/src/main/java/com/example/dattingapp/chat/PersonChatScreen.kt b/app/src/main/java/com/example/dattingapp/chat/PersonChatScreen.kt new file mode 100644 index 0000000..67f7690 --- /dev/null +++ b/app/src/main/java/com/example/dattingapp/chat/PersonChatScreen.kt @@ -0,0 +1,385 @@ +package com.example.dattingapp.chat + + +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +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.automirrored.filled.Send +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.PlayArrow +import androidx.compose.material.icons.outlined.SentimentSatisfied +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.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.example.dattingapp.ui.theme.AppBackground +import com.example.dattingapp.ui.theme.HeaderTitleColor +import com.example.dattingapp.ui.theme.InputFieldBg +import com.example.dattingapp.ui.theme.RecipientBubbleBg +import com.example.dattingapp.ui.theme.SenderBubbleBg +import com.example.dattingapp.ui.theme.SubtextColor + + +class PersonChatScreen { +} + + + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ChatDetailScreen( + userName: String = "Sarah", + onBackClick: () -> Unit = {} +) { + var textValue by remember { mutableStateOf("") } + + Scaffold( + topBar = { + TopAppBar( + title = { + Box( + modifier = Modifier.fillMaxWidth(), + contentAlignment = Alignment.Center + ) { + Text( + text = userName, + fontSize = 18.sp, + fontWeight = FontWeight.Bold, + color = HeaderTitleColor + ) + } + }, + navigationIcon = { + IconButton(onClick = onBackClick) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = "Back", + tint = Color.DarkGray + ) + } + }, + actions = { + Box( + modifier = Modifier + .padding(end = 16.dp) + .size(32.dp) + .clip(CircleShape) + .background(Color.LightGray) + ) { + // Replace drawable resource with profile image + Image( + painter = painterResource(id = android.R.drawable.ic_menu_gallery), + contentDescription = "Profile", + contentScale = ContentScale.Crop, + modifier = Modifier.fillMaxSize() + ) + } + }, + colors = TopAppBarDefaults.topAppBarColors(containerColor = AppBackground) + ) + }, + bottomBar = { + ChatBottomInputBar( + textValue = textValue, + onValueChange = { textValue = it } + ) + }, + containerColor = AppBackground, + modifier = Modifier.fillMaxSize() + ) { innerPadding -> + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding) + .padding(horizontal = 16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + // Video Intro Item + item { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.Start + ) { + AvatarIcon() + Spacer(modifier = Modifier.width(8.dp)) + Column { + VideoCardItem(duration = "0:15") + Spacer(modifier = Modifier.height(6.dp)) + Text( + text = "$userName sent a video intro!", + fontSize = 14.sp, + color = Color.DarkGray, + fontWeight = FontWeight.Medium + ) + Text( + text = "Just now", + fontSize = 12.sp, + color = SubtextColor + ) + } + } + } + + // Received Text Message Item + item { + Row( + horizontalArrangement = Arrangement.Start, + verticalAlignment = Alignment.Top + ) { + AvatarIcon() + Spacer(modifier = Modifier.width(8.dp)) + Column { + Surface( + shape = RoundedCornerShape( + topStart = 16.dp, + topEnd = 16.dp, + bottomEnd = 16.dp, + bottomStart = 4.dp + ), + color = SenderBubbleBg, + modifier = Modifier.widthIn(max = 240.dp) + ) { + Text( + text = "Hi", + fontSize = 15.sp, + color = Color.Black, + modifier = Modifier.padding(horizontal = 14.dp, vertical = 10.dp) + ) + } + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = "2:46 PM", + fontSize = 11.sp, + color = SubtextColor + ) + } + } + } + + // Timestamp Header Item + item { + Text( + text = "Today, 2:45 PM", + fontSize = 12.sp, + color = SubtextColor, + textAlign = TextAlign.Center, + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp) + ) + } + + // Sent Message Item + item { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.End + ) { + Surface( + shape = RoundedCornerShape( + topStart = 16.dp, + topEnd = 16.dp, + bottomEnd = 4.dp, + bottomStart = 16.dp + ), + color = RecipientBubbleBg + ) { + Text( + text = "Hello", + fontSize = 15.sp, + color = Color.White, + modifier = Modifier.padding(horizontal = 16.dp, vertical = 10.dp) + ) + } + AvatarIcon() + } + } + } + } +} + +@Composable +fun VideoCardItem(duration: String) { + Card( + shape = RoundedCornerShape(16.dp), + modifier = Modifier + .width(280.dp) + .height(280.dp) + ) { + Box(modifier = Modifier.fillMaxSize()) { + // Replace R.drawable.ic_menu_gallery with your video thumbnail resource + Image( + painter = painterResource(id = android.R.drawable.ic_menu_gallery), + contentDescription = "Video Thumbnail", + contentScale = ContentScale.Crop, + modifier = Modifier.fillMaxSize() + ) + + // Center Play Button Overlay + Box( + modifier = Modifier + .size(52.dp) + .align(Alignment.Center) + .clip(CircleShape) + .background(Color.White.copy(alpha = 0.4f)), + contentAlignment = Alignment.Center + ) { + Icon( + imageVector = Icons.Default.PlayArrow, + contentDescription = "Play", + tint = Color.White, + modifier = Modifier.size(32.dp) + ) + } + + // Video Duration Chip + Surface( + color = Color.Black.copy(alpha = 0.6f), + shape = RoundedCornerShape(6.dp), + modifier = Modifier + .align(Alignment.BottomEnd) + .padding(12.dp) + ) { + Text( + text = duration, + color = Color.White, + fontSize = 11.sp, + fontWeight = FontWeight.Bold, + modifier = Modifier.padding(horizontal = 6.dp, vertical = 2.dp) + ) + } + } + } +} + +@Composable +fun AvatarIcon() { + Box( + modifier = Modifier + .size(28.dp) + .clip(CircleShape) + .background(Color.LightGray) + ) { + Image( + painter = painterResource(id = android.R.drawable.ic_menu_gallery), + contentDescription = "Avatar", + contentScale = ContentScale.Crop, + modifier = Modifier.fillMaxSize() + ) + } +} + +@Composable +fun ChatBottomInputBar( + textValue: String, + onValueChange: (String) -> Unit +) { + Surface( + color = Color.White, + modifier = Modifier.fillMaxWidth() + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .navigationBarsPadding() + .padding(horizontal = 12.dp, vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + // Plus/Attachment Button + IconButton( + onClick = {}, + modifier = Modifier.size(36.dp) + ) { + Icon( + imageVector = Icons.Default.Add, + contentDescription = "Add Attachment", + tint = Color.DarkGray + ) + } + + Spacer(modifier = Modifier.width(4.dp)) + + // Text Field Container + TextField( + value = textValue, + onValueChange = onValueChange, + placeholder = { + Text( + "Message Sarah...", + color = Color.Gray, + fontSize = 14.sp + ) + }, + trailingIcon = { + IconButton(onClick = {}) { + Icon( + imageVector = Icons.Outlined.SentimentSatisfied, + contentDescription = "Emoji", + tint = Color.Gray + ) + } + }, + colors = TextFieldDefaults.colors( + focusedContainerColor = InputFieldBg, + unfocusedContainerColor = InputFieldBg, + disabledContainerColor = InputFieldBg, + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent + ), + shape = RoundedCornerShape(24.dp), + modifier = Modifier + .weight(1f) + .height(48.dp) + ) + + Spacer(modifier = Modifier.width(8.dp)) + + // Gradient Send Button + Box( + modifier = Modifier + .size(44.dp) + .clip(CircleShape) + .background( + Brush.linearGradient( + colors = listOf( + Color(0xFFC2185B), + Color(0xFF4A148C) + ) + ) + ), + contentAlignment = Alignment.Center + ) { + IconButton(onClick = {}) { + Icon( + imageVector = Icons.AutoMirrored.Filled.Send, + contentDescription = "Send", + tint = Color.White, + modifier = Modifier.size(20.dp) + ) + } + } + } + } +} + +@Preview(showBackground = true) +@Composable +fun ChatDetailScreenPreview() { + ChatDetailScreen() +} \ No newline at end of file diff --git a/app/src/main/java/com/example/dattingapp/homes/HomeActivity.kt b/app/src/main/java/com/example/dattingapp/homes/HomeActivity.kt index dc19496..02cb259 100644 --- a/app/src/main/java/com/example/dattingapp/homes/HomeActivity.kt +++ b/app/src/main/java/com/example/dattingapp/homes/HomeActivity.kt @@ -1,7 +1,9 @@ package com.example.dattingapp.homes +import ChatScreen import android.os.Bundle import android.util.Log +import android.widget.Toast import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge @@ -13,11 +15,13 @@ import androidx.compose.material.icons.filled.ChatBubbleOutline import androidx.compose.material.icons.filled.Explore import androidx.compose.material.icons.filled.Person import androidx.compose.material.icons.filled.SwapHoriz +import androidx.compose.material3.AlertDialog import androidx.compose.material3.Icon import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.Scaffold import androidx.compose.material3.Text +import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState @@ -29,28 +33,97 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.lifecycle.viewmodel.compose.viewModel -import com.example.dattingapp.ChatScreen import com.example.dattingapp.homes.networkandapi.HomeNetworkModule import com.example.dattingapp.homes.networkandapi.MeetUiState import com.example.dattingapp.homes.networkandapi.MeetUserProfile import com.example.dattingapp.homes.networkandapi.MeetViewModel import com.example.dattingapp.homes.networkandapi.MyProfileUiState import com.example.dattingapp.homes.networkandapi.MyProfileViewModel +import com.example.dattingapp.permission.PermissionHelper import com.example.dattingapp.ui.theme.DattingAPPTheme +import kotlinx.coroutines.delay import kotlin.coroutines.cancellation.CancellationException - +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton class HomeActivity : ComponentActivity() { + private lateinit var permissionHelper: PermissionHelper + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) + permissionHelper = PermissionHelper(this) enableEdgeToEdge() setContent { - DattingAPPTheme { + var showDialog by remember { mutableStateOf(false) } + var targetPermission by remember { mutableStateOf(null) } + + // Function to handle permission requests with state check + fun checkAndRequestPermission(type: PermissionHelper.PermissionType) { + val isAlreadyGranted = if (type == PermissionHelper.PermissionType.OVERLAY) { + permissionHelper.isOverlayPermissionGranted() + } else { + permissionHelper.hasPermission(type) + } + + if (isAlreadyGranted) { + Toast.makeText(this, "${type.name} already granted!", Toast.LENGTH_SHORT).show() + } else { + targetPermission = type + showDialog = true + } + } + + // 10-second delay trigger inside Compose lifecycle + LaunchedEffect(Unit) { + delay(10_000) // Wait 10 seconds + checkAndRequestPermission(PermissionHelper.PermissionType.OVERLAY) + } + + // Custom rationale dialog + if (showDialog && targetPermission != null) { + val (title, description) = when (targetPermission!!) { + PermissionHelper.PermissionType.OVERLAY -> + "Display Over Other Apps" to "This permission is needed to show floating call alerts over other apps." + PermissionHelper.PermissionType.LOCATION -> + "Location Access" to "We need location access to find matches near you." + PermissionHelper.PermissionType.AUDIO -> + "Microphone Access" to "Microphone access is required for voice calling." + PermissionHelper.PermissionType.GALLERY -> + "Photos Access" to "Access to photos is required to update your profile picture." + PermissionHelper.PermissionType.NOTIFICATION -> + "Notifications" to "Enable notifications so you don't miss new messages." + } + + PermissionRationaleDialog( + title = title, + description = description, + onConfirm = { + showDialog = false + val currentType = targetPermission!! + + if (currentType == PermissionHelper.PermissionType.OVERLAY) { + permissionHelper.requestOverlayPermission { isGranted -> + val msg = if (isGranted) "Overlay Granted" else "Overlay Denied" + Toast.makeText(this@HomeActivity, msg, Toast.LENGTH_SHORT).show() + } + } else { + permissionHelper.requestPermissions(currentType) { isGranted -> + val msg = if (isGranted) "${currentType.name} Granted" else "${currentType.name} Denied" + Toast.makeText(this@HomeActivity, msg, Toast.LENGTH_SHORT).show() + } + } + }, + onDismiss = { + showDialog = false + } + ) + } HomeScreen() } @@ -59,6 +132,32 @@ class HomeActivity : ComponentActivity() { } + + +@Composable +fun PermissionRationaleDialog( + title: String, + description: String, + onConfirm: () -> Unit, + onDismiss: () -> Unit +) { + AlertDialog( + onDismissRequest = onDismiss, + title = { Text(text = title) }, + text = { Text(text = description) }, + confirmButton = { + TextButton(onClick = onConfirm) { + Text(text = "Allow") + } + }, + dismissButton = { + TextButton(onClick = onDismiss) { + Text(text = "Cancel") + } + } + ) +} + @Composable fun HomeScreen() { diff --git a/app/src/main/java/com/example/dattingapp/homes/networkandapi/TokenManager.kt b/app/src/main/java/com/example/dattingapp/homes/networkandapi/TokenManager.kt index 400dc95..e3ee0c8 100644 --- a/app/src/main/java/com/example/dattingapp/homes/networkandapi/TokenManager.kt +++ b/app/src/main/java/com/example/dattingapp/homes/networkandapi/TokenManager.kt @@ -41,7 +41,7 @@ class TokenManager private constructor(context: Context) { // Checks local token presence, not server validity or expiration. fun isLoggedIn(): Boolean { - return !getAccessToken().isNullOrBlank() + return !getRefreshToken().isNullOrBlank() } fun clearTokens() { diff --git a/app/src/main/java/com/example/dattingapp/permission/GallaryPermission.kt b/app/src/main/java/com/example/dattingapp/permission/GallaryPermission.kt deleted file mode 100644 index 51f9e5e..0000000 --- a/app/src/main/java/com/example/dattingapp/permission/GallaryPermission.kt +++ /dev/null @@ -1,231 +0,0 @@ -package com.example.dattingapp.permission - -import android.Manifest -import android.content.Intent -import android.os.Build -import android.os.Bundle -import android.widget.Toast -import androidx.activity.ComponentActivity -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.layout.* -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.ArrowForward -import androidx.compose.material.icons.filled.Image -import androidx.compose.material3.* -import androidx.compose.runtime.Composable -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.res.painterResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.example.dattingapp.R -import com.example.dattingapp.ui.theme.DattingAPPTheme -import com.example.dattingapp.ui.theme.GradientEnd -import com.example.dattingapp.ui.theme.GradientStart - -class GallaryPermission : ComponentActivity() { - - // 1. Permission Launcher to handle gallery/photos permission request - private val requestGalleryPermissionLauncher = - registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -> - if (isGranted) { - // Permission granted: navigate to next screen - navigateToNextActivity() - } else { - // Permission denied: show toast or proceed directly - Toast.makeText(this, "Gallery permission denied", Toast.LENGTH_SHORT).show() - navigateToNextActivity() - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - enableEdgeToEdge() - setContent { - DattingAPPTheme { - GallaryPermissionScreen( - onEnableGalleryClick = { - // Request correct permission based on Android SDK version - val permissionToRequest = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - Manifest.permission.READ_MEDIA_IMAGES - } else { - Manifest.permission.READ_EXTERNAL_STORAGE - } - requestGalleryPermissionLauncher.launch(permissionToRequest) - }, - onNotNowClick = { - // Directly skip to next activity - navigateToNextActivity() - } - ) - } - } - } - - // Helper function to launch the target activity - private fun navigateToNextActivity() { - // TODO: Replace NextActivity::class.java with your target Activity class - val intent = Intent(this, LocationPermision::class.java) - startActivity(intent) - finish() // Closes current permission screen - } -} - -@Composable -fun GallaryPermissionScreen( - onEnableGalleryClick: () -> Unit, - onNotNowClick: () -> Unit, - modifier: Modifier = Modifier -) { - Box( - modifier = modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - Image( - painter = painterResource(id = R.drawable.gallarypermissionbg), - contentDescription = null, - modifier = Modifier.fillMaxSize(), - contentScale = ContentScale.Crop - ) - NotificationPermissionCard( - onEnableGalleryClick = onEnableGalleryClick, - onNotNowClick = onNotNowClick - ) - } -} - -@Composable -private fun NotificationPermissionCard( - onEnableGalleryClick: () -> Unit, - onNotNowClick: () -> Unit, - modifier: Modifier = Modifier -) { - val buttonGradient = Brush.horizontalGradient( - colors = listOf(GradientStart, GradientEnd) - ) - Column( - modifier = modifier - .fillMaxSize() - .padding(horizontal = 24.dp, vertical = 50.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.SpaceBetween - ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally - ) { - Box( - modifier = Modifier - .size(72.dp) - .clip(CircleShape) - .background(buttonGradient), - contentAlignment = Alignment.Center - ) { - Icon( - imageVector = Icons.Default.Image, - contentDescription = null, - tint = Color.White, - modifier = Modifier.size(36.dp) - ) - } - - Spacer(modifier = Modifier.height(16.dp)) - - Text( - text = "Discover Cultural\nVibes", - fontSize = 22.sp, - fontWeight = FontWeight.Bold, - color = Color.Black, - textAlign = TextAlign.Center, - lineHeight = 28.sp - ) - - Spacer(modifier = Modifier.height(12.dp)) - - Text( - text = "Find amazing people sharing the same space. Let's make a radiant connection at your favorite spots.", - fontSize = 17.sp, - fontWeight = FontWeight.Normal, - color = Color.Black, - textAlign = TextAlign.Center, - lineHeight = 22.sp, - modifier = Modifier.padding(horizontal = 4.dp) - ) - } - - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.fillMaxWidth() - ) { - Button( - onClick = onEnableGalleryClick, - modifier = Modifier - .fillMaxWidth() - .height(50.dp) - .clip(CircleShape) - .background(buttonGradient), - colors = ButtonDefaults.buttonColors( - containerColor = Color.Transparent - ), - contentPadding = PaddingValues() - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center - ) { - Text( - text = "ALLOW GALLERY ACCESS", - color = Color.White, - fontSize = 13.sp, - fontWeight = FontWeight.Bold, - letterSpacing = 0.5.sp - ) - Spacer(modifier = Modifier.width(8.dp)) - Icon( - imageVector = Icons.AutoMirrored.Filled.ArrowForward, - contentDescription = null, - tint = Color.White, - modifier = Modifier.size(16.dp) - ) - } - } - - Spacer(modifier = Modifier.height(4.dp)) - - TextButton( - onClick = onNotNowClick, - modifier = Modifier.height(40.dp) - ) { - Text( - text = "NOT NOW", - color = GradientStart, - fontSize = 13.sp, - fontWeight = FontWeight.Bold, - letterSpacing = 0.5.sp - ) - } - } - } -} - -@Preview(showBackground = true, showSystemUi = true) -@Composable -fun GallaryPermissionPreview() { - DattingAPPTheme { - GallaryPermissionScreen( - onEnableGalleryClick = {}, - onNotNowClick = {} - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/dattingapp/permission/LocationPermision.kt b/app/src/main/java/com/example/dattingapp/permission/LocationPermision.kt deleted file mode 100644 index 755b7fd..0000000 --- a/app/src/main/java/com/example/dattingapp/permission/LocationPermision.kt +++ /dev/null @@ -1,331 +0,0 @@ -package com.example.dattingapp.permission - -import android.Manifest -import android.content.Intent -import android.os.Bundle -import android.widget.Toast -import androidx.activity.ComponentActivity -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.layout.* -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.foundation.shape.GenericShape -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.ArrowForward -import androidx.compose.material.icons.filled.Check -import androidx.compose.material.icons.filled.LocationOn -import androidx.compose.material3.* -import androidx.compose.runtime.Composable -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.res.painterResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.example.dattingapp.R -import com.example.dattingapp.ui.theme.DattingAPPTheme -import com.example.dattingapp.ui.theme.GradientEnd -import com.example.dattingapp.ui.theme.GradientStart -import com.example.dattingapp.ui.theme.whitecolor - -class LocationPermision : ComponentActivity() { - - // 1. Permission Launcher for Location Permissions - private val requestLocationPermissionLauncher = - registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions -> - val isFineLocationGranted = permissions[Manifest.permission.ACCESS_FINE_LOCATION] ?: false - val isCoarseLocationGranted = permissions[Manifest.permission.ACCESS_COARSE_LOCATION] ?: false - - if (isFineLocationGranted || isCoarseLocationGranted) { - navigateToNextActivity() - } else { - Toast.makeText(this, "Location permission denied", Toast.LENGTH_SHORT).show() - navigateToNextActivity() - } - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - enableEdgeToEdge() - setContent { - DattingAPPTheme { - LocationPermissionScreen( - onEnableLocationClick = { - // Request FINE and COARSE location runtime permissions - requestLocationPermissionLauncher.launch( - arrayOf( - Manifest.permission.ACCESS_FINE_LOCATION, - Manifest.permission.ACCESS_COARSE_LOCATION - ) - ) - }, - onNotNowClick = { - // Skip permission prompt and go straight to next activity - navigateToNextActivity() - } - ) - } - } - } - - private fun navigateToNextActivity() { - val intent = Intent(this, OtherAppPermission::class.java) - startActivity(intent) - finish() - } -} - -@Composable -fun LocationPermissionScreen( - onEnableLocationClick: () -> Unit, - onNotNowClick: () -> Unit, - modifier: Modifier = Modifier -) { - Box( - modifier = modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - Image( - painter = painterResource(id = R.drawable.locationpermissionbg), - contentDescription = null, - modifier = Modifier.fillMaxSize(), - contentScale = ContentScale.Crop - ) - LocationPermissionCard( - onEnableLocationClick = onEnableLocationClick, - onNotNowClick = onNotNowClick - ) - } -} - -@Composable -private fun LocationPermissionCard( - onEnableLocationClick: () -> Unit, - onNotNowClick: () -> Unit, - modifier: Modifier = Modifier -) { - val buttonGradient = Brush.horizontalGradient( - colors = listOf(GradientStart, GradientEnd) - ) - Column( - modifier = modifier - .fillMaxSize() - .padding(horizontal = 24.dp, vertical = 50.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.SpaceBetween - ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally - ) { - Box( - modifier = Modifier - .size(72.dp) - .clip(CircleShape) - .background(buttonGradient), - contentAlignment = Alignment.Center - ) { - Icon( - imageVector = Icons.Default.LocationOn, - contentDescription = null, - tint = Color.White, - modifier = Modifier.size(36.dp) - ) - } - - Spacer(modifier = Modifier.height(16.dp)) - - Text( - text = "Discover Cultural\nVibes", - fontSize = 22.sp, - fontWeight = FontWeight.Bold, - color = whitecolor, - textAlign = TextAlign.Center, - lineHeight = 28.sp - ) - - Spacer(modifier = Modifier.height(12.dp)) - - Text( - text = "Find amazing people sharing the same space. Let's make a radiant connection at your favorite spots.", - fontSize = 13.sp, - fontWeight = FontWeight.Normal, - color = whitecolor, - textAlign = TextAlign.Center, - lineHeight = 18.sp, - modifier = Modifier.padding(horizontal = 4.dp) - ) - } - - vibeFoundCard(modifier = Modifier.offset(y = (-100).dp)) - - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.fillMaxWidth() - ) { - Button( - onClick = onEnableLocationClick, - modifier = Modifier - .fillMaxWidth() - .height(50.dp) - .clip(CircleShape) - .background(buttonGradient), - colors = ButtonDefaults.buttonColors( - containerColor = Color.Transparent - ), - contentPadding = PaddingValues() - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center - ) { - Text( - text = "ENABLE LOCATION", - color = Color.White, - fontSize = 13.sp, - fontWeight = FontWeight.Bold, - letterSpacing = 0.5.sp - ) - Spacer(modifier = Modifier.width(8.dp)) - Icon( - imageVector = Icons.AutoMirrored.Filled.ArrowForward, - contentDescription = null, - tint = Color.White, - modifier = Modifier.size(16.dp) - ) - } - } - - Spacer(modifier = Modifier.height(4.dp)) - - TextButton( - onClick = onNotNowClick, - modifier = Modifier.height(40.dp) - ) { - Text( - text = "NOT NOW", - color = GradientStart, - fontSize = 13.sp, - fontWeight = FontWeight.Bold, - letterSpacing = 0.5.sp - ) - } - } - } -} - -@Composable -fun vibeFoundCard( - modifier: Modifier = Modifier -) { - val BubbleTailShape = GenericShape { size, _ -> - moveTo(0f, 0f) - lineTo(size.width, 0f) - lineTo(size.width / 2f, size.height) - close() - } - - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = modifier - ) { - Surface( - shape = RoundedCornerShape(28.dp), - color = Color.Transparent, - shadowElevation = 8.dp - ) { - Box( - modifier = Modifier - .background( - brush = Brush.verticalGradient( - colors = listOf( - Color(0xFFFBF8FE).copy(alpha = 0.95f), - Color(0xFFEFE8FA).copy(alpha = 0.95f) - ) - ) - ) - .border( - width = 1.dp, - color = Color.White.copy(alpha = 0.8f), - shape = RoundedCornerShape(28.dp) - ) - .padding(horizontal = 20.dp, vertical = 14.dp) - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center - ) { - Box( - contentAlignment = Alignment.BottomEnd, - ) { - Image( - painter = painterResource(id = R.drawable.locationpermissionbg), - contentDescription = "User Avatar", - contentScale = ContentScale.Crop, - modifier = Modifier - .size(30.dp) - .clip(CircleShape) - ) - - Box( - contentAlignment = Alignment.Center, - modifier = Modifier - .size(20.dp) - .offset(x = 2.dp, y = 2.dp) - .clip(CircleShape) - .background(Color(0xFF8A62D6)) - .border(1.5.dp, Color.White, CircleShape) - ) { - Icon( - imageVector = Icons.Default.Check, - contentDescription = "Verified", - tint = Color.White, - modifier = Modifier.size(12.dp) - ) - } - } - - Spacer(modifier = Modifier.width(16.dp)) - - Text( - text = "Vibe Found!", - fontWeight = FontWeight.Normal, - color = Color(0xFF4A154B), - letterSpacing = (-0.5).sp - ) - } - } - } - - Box( - modifier = Modifier - .offset(y = (-1).dp) - .size(width = 18.dp, height = 10.dp) - .background( - color = Color(0xFFEFE8FA).copy(alpha = 0.95f), - shape = BubbleTailShape - ) - ) - } -} - -@Preview(showBackground = true, showSystemUi = true) -@Composable -fun LocationPermissionScreenPreview() { - DattingAPPTheme { - LocationPermissionScreen( - onEnableLocationClick = {}, - onNotNowClick = {} - ) - } -} \ No newline at end of file diff --git a/app/src/main/java/com/example/dattingapp/permission/NotificationPermission.kt b/app/src/main/java/com/example/dattingapp/permission/NotificationPermission.kt deleted file mode 100644 index 320fb38..0000000 --- a/app/src/main/java/com/example/dattingapp/permission/NotificationPermission.kt +++ /dev/null @@ -1,274 +0,0 @@ -package com.example.dattingapp.permission - -import android.Manifest -import android.content.Intent -import android.os.Build -import android.os.Bundle -import android.widget.Toast -import androidx.activity.ComponentActivity -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.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -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.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.ArrowForward -import androidx.compose.material.icons.filled.Notifications -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.runtime.Composable -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.res.painterResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.example.dattingapp.Login.LoginActivity -import com.example.dattingapp.R -import com.example.dattingapp.ui.theme.DattingAPPTheme -import com.example.dattingapp.ui.theme.GradientEnd -import com.example.dattingapp.ui.theme.GradientStart - -class NotificationPermission : ComponentActivity() { - - private val prefs by lazy { - getSharedPreferences("app_preferences", MODE_PRIVATE) - } - - private val requestNotificationPermissionLauncher = - registerForActivityResult( - ActivityResultContracts.RequestPermission() - ) { isGranted -> - - // Mark this screen as completed - markPermissionScreenCompleted() - - // Go to next screen regardless of permission result - navigateToNextActivity() - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - - // Check if this screen was already completed - if (prefs.getBoolean("notification_screen_completed", false)) { - val intent = Intent(this, LoginActivity::class.java) - startActivity(intent) - finish() - return - } - - enableEdgeToEdge() - - setContent { - DattingAPPTheme { - NotificationPermissionScreen( - onEnableNotificationClick = { - requestNotificationPermission() - }, - onNotNowClick = { - // User doesn't want notification permission - markPermissionScreenCompleted() - navigateToNextActivity() - } - ) - } - } - } - - private fun requestNotificationPermission() { - - // Mark screen as completed immediately - // so it won't appear again - markPermissionScreenCompleted() - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { - - requestNotificationPermissionLauncher.launch( - Manifest.permission.POST_NOTIFICATIONS - ) - - } else { - navigateToNextActivity() - } - } - - private fun markPermissionScreenCompleted() { - prefs.edit() - .putBoolean("notification_screen_completed", true) - .apply() - } - - private fun navigateToNextActivity() { - val intent = Intent(this, GallaryPermission::class.java) - startActivity(intent) - finish() - } -} -// screen create -@Composable -fun NotificationPermissionScreen( - onEnableNotificationClick: () -> Unit, - onNotNowClick: () -> Unit, - modifier: Modifier = Modifier -) { - Box( - modifier = modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - Image( - painter = painterResource(id = R.drawable.notificationpermission), - contentDescription = null, - modifier = Modifier.fillMaxSize(), - contentScale = ContentScale.Crop - ) - NotificationPermissionCard( - onEnableNotificationClick = onEnableNotificationClick, - onNotNowClick = onNotNowClick - ) - } -} - -@Composable -private fun NotificationPermissionCard( - onEnableNotificationClick: () -> Unit, - onNotNowClick: () -> Unit, - modifier: Modifier = Modifier -) { - val buttonGradient = Brush.horizontalGradient( - colors = listOf(GradientStart, GradientEnd) - ) - Column( - modifier = modifier - .fillMaxSize() - .padding(horizontal = 24.dp, vertical = 50.dp), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.SpaceBetween - ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally - ) { - Box( - modifier = Modifier - .size(72.dp) - .clip(CircleShape) - .background(buttonGradient), - contentAlignment = Alignment.Center - ) { - Icon( - imageVector = Icons.Default.Notifications, - contentDescription = null, - tint = Color.White, - modifier = Modifier.size(36.dp) - ) - } - - Spacer(modifier = Modifier.height(16.dp)) - - Text( - text = "Discover Cultural\nVibes", - fontSize = 22.sp, - fontWeight = FontWeight.Bold, - color = Color.Black, - textAlign = TextAlign.Center, - lineHeight = 28.sp - ) - - Spacer(modifier = Modifier.height(12.dp)) - - Text( - text = "Find amazing people sharing the same space. Let's make a radiant connection at your favorite spots.", - fontSize = 17.sp, - fontWeight = FontWeight.Normal, - color = Color.Black, - textAlign = TextAlign.Center, - lineHeight = 18.sp, - modifier = Modifier.padding(horizontal = 4.dp) - ) - } - - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.fillMaxWidth() - ) { - Button( - onClick = onEnableNotificationClick, - modifier = Modifier - .fillMaxWidth() - .height(50.dp) - .clip(CircleShape) - .background(buttonGradient), - colors = ButtonDefaults.buttonColors( - containerColor = Color.Transparent - ), - contentPadding = PaddingValues() - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center - ) { - Text( - text = "ENABLE NOTIFICATIONS", - color = Color.White, - fontSize = 13.sp, - fontWeight = FontWeight.Bold, - letterSpacing = 0.5.sp - ) - Spacer(modifier = Modifier.width(8.dp)) - Icon( - imageVector = Icons.AutoMirrored.Filled.ArrowForward, - contentDescription = null, - tint = Color.White, - modifier = Modifier.size(16.dp) - ) - } - } - - Spacer(modifier = Modifier.height(4.dp)) - - TextButton( - onClick = onNotNowClick, - modifier = Modifier.height(40.dp) - ) { - Text( - text = "NOT NOW", - color = GradientStart, - fontSize = 13.sp, - fontWeight = FontWeight.Bold, - letterSpacing = 0.5.sp - ) - } - } - } -} - -@Preview(showBackground = true, showSystemUi = true) -@Composable -fun NotificationPermissionPreview() { - NotificationPermissionScreen( - onEnableNotificationClick = {}, - onNotNowClick = {} - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/example/dattingapp/permission/OtherAppPermission.kt b/app/src/main/java/com/example/dattingapp/permission/OtherAppPermission.kt deleted file mode 100644 index 9d17656..0000000 --- a/app/src/main/java/com/example/dattingapp/permission/OtherAppPermission.kt +++ /dev/null @@ -1,264 +0,0 @@ -package com.example.dattingapp.permission - -import android.content.Intent -import android.net.Uri -import android.os.Build -import android.os.Bundle -import android.provider.Settings -import androidx.activity.ComponentActivity -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.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -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.material.icons.Icons -import androidx.compose.material.icons.automirrored.filled.ArrowForward -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.runtime.Composable -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.res.painterResource -import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp -import com.example.dattingapp.Login.LoginActivity -import com.example.dattingapp.R -import com.example.dattingapp.ui.theme.DattingAPPTheme -import com.example.dattingapp.ui.theme.GradientEnd -import com.example.dattingapp.ui.theme.GradientStart -import com.example.dattingapp.ui.theme.whitecolor - -class OtherAppPermission : ComponentActivity() { - - private var wasSettingsOpened = false - - private val overlayPermissionLauncher = - registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { - checkAndNavigate() - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - enableEdgeToEdge() - setContent { - DattingAPPTheme { - OtherAppPermissionScreen( - onEnableOverlayClick = { - requestOverlayPermission() - }, - onNotNowClick = { - navigateToNextActivity() - } - ) - } - } - } - - override fun onResume() { - super.onResume() - // If returning from system settings, verify permission status - if (wasSettingsOpened) { - wasSettingsOpened = false - checkAndNavigate() - } - } - - private fun requestOverlayPermission() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (!Settings.canDrawOverlays(this)) { - wasSettingsOpened = true - try { - // Direct link to Floating Window settings - val intent = Intent( - Settings.ACTION_MANAGE_OVERLAY_PERMISSION, - Uri.parse("package:$packageName") - ) - overlayPermissionLauncher.launch(intent) - } catch (e: Exception) { - // Fallback for custom OS settings pages (Realme/OPPO/Xiaomi) - val fallbackIntent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION) - overlayPermissionLauncher.launch(fallbackIntent) - } - } else { - navigateToNextActivity() - } - } else { - navigateToNextActivity() - } - } - - private fun checkAndNavigate() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - if (Settings.canDrawOverlays(this)) { - navigateToNextActivity() - } - } else { - navigateToNextActivity() - } - } - - private fun navigateToNextActivity() { - val intent = Intent(this, LoginActivity::class.java).apply { - // Re-anchors app context preventing app auto-close on return - addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP) - } - startActivity(intent) - finish() - } -} - -@Composable -fun OtherAppPermissionScreen( - onEnableOverlayClick: () -> Unit, - onNotNowClick: () -> Unit, - modifier: Modifier = Modifier -) { - Box( - modifier = modifier.fillMaxSize(), - contentAlignment = Alignment.Center - ) { - Image( - painter = painterResource(id = R.drawable.otherpermission), - contentDescription = null, - modifier = Modifier.fillMaxSize(), - contentScale = ContentScale.Crop - ) - NotificationPermissionCard( - onEnableOverlayClick = onEnableOverlayClick, - onNotNowClick = onNotNowClick - ) - } -} - -@Composable -private fun NotificationPermissionCard( - onEnableOverlayClick: () -> Unit, - onNotNowClick: () -> Unit, - modifier: Modifier = Modifier -) { - val buttonGradient = Brush.horizontalGradient( - colors = listOf(GradientStart, GradientEnd) - ) - Column( - modifier = modifier - .fillMaxSize() - .padding( - start = 16.dp, - top = 140.dp, - end = 24.dp, - bottom = 60.dp - ), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.SpaceBetween - ) { - Column( - horizontalAlignment = Alignment.CenterHorizontally - ) { - Text( - text = "Discover Cultural\nVibes", - fontSize = 22.sp, - fontWeight = FontWeight.Bold, - color = whitecolor, - textAlign = TextAlign.Center, - lineHeight = 28.sp - ) - - Spacer(modifier = Modifier.height(12.dp)) - - Text( - text = "Find amazing people sharing the same space. Let's make a radiant connection at your favorite spots.", - fontSize = 13.sp, - fontWeight = FontWeight.Normal, - color = whitecolor, - textAlign = TextAlign.Center, - lineHeight = 18.sp, - modifier = Modifier.padding(horizontal = 4.dp) - ) - } - - Column( - horizontalAlignment = Alignment.CenterHorizontally, - modifier = Modifier.fillMaxWidth() - ) { - Button( - onClick = onEnableOverlayClick, - modifier = Modifier - .fillMaxWidth() - .height(50.dp) - .clip(CircleShape) - .background(buttonGradient), - colors = ButtonDefaults.buttonColors( - containerColor = Color.Transparent - ), - contentPadding = PaddingValues() - ) { - Row( - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Center - ) { - Text( - text = "ALLOW OVERLAY ACCESS", - color = Color.White, - fontSize = 13.sp, - fontWeight = FontWeight.Bold, - letterSpacing = 0.5.sp - ) - Spacer(modifier = Modifier.width(8.dp)) - Icon( - imageVector = Icons.AutoMirrored.Filled.ArrowForward, - contentDescription = null, - tint = Color.White, - modifier = Modifier.size(16.dp) - ) - } - } - - Spacer(modifier = Modifier.height(4.dp)) - - TextButton( - onClick = onNotNowClick, - modifier = Modifier.height(40.dp) - ) { - Text( - text = "NOT NOW", - color = GradientStart, - fontSize = 13.sp, - fontWeight = FontWeight.Bold, - letterSpacing = 0.5.sp - ) - } - } - } -} - -@Preview(showBackground = true, showSystemUi = true) -@Composable -fun OtherAppPermissionScreenPreview() { - OtherAppPermissionScreen( - onEnableOverlayClick = {}, - onNotNowClick = {} - ) -} \ No newline at end of file diff --git a/app/src/main/java/com/example/dattingapp/permission/PermissionHelper.kt b/app/src/main/java/com/example/dattingapp/permission/PermissionHelper.kt new file mode 100644 index 0000000..0e50ebc --- /dev/null +++ b/app/src/main/java/com/example/dattingapp/permission/PermissionHelper.kt @@ -0,0 +1,142 @@ +package com.example.dattingapp.permission + +import android.Manifest +import android.content.Context +import android.content.Intent +import android.content.pm.PackageManager +import android.net.Uri +import android.os.Build +import android.provider.Settings +import androidx.activity.ComponentActivity +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.content.ContextCompat + +class PermissionHelper(private val activity: ComponentActivity) { + + private lateinit var permissionLauncher: ActivityResultLauncher> + private lateinit var overlayLauncher: ActivityResultLauncher + + private var onResult: ((Boolean) -> Unit)? = null + + init { + registerLaunchers() + } + + private fun registerLaunchers() { + // Multi-permission launcher for standard runtime permissions + permissionLauncher = activity.registerForActivityResult( + ActivityResultContracts.RequestMultiplePermissions() + ) { permissions -> + // Check if all permissions requested are granted + val allGranted = permissions.entries.all { it.value } + onResult?.invoke(allGranted) + } + + // Launcher for special permissions (Draw Over Other Apps) + overlayLauncher = activity.registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { + val isGranted = isOverlayPermissionGranted() + onResult?.invoke(isGranted) + } + } + + /** + * Checks whether permissions for a given type are already granted. + */ + fun hasPermission(type: PermissionType): Boolean { + val permissions = getPermissionsForType(type) + if (permissions.isEmpty()) return true + + return permissions.all { permission -> + ContextCompat.checkSelfPermission( + activity, + permission + ) == PackageManager.PERMISSION_GRANTED + } + } + + /** + * Request standard runtime permissions: NOTIFICATION, GALLERY, AUDIO, or LOCATION + */ + fun requestPermissions(type: PermissionType, callback: (Boolean) -> Unit) { + this.onResult = callback + val permissionsToRequest = getPermissionsForType(type) + + if (permissionsToRequest.isNotEmpty()) { + permissionLauncher.launch(permissionsToRequest) + } else { + callback(true) + } + } + + /** + * Request Special Permission: Draw Over Other Apps + */ + fun requestOverlayPermission(callback: (Boolean) -> Unit) { + this.onResult = callback + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + if (!Settings.canDrawOverlays(activity)) { + val intent = Intent( + Settings.ACTION_MANAGE_OVERLAY_PERMISSION, + Uri.parse("package:${activity.packageName}") + ) + overlayLauncher.launch(intent) + } else { + callback(true) + } + } else { + callback(true) + } + } + + /** + * Check if Overlay Permission is currently granted without opening Settings + */ + fun isOverlayPermissionGranted(): Boolean { + return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + Settings.canDrawOverlays(activity) + } else { + true + } + } + + private fun getPermissionsForType(type: PermissionType): Array { + return when (type) { + PermissionType.NOTIFICATION -> { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + arrayOf(Manifest.permission.POST_NOTIFICATIONS) + } else { + emptyArray() + } + } + PermissionType.GALLERY -> { + when { + Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE -> arrayOf( + Manifest.permission.READ_MEDIA_IMAGES, + Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED + ) + Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU -> arrayOf( + Manifest.permission.READ_MEDIA_IMAGES + ) + else -> arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE) + } + } + PermissionType.AUDIO -> arrayOf(Manifest.permission.RECORD_AUDIO) + PermissionType.LOCATION -> arrayOf( + Manifest.permission.ACCESS_FINE_LOCATION, + Manifest.permission.ACCESS_COARSE_LOCATION + ) + PermissionType.OVERLAY -> emptyArray() + } + } + + enum class PermissionType { + NOTIFICATION, + GALLERY, + AUDIO, + LOCATION, + OVERLAY + } +} \ No newline at end of file diff --git a/app/src/main/java/com/example/dattingapp/registration/RegistrationScreen.kt b/app/src/main/java/com/example/dattingapp/registration/RegistrationScreen.kt index 836a3c1..0d49743 100644 --- a/app/src/main/java/com/example/dattingapp/registration/RegistrationScreen.kt +++ b/app/src/main/java/com/example/dattingapp/registration/RegistrationScreen.kt @@ -65,10 +65,15 @@ import java.util.Calendar import java.util.Date import java.util.Locale import androidx.compose.animation.core.* +import androidx.compose.material.icons.filled.CompassCalibration +import androidx.compose.material.icons.filled.FavoriteBorder +import androidx.compose.material.icons.filled.LocalBar +import androidx.compose.material.icons.filled.People import androidx.compose.material3.Icon import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.ui.draw.scale @@ -92,7 +97,7 @@ fun RegistrationFlowContainer( val uiState by registrationViewModel.uiState.collectAsState() var currentStep by remember { mutableIntStateOf(1) } - val totalSteps = 6 + val totalSteps = 7 BackHandler(enabled = currentStep > 1 && uiState !is RegistrationUiState.Loading) { currentStep-- } @@ -117,6 +122,7 @@ fun RegistrationFlowContainer( var selectedBirthDateMillis by rememberSaveable { mutableStateOf(null) } var addressInput by rememberSaveable { mutableStateOf("") } var selectedGender by rememberSaveable { mutableStateOf("") } + var selectedRealationStatus by remember { mutableStateOf(null) } val scrollState = rememberScrollState() @@ -217,6 +223,7 @@ fun RegistrationFlowContainer( 4 -> "Date of Birth" 5 -> "Location" 6 -> "Interested Gender" + 7-> "Relationship status" else -> "" }, color = Color.Gray, @@ -312,15 +319,20 @@ fun RegistrationFlowContainer( addressText = addressInput, onAddressChange = { addressInput = it } ) - 6 -> GenderCompose( + 6 -> VibeCheckScreen( + selectedRealationStatus = selectedRealationStatus ?: "", + onRelationSelected = { selectedRealationStatus = it } + ) + 7 -> GenderCompose( selectedGender = selectedGender, onGenderSelected = { selectedGender = it } ) + else -> Text("Step $step") } } - Spacer(modifier = Modifier.height(100.dp)) + Spacer(modifier = Modifier.height(50.dp)) // 6. Action Button Button( @@ -374,6 +386,13 @@ fun RegistrationFlowContainer( } else currentStep++ } 6 -> { + if (selectedRealationStatus.isNullOrEmpty()) { + Toast.makeText(context, "Please select your vibe", Toast.LENGTH_SHORT).show() + } else { + currentStep++ + } + } + 7 -> { if (selectedGender.isBlank()) { Toast.makeText(context, "Select gender interest", Toast.LENGTH_SHORT).show() return@Button @@ -1120,8 +1139,6 @@ data class GenderPreferenceOption( val icon: ImageVector ) - - @OptIn(ExperimentalMaterial3Api::class) @Composable fun BirthDayCompose( @@ -1185,7 +1202,8 @@ fun BirthDayCompose( // Input Card with Calendar Trigger Button Card( - modifier = Modifier.fillMaxWidth(), + modifier = Modifier.fillMaxWidth() + .clickable { showDatePickerDialog = true } , shape = RoundedCornerShape(20.dp), colors = CardDefaults.cardColors(containerColor = Color.White), border = BorderStroke( @@ -1204,14 +1222,17 @@ fun BirthDayCompose( Spacer(modifier = Modifier.height(12.dp)) // Date Display Field & Select Button + + Row( modifier = Modifier .fillMaxWidth() .background(InputBg, shape = RoundedCornerShape(12.dp)) + .clickable { showDatePickerDialog = true } // Added clickable modifier here .padding(horizontal = 12.dp, vertical = 8.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.SpaceBetween - ) { + ){ Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.weight(1f) @@ -1233,31 +1254,7 @@ fun BirthDayCompose( ) ) } - // Open Calendar Button - Button( - onClick = { showDatePickerDialog = true }, - colors = ButtonDefaults.buttonColors(containerColor = Color.Transparent), - contentPadding = PaddingValues(horizontal = 12.dp, vertical = 6.dp), - modifier = Modifier.background( - brush = Brush.horizontalGradient(listOf(PinkGradientStart, PurpleGradientEnd)), - shape = RoundedCornerShape(8.dp) - ) - ) { - Icon( - imageVector = Icons.Default.CalendarToday, - contentDescription = "Open Calendar", - tint = Color.White, - modifier = Modifier.size(16.dp) - ) - Spacer(modifier = Modifier.width(6.dp)) - Text( - text = "Choose", - color = Color.White, - fontSize = 12.sp, - fontWeight = FontWeight.Bold - ) - } } // Display Age Badge or Error Message @@ -1335,10 +1332,6 @@ fun BirthDayCompose( } } - - - - @Composable fun GenderCompose( selectedGender: String = "", @@ -1501,6 +1494,183 @@ fun UniquePulseLoader( } } +data class VibeOption( + val title: String, + val description: String, + val icon: ImageVector, + val iconBgColor: Color, + val iconTint: Color +) + +@Composable +fun VibeCheckScreen( + selectedRealationStatus: String = "", + onRelationSelected: (String) -> Unit = {} +) { + val vibeOptions = remember { + listOf( + VibeOption( + title = "Serious Relationship", + description = "Looking for the one", + icon = Icons.Default.FavoriteBorder, + iconBgColor = Color(0xFFFFE3E3), + iconTint = Color(0xFFC2185B) + ), + VibeOption( + title = "Casual Dates", + description = "Drinks & good times", + icon = Icons.Default.LocalBar, + iconBgColor = Color(0xFFEDE7F6), + iconTint = Color(0xFF673AB7) + ), + VibeOption( + title = "New Friends", + description = "Expand my circle", + icon = Icons.Default.People, + iconBgColor = Color(0xFFFFE0B2), + iconTint = Color(0xFFE65100) + ), + VibeOption( + title = "Marriage", + description = "Seeing where things go", + icon = Icons.Default.CompassCalibration, + iconBgColor = Color(0xFFE0E0E0), + iconTint = Color(0xFF424242) + ) + ) + } + + val backgroundBrush = Brush.verticalGradient( + colors = listOf( + Color(0xFFFFF0F3), + Color(0xFFFFFFFF), + Color(0xFFFFF0F5) + ) + ) + + Box( + modifier = Modifier + .fillMaxSize() + .background(backgroundBrush) + .padding(horizontal = 24.dp, vertical = 32.dp) + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally, + modifier = Modifier.fillMaxWidth() + ) { + Text( + text = "VibeCheck", + fontSize = 24.sp, + fontWeight = FontWeight.Bold, + color = Color(0xFFB0003A) + ) + + Spacer(modifier = Modifier.height(32.dp)) + + Text( + text = "What's your vibe?", + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + color = Color(0xFF1E293B) + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = "Select your main goal right now to help us find theright connections.", + fontSize = 13.sp, + color = Color(0xFF64748B), + textAlign = TextAlign.Center, + lineHeight = 18.sp + ) + + Spacer(modifier = Modifier.height(48.dp)) + + Column( + verticalArrangement = Arrangement.spacedBy(16.dp), + modifier = Modifier.fillMaxWidth() + ) { + vibeOptions.forEach { option -> + val isSelected = option.title == selectedRealationStatus + + VibeCard( + option = option, + isSelected = isSelected, + onClick = { + onRelationSelected(option.title) + } + ) + } + } + } + } +} + +@Composable +fun VibeCard( + option: VibeOption, + isSelected: Boolean, + onClick: () -> Unit +) { + val borderColor = if (isSelected) Color(0xFFB0003A) else Color(0xFFFFE4E6) + val borderWidth = if (isSelected) 2.dp else 1.dp + + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier + .fillMaxWidth() + .clip(RoundedCornerShape(16.dp)) + .background(Color.White) + .border( + width = borderWidth, + color = borderColor, + shape = RoundedCornerShape(16.dp) + ) + .clickable { onClick() } + .padding(horizontal = 16.dp, vertical = 14.dp) + ) { + // Rounded Icon Container + Box( + contentAlignment = Alignment.Center, + modifier = Modifier + .size(44.dp) + .clip(CircleShape) + .background(option.iconBgColor) + ) { + Icon( + imageVector = option.icon, + contentDescription = option.title, + tint = option.iconTint, + modifier = Modifier.size(22.dp) + ) + } + + Spacer(modifier = Modifier.width(16.dp)) + + // Label and Description + Column { + Text( + text = option.title, + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + color = Color(0xFF1E293B) + ) + Spacer(modifier = Modifier.height(2.dp)) + Text( + text = option.description, + fontSize = 12.sp, + color = Color(0xFF64748B) + ) + } + } +} + +@Preview(showBackground = true, showSystemUi = true) +@Composable +fun VibeCheckScreenPreview() { + VibeCheckScreen() +} + // 1. Email Tab Preview (Normal State) @Preview(showBackground = true, name = "Step Two - Email Tab") @Composable diff --git a/app/src/main/java/com/example/dattingapp/ui/theme/Color.kt b/app/src/main/java/com/example/dattingapp/ui/theme/Color.kt index 51dc96b..6c0d11d 100644 --- a/app/src/main/java/com/example/dattingapp/ui/theme/Color.kt +++ b/app/src/main/java/com/example/dattingapp/ui/theme/Color.kt @@ -96,3 +96,18 @@ val LightPinkBg = Color(0xFFFDE8E8) val LightBlueBg = Color(0xFFEEF5FF) val LightPurpleBg = Color(0xFFF5EEFF) val DarkOverlay = Color(0xCC212529) + +// Color Palette matching the UI +val HeaderTitleColor = Color(0xFFAD004B) +val SenderBubbleBg = Color(0xFFECEFF1) +val RecipientBubbleBg = Color(0xFFC2185B) +val AppBackground = Color(0xFFF9F8FD) +val SubtextColor = Color(0xFFB0BEC5) +val InputFieldBg = Color(0xFFF0EFF4) + +// Color Palette matching the UI +val ScreenBackground = Color(0xFFFAF9FD) +val SectionHeaderColor = Color(0xFF757575) +val CardBackgroundColor = Color.White +val LinkTextColor = Color(0xFF7B1FA2) +val UnreadDotColor = Color(0xFFD32F2F) \ No newline at end of file