15 changed files with 1747 additions and 1199 deletions
@ -1,27 +1,616 @@ |
|||
package com.example.dattingapp |
|||
|
|||
import android.os.Bundle |
|||
import androidx.activity.ComponentActivity |
|||
import androidx.activity.compose.setContent |
|||
import androidx.activity.enableEdgeToEdge |
|||
import androidx.compose.foundation.layout.fillMaxSize |
|||
import androidx.compose.foundation.layout.padding |
|||
import androidx.compose.material3.Scaffold |
|||
import androidx.compose.material3.Text |
|||
import androidx.compose.foundation.BorderStroke |
|||
import androidx.compose.foundation.background |
|||
import androidx.compose.foundation.border |
|||
import androidx.compose.foundation.layout.* |
|||
import androidx.compose.foundation.lazy.LazyColumn |
|||
import androidx.compose.foundation.lazy.LazyRow |
|||
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.filled.* |
|||
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.Color |
|||
import androidx.compose.ui.text.font.FontWeight |
|||
import androidx.compose.ui.tooling.preview.Preview |
|||
import com.example.dattingapp.ui.theme.DattingAPPTheme |
|||
import androidx.compose.ui.unit.dp |
|||
import androidx.compose.ui.unit.sp |
|||
|
|||
class ChatScreen : ComponentActivity() { |
|||
override fun onCreate(savedInstanceState: Bundle?) { |
|||
super.onCreate(savedInstanceState) |
|||
enableEdgeToEdge() |
|||
setContent { |
|||
DattingAPPTheme { |
|||
// Data Models |
|||
data class ActiveSpark( |
|||
val name: String, |
|||
val isUser: Boolean = false |
|||
) |
|||
|
|||
data class ChatMessage( |
|||
val id: Int, |
|||
val name: String, |
|||
val age: Int, |
|||
val time: String, |
|||
val isVerified: Boolean = false, |
|||
val matchPercentage: Int? = null, |
|||
val messageText: String? = null, |
|||
val isVoiceNote: Boolean = false, |
|||
val voiceDuration: String? = null, |
|||
val isPhoto: Boolean = false, |
|||
val photoText: String? = null, |
|||
val isAiBot: Boolean = false, |
|||
val aiTag: String? = null, |
|||
val unreadCount: Int = 0, |
|||
val isPinned: Boolean = false, |
|||
val isOnline: Boolean = false |
|||
) |
|||
|
|||
@OptIn(ExperimentalMaterial3Api::class) |
|||
@Composable |
|||
fun ChatScreen(modifier: Modifier = Modifier) { |
|||
Scaffold( |
|||
topBar = { |
|||
Column( |
|||
modifier = Modifier |
|||
.fillMaxWidth() |
|||
.background(Color.White) |
|||
.statusBarsPadding() |
|||
) { |
|||
// Header |
|||
Row( |
|||
modifier = Modifier.fillMaxWidth(), |
|||
horizontalArrangement = Arrangement.SpaceBetween, |
|||
verticalAlignment = Alignment.CenterVertically |
|||
) { |
|||
Row( |
|||
verticalAlignment = Alignment.CenterVertically, |
|||
horizontalArrangement = Arrangement.spacedBy(8.dp) |
|||
) { |
|||
Row( |
|||
verticalAlignment = Alignment.CenterVertically, |
|||
horizontalArrangement = Arrangement.spacedBy(8.dp) |
|||
) { |
|||
Text( |
|||
"Messages", |
|||
fontSize = 22.sp, |
|||
fontWeight = FontWeight.Bold |
|||
) |
|||
Surface( |
|||
color = Color(0xFFE91E63), |
|||
shape = RoundedCornerShape(12.dp) |
|||
) { |
|||
Text( |
|||
"3 new", |
|||
color = Color.White, |
|||
fontSize = 11.sp, |
|||
fontWeight = FontWeight.Bold, |
|||
modifier = Modifier.padding(horizontal = 8.dp, vertical = 2.dp) |
|||
) |
|||
} |
|||
} |
|||
} |
|||
|
|||
Row( |
|||
horizontalArrangement = Arrangement.spacedBy(8.dp), |
|||
verticalAlignment = Alignment.CenterVertically |
|||
) { |
|||
IconButton( |
|||
onClick = {}, |
|||
modifier = Modifier |
|||
.size(36.dp) |
|||
.background(Color(0xFFF5F5F5), CircleShape) |
|||
) { |
|||
Icon( |
|||
imageVector = Icons.Default.Shield, |
|||
contentDescription = "Safety", |
|||
tint = Color.Gray, |
|||
modifier = Modifier.size(18.dp) |
|||
) |
|||
} |
|||
IconButton( |
|||
onClick = {}, |
|||
modifier = Modifier |
|||
.size(36.dp) |
|||
.background(Color(0xFFF5F5F5), CircleShape) |
|||
) { |
|||
Icon( |
|||
imageVector = Icons.Default.Search, |
|||
contentDescription = "Search", |
|||
tint = Color.Gray, |
|||
modifier = Modifier.size(18.dp) |
|||
) |
|||
} |
|||
Box( |
|||
modifier = Modifier |
|||
.size(36.dp) |
|||
.clip(CircleShape) |
|||
.background(Color(0xFFCE93D8)) |
|||
) |
|||
} |
|||
} |
|||
|
|||
Spacer(modifier = Modifier.height(12.dp)) |
|||
|
|||
// Messages Title Bar |
|||
|
|||
|
|||
} |
|||
}, |
|||
modifier = modifier.fillMaxSize() |
|||
) { innerPadding -> |
|||
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() |
|||
} |
|||
} |
|||
@ -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<String>, |
|||
val imageUrl: String, |
|||
val hasUnread: Boolean = false |
|||
) |
|||
|
|||
@Composable |
|||
fun ProfileGrid(profiles: List<Profile>, 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) |
|||
} |
|||
@ -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() |
|||
} |
|||
@ -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 = {} |
|||
) |
|||
} |
|||
} |
|||
@ -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 = {} |
|||
) |
|||
} |
|||
} |
|||
@ -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 = {} |
|||
) |
|||
} |
|||
@ -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 = {} |
|||
) |
|||
} |
|||
@ -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<Array<String>> |
|||
private lateinit var overlayLauncher: ActivityResultLauncher<Intent> |
|||
|
|||
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<String> { |
|||
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 |
|||
} |
|||
} |
|||
Loading…
Reference in new issue