@@ -2,56 +2,94 @@ package com.example.intra
22
33import android.content.Intent
44import android.net.Uri
5+ import android.os.Build
56import android.util.Log
7+ import android.util.Patterns
8+ import androidx.compose.animation.core.LinearEasing
9+ import androidx.compose.animation.core.RepeatMode
10+ import androidx.compose.animation.core.animateFloat
11+ import androidx.compose.animation.core.infiniteRepeatable
12+ import androidx.compose.animation.core.rememberInfiniteTransition
13+ import androidx.compose.animation.core.tween
614import androidx.compose.foundation.background
7- import androidx.compose.foundation.layout.*
15+ import androidx.compose.foundation.border
16+ import androidx.compose.foundation.clickable
17+ import androidx.compose.foundation.isSystemInDarkTheme
18+ import androidx.compose.foundation.layout.Arrangement
19+ import androidx.compose.foundation.layout.Box
20+ import androidx.compose.foundation.layout.Column
21+ import androidx.compose.foundation.layout.PaddingValues
22+ import androidx.compose.foundation.layout.Row
23+ import androidx.compose.foundation.layout.Spacer
24+ import androidx.compose.foundation.layout.fillMaxSize
25+ import androidx.compose.foundation.layout.fillMaxWidth
26+ import androidx.compose.foundation.layout.height
27+ import androidx.compose.foundation.layout.heightIn
28+ import androidx.compose.foundation.layout.padding
29+ import androidx.compose.foundation.layout.size
30+ import androidx.compose.foundation.layout.width
31+ import androidx.compose.foundation.layout.widthIn
832import androidx.compose.foundation.lazy.LazyColumn
933import androidx.compose.foundation.lazy.items
1034import androidx.compose.foundation.lazy.rememberLazyListState
1135import androidx.compose.foundation.shape.CircleShape
1236import androidx.compose.foundation.shape.RoundedCornerShape
37+ import androidx.compose.foundation.text.ClickableText
1338import androidx.compose.material.icons.Icons
1439import androidx.compose.material.icons.automirrored.filled.ArrowBack
1540import androidx.compose.material.icons.filled.AttachFile
16- import androidx.compose.material.icons.filled.Send
41+ import androidx.compose.material.icons.filled.Call
1742import androidx.compose.material.icons.filled.Download
1843import androidx.compose.material.icons.filled.Image
19- import androidx.compose.material.icons.filled.VideoLibrary
2044import androidx.compose.material.icons.filled.InsertDriveFile
21- import androidx.compose.material3.*
22- import androidx.compose.runtime.*
45+ import androidx.compose.material.icons.filled.PlayCircle
46+ import androidx.compose.material.icons.filled.Send
47+ import androidx.compose.material.icons.filled.VideoLibrary
48+ import androidx.compose.material3.Button
49+ import androidx.compose.material3.ButtonDefaults
50+ import androidx.compose.material3.Card
51+ import androidx.compose.material3.CardDefaults
52+ import androidx.compose.material3.ExperimentalMaterial3Api
53+ import androidx.compose.material3.Icon
54+ import androidx.compose.material3.IconButton
55+ import androidx.compose.material3.LocalTextStyle
56+ import androidx.compose.material3.MaterialTheme
57+ import androidx.compose.material3.OutlinedTextField
58+ import androidx.compose.material3.Scaffold
59+ import androidx.compose.material3.Text
60+ import androidx.compose.material3.TopAppBar
61+ import androidx.compose.runtime.Composable
62+ import androidx.compose.runtime.DisposableEffect
63+ import androidx.compose.runtime.LaunchedEffect
64+ import androidx.compose.runtime.SideEffect
65+ import androidx.compose.runtime.getValue
66+ import androidx.compose.runtime.mutableStateOf
67+ import androidx.compose.runtime.remember
68+ import androidx.compose.runtime.setValue
2369import androidx.compose.ui.Alignment
2470import androidx.compose.ui.Modifier
71+ import androidx.compose.ui.draw.alpha
72+ import androidx.compose.ui.draw.clip
73+ import androidx.compose.ui.draw.rotate
2574import androidx.compose.ui.graphics.Color
75+ import androidx.compose.ui.graphics.toArgb
76+ import androidx.compose.ui.layout.ContentScale
2677import androidx.compose.ui.platform.LocalContext
27- import androidx.compose.ui.text.font.FontStyle
28- import androidx.compose.material.icons.filled.Call
29- import androidx.compose.ui.unit.dp
30- import androidx.compose.ui.unit.sp
31- import androidx.compose.ui.draw.clip
32- import androidx.compose.foundation.text.ClickableText
78+ import androidx.compose.ui.platform.LocalView
3379import androidx.compose.ui.text.SpanStyle
3480import androidx.compose.ui.text.buildAnnotatedString
81+ import androidx.compose.ui.text.font.FontStyle
3582import androidx.compose.ui.text.style.TextDecoration
36- import android.util.Patterns
37- import androidx.compose.ui.layout.ContentScale
38- import kotlinx.coroutines.delay
39- import java.text.SimpleDateFormat
40- import java.util.*
83+ import androidx.compose.ui.unit.dp
84+ import androidx.compose.ui.unit.sp
85+ import androidx.core.view.WindowCompat
4186import coil.compose.AsyncImage
4287import coil.request.ImageRequest
43- import androidx.compose.animation.core.*
44- import androidx.compose.foundation.border
45- import androidx.compose.material.icons.filled.PlayCircle
46- import androidx.compose.ui.draw.rotate
47- import androidx.compose.ui.graphics.RectangleShape
48- import androidx.compose.ui.draw.alpha
4988import coil.request.videoFrameMillis
50- import androidx.compose.ui.graphics.toArgb
51- import androidx.compose.ui.platform.LocalView
52- import androidx.core.view.WindowCompat
53- import androidx.compose.foundation.isSystemInDarkTheme
54- import android.os.Build
89+ import java.text.SimpleDateFormat
90+ import java.util.Date
91+ import java.util.Locale
92+ import kotlinx.coroutines.delay
5593
5694
5795@OptIn(ExperimentalMaterial3Api ::class )
@@ -64,12 +102,13 @@ fun ChatScreen(
64102 onStartCall : () -> Unit ,
65103) {
66104 val listState = rememberLazyListState()
105+ var videoUrlToPlay by remember { mutableStateOf<String ?>(null ) }
106+ var imageUrlToView by remember { mutableStateOf<String ?>(null ) } // 👈 YE ADD HUA
67107
68108 // Set Status Bar Color
69109 val view = LocalView .current
70110 val isDark = isSystemInDarkTheme()
71111 val backgroundColor = MaterialTheme .colorScheme.background
72- val primaryDarkColor = Color (0xFF512DA8 ) // Dark Purple (Aapka theme color)
73112
74113 if (! view.isInEditMode) {
75114 SideEffect {
@@ -80,8 +119,8 @@ fun ChatScreen(
80119 window.statusBarColor = backgroundColor.toArgb()
81120 insetsController.isAppearanceLightStatusBars = ! isDark
82121 } else {
83- if (! isDark) {
84- window.statusBarColor = Color .Black .toArgb()
122+ if (! isDark) {
123+ window.statusBarColor = Color .Black .toArgb()
85124 } else {
86125 window.statusBarColor = backgroundColor.toArgb()
87126 }
@@ -118,6 +157,22 @@ fun ChatScreen(
118157 }
119158 }
120159
160+ if (videoUrlToPlay != null ) {
161+ VideoPlayerDialog (
162+ videoUrl = videoUrlToPlay!! ,
163+ onDismiss = { videoUrlToPlay = null }
164+ )
165+ }
166+
167+ // 👈 YE CODE ADD HUA:
168+ if (imageUrlToView != null ) {
169+ ImageViewerDialog (
170+ imageUrl = imageUrlToView!! ,
171+ onDismiss = { imageUrlToView = null }
172+ )
173+ }
174+
175+
121176 Scaffold (
122177 topBar = {
123178 TopAppBar (
@@ -163,7 +218,6 @@ fun ChatScreen(
163218 .padding(padding)
164219 .background(MaterialTheme .colorScheme.background)
165220 ) {
166- // 🔥 FIX 3: Messages bottom se start honge (reverseLayout hataya)
167221 LazyColumn (
168222 state = listState,
169223 modifier = Modifier .weight(1f ).fillMaxWidth(),
@@ -175,10 +229,14 @@ fun ChatScreen(
175229 )
176230 ) {
177231 items(viewModel.messages) { msg ->
178- MessageBubble (msg)
232+ // 👈 YE LINE UPDATE HUI
233+ MessageBubble (
234+ message = msg,
235+ onVideoClick = { url -> videoUrlToPlay = url },
236+ onImageClick = { url -> imageUrlToView = url }
237+ )
179238 }
180239
181- // Typing Indicator inside the list
182240 if (isTyping) {
183241 item {
184242 TypingIndicatorUI (receiverName)
@@ -274,7 +332,11 @@ fun TypingIndicatorUI(name: String) {
274332}
275333
276334@Composable
277- fun MessageBubble (message : ChatMessage ) {
335+ fun MessageBubble (
336+ message : ChatMessage ,
337+ onVideoClick : (String ) -> Unit = {},
338+ onImageClick : (String ) -> Unit = {} // 👈 YE ADD HUA
339+ ) {
278340 val context = LocalContext .current
279341
280342 Row (
@@ -296,42 +358,31 @@ fun MessageBubble(message: ChatMessage) {
296358 ) {
297359 Column (modifier = Modifier .padding(10 .dp)) {
298360
299- // File extensions detect karo
300361 val fileName = message.fileName ? : " File"
301362 val fileExtension = fileName.substringAfterLast(" ." , " " ).lowercase()
302363
303364 val isImage = fileExtension in listOf (" jpg" , " jpeg" , " png" , " gif" , " webp" )
304- // 🔥 Video detect karne ke liye
305365 val isVideo = fileExtension in listOf (" mp4" , " mkv" , " avi" , " mov" , " webm" )
306366
307- // ==========================================
308- // CASE 1: UPLOADING STAGE (Loader + Thumbnail)
309- // ==========================================
310367 if (message.isLoading) {
311-
312- // Agar Image YA Video hai, toh thumbnail dikhao
313- if (message.localUri != null && (isImage || isVideo)) { // 👈 '|| isVideo' add kiya
368+ if (message.localUri != null && (isImage || isVideo)) {
314369 Box (contentAlignment = Alignment .Center ) {
315- // 1. Thumbnail
316370 AsyncImage (
317371 model = ImageRequest .Builder (context)
318- .data(message.localUri) // Video path
372+ .data(message.localUri)
319373 .crossfade(true )
320374 .build(),
321375 contentDescription = " Uploading Preview" ,
322376 modifier = Modifier
323377 .fillMaxWidth()
324378 .heightIn(max = 200 .dp)
325379 .clip(RoundedCornerShape (8 .dp))
326- .alpha(0.6f ), // Thoda dhundhla taaki loader dikhe
380+ .alpha(0.6f ),
327381 contentScale = ContentScale .Crop
328382 )
329-
330- // 2. 🔥 Aapka Favorite Square Loader (Center mein)
331383 UniqueLoader ()
332384 }
333385 } else {
334- // Non-media file uploading (Doc/PDF etc)
335386 Row (verticalAlignment = Alignment .CenterVertically ) {
336387 UniqueLoader (Modifier .size(24 .dp))
337388 Spacer (Modifier .width(8 .dp))
@@ -342,31 +393,32 @@ fun MessageBubble(message: ChatMessage) {
342393 )
343394 }
344395 }
345- }
396+ } else if (message.type == " file " && message.fileUrl != null ) {
346397
347- // ==========================================
348- // CASE 2: FILE SENT / RECEIVED (Thumbnail + Play Icon)
349- // ==========================================
350- else if (message.type == " file" && message.fileUrl != null ) {
351-
352- // Agar Image YA Video hai
353- if (isImage || isVideo) { // 👈 Yahan bhi Video allow kiya
398+ if (isImage || isVideo) {
354399
355400 val settingsManager = remember { SettingsManager (context) }
356401 val baseUrl = settingsManager.getBaseUrl().removeSuffix(" /" )
357402
358- // URL banao
359403 val fullUrl = if (message.fileUrl.startsWith(" http" ))
360404 message.fileUrl
361405 else
362406 baseUrl + message.fileUrl
363407
364- Box (contentAlignment = Alignment .Center ) {
365- // 1. Thumbnail Load karo
408+ Box (
409+ contentAlignment = Alignment .Center ,
410+ modifier = Modifier .clickable {
411+ if (isVideo) {
412+ onVideoClick(fullUrl)
413+ } else if (isImage) {
414+ onImageClick(fullUrl)
415+ }
416+ }
417+ ) {
366418 AsyncImage (
367419 model = ImageRequest .Builder (context)
368420 .data(fullUrl)
369- .videoFrameMillis(2000 ) // 👈 Video ke 2nd second ka frame lega (better thumbnail)
421+ .videoFrameMillis(2000 )
370422 .crossfade(true )
371423 .build(),
372424 contentDescription = fileName,
@@ -377,10 +429,9 @@ fun MessageBubble(message: ChatMessage) {
377429 contentScale = ContentScale .Crop
378430 )
379431
380- // 2. 🔥 Video hai toh PLAY icon dikhao
381432 if (isVideo) {
382433 Icon (
383- imageVector = Icons .Default .PlayCircle , // Ensure icon import
434+ imageVector = Icons .Default .PlayCircle ,
384435 contentDescription = " Play" ,
385436 tint = Color .White .copy(alpha = 0.8f ),
386437 modifier = Modifier .size(48 .dp)
@@ -392,12 +443,10 @@ fun MessageBubble(message: ChatMessage) {
392443 Spacer (Modifier .height(6 .dp))
393444 }
394445
395- // 📁 File Info Row
396446 Row (
397447 verticalAlignment = Alignment .CenterVertically ,
398448 modifier = Modifier .fillMaxWidth()
399449 ) {
400- // File Icon based on type
401450 Icon (
402451 imageVector = when {
403452 isImage -> Icons .Default .Image
@@ -427,7 +476,6 @@ fun MessageBubble(message: ChatMessage) {
427476
428477 Spacer (Modifier .height(8 .dp))
429478
430- // 🔥 Modern Action Button
431479 Button (
432480 onClick = {
433481 try {
@@ -437,9 +485,16 @@ fun MessageBubble(message: ChatMessage) {
437485 message.fileUrl
438486 else
439487 baseUrl + message.fileUrl
440-
441- val intent = Intent (Intent .ACTION_VIEW , Uri .parse(finalUrl))
442- context.startActivity(intent)
488+
489+ // 👈 YE LOGIC UPDATE HUA
490+ if (isVideo) {
491+ onVideoClick(finalUrl)
492+ } else if (isImage) {
493+ onImageClick(finalUrl)
494+ } else {
495+ val intent = Intent (Intent .ACTION_VIEW , Uri .parse(finalUrl))
496+ context.startActivity(intent)
497+ }
443498 } catch (e: Exception ) {
444499 Log .e(" Chat" , " File open error" , e)
445500 }
@@ -465,7 +520,6 @@ fun MessageBubble(message: ChatMessage) {
465520 }
466521
467522 } else {
468- // 💬 TEXT MESSAGE (With Clickable Links)
469523 val textColor = if (message.isSelf)
470524 MaterialTheme .colorScheme.onPrimary
471525 else
@@ -514,8 +568,7 @@ fun MessageBubble(message: ChatMessage) {
514568 }
515569 )
516570 }
517-
518- // 🕒 TIMESTAMP
571+
519572 message.timestamp?.let {
520573 Spacer (Modifier .height(4 .dp))
521574 Text (
0 commit comments