package com.example import android.Manifest import android.content.Intent import android.os.Build import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.BackHandler import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.setContent import androidx.activity.enableEdgeToEdge import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.annotation.OptIn import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.slideInVertically import androidx.compose.animation.slideOutVertically import androidx.compose.foundation.background import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Favorite import androidx.compose.material.icons.filled.Folder import androidx.compose.material.icons.filled.History import androidx.compose.material.icons.filled.MusicNote import androidx.compose.material.icons.filled.PlayArrow import androidx.compose.material.icons.filled.QueueMusic import androidx.compose.material.icons.filled.Settings import androidx.compose.material.icons.filled.SlowMotionVideo import androidx.compose.material.icons.filled.Videocam import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.NavigationBarItemDefaults import androidx.compose.material3.Scaffold import androidx.compose.material3.ScrollableTabRow import androidx.compose.material3.Tab import androidx.compose.material3.TabRowDefaults import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.platform.testTag import androidx.compose.ui.text.font.FontWeight import androidx.compose.foundation.layout.Row import androidx.compose.ui.Alignment import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.media3.common.util.UnstableApi import com.example.data.model.MediaItemData import com.example.playback.PlaybackManager import com.example.ui.components.AddToPlaylistDialog import com.example.ui.components.CreatePlaylistDialog import com.example.ui.components.FullAudioPlayerSheet import com.example.ui.components.MediaDetailDialog import com.example.ui.components.MiniPlayerBar import com.example.ui.navigation.Screen import com.example.ui.screens.FavoritesScreen import com.example.ui.screens.FoldersScreen import com.example.ui.screens.HistoryScreen import com.example.ui.screens.MusicLibraryScreen import com.example.ui.screens.PlaylistsScreen import com.example.ui.screens.QueueScreen import com.example.ui.screens.SettingsScreen import com.example.ui.screens.SwipeFeedScreen import com.example.ui.screens.VideoLibraryScreen import com.example.ui.theme.AccentViolet import com.example.ui.theme.AmoledBlack import com.example.ui.theme.DividerSubtle import com.example.ui.theme.SurfaceElevated import com.example.ui.theme.SurfaceSecondary import com.example.ui.theme.SwipePlayTheme import com.example.ui.theme.TextPrimary import com.example.ui.theme.TextSecondary import com.example.ui.viewmodel.MainViewModel @UnstableApi class MainActivity : ComponentActivity() { private val viewModel: MainViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() // Handle "Open With" intent intent?.data?.let { uri -> viewModel.importMediaUri(uri) } setContent { SwipePlayTheme { SwipePlayApp(viewModel = viewModel) } } } override fun onNewIntent(intent: Intent) { super.onNewIntent(intent) intent.data?.let { uri -> viewModel.importMediaUri(uri) } } override fun onPause() { super.onPause() PlaybackManager.saveCurrentPosition() } override fun onStop() { super.onStop() PlaybackManager.saveCurrentPosition() } } @OptIn(UnstableApi::class) @Composable fun SwipePlayApp(viewModel: MainViewModel) { val currentScreen by viewModel.currentScreen.collectAsState() val currentPlayingItem by PlaybackManager.currentMediaItem.collectAsState() val isAudioSheetVisible by viewModel.showAudioPlayerSheet.collectAsState() val mediaDetailsItem by viewModel.mediaDetailsItem.collectAsState() val playlists by viewModel.playlists.collectAsState() val favorites by viewModel.favorites.collectAsState() var itemForPlaylistDialog by remember { mutableStateOf(null) } var showCreatePlaylistDialog by remember { mutableStateOf(false) } // Permissions launcher val permissionsToRequest = remember { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { arrayOf( Manifest.permission.READ_MEDIA_AUDIO, Manifest.permission.READ_MEDIA_VIDEO, Manifest.permission.POST_NOTIFICATIONS ) } else { arrayOf( Manifest.permission.READ_EXTERNAL_STORAGE ) } } val permissionLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.RequestMultiplePermissions() ) { permissions -> val anyGranted = permissions.values.any { it } if (anyGranted) { viewModel.scanMedia() } } LaunchedEffect(Unit) { permissionLauncher.launch(permissionsToRequest) } // SAF File Picker val filePickerLauncher = rememberLauncherForActivityResult( contract = ActivityResultContracts.OpenMultipleDocuments() ) { uris -> uris.forEach { uri -> viewModel.importMediaUri(uri) } } // Custom Back Handling: If not on Feed or in a sub-view, go back to Feed BackHandler(enabled = currentScreen != Screen.SwipeFeed || isAudioSheetVisible) { if (isAudioSheetVisible) { viewModel.showAudioPlayer(false) } else if (viewModel.selectedPlaylistId.value != null) { viewModel.selectPlaylist(null) } else if (viewModel.activeFolder.value != null) { viewModel.setActiveFolder(null) } else { viewModel.navigateTo(Screen.SwipeFeed) } } val navItems = listOf( NavDestination(Screen.SwipeFeed, "Feed", Icons.Default.SlowMotionVideo), NavDestination(Screen.MusicLibrary, "Music", Icons.Default.MusicNote), NavDestination(Screen.VideoLibrary, "Videos", Icons.Default.Videocam), NavDestination(Screen.Folders, "Folders", Icons.Default.Folder), NavDestination(Screen.Playlists, "Playlists", Icons.Default.QueueMusic), NavDestination(Screen.Favorites, "Favorites", Icons.Default.Favorite), NavDestination(Screen.Queue, "Queue", Icons.Default.PlayArrow), NavDestination(Screen.History, "History", Icons.Default.History), NavDestination(Screen.Settings, "Settings", Icons.Default.Settings) ) Scaffold( modifier = Modifier .fillMaxSize() .background(AmoledBlack), containerColor = AmoledBlack, bottomBar = { Column( modifier = Modifier .fillMaxWidth() .background(AmoledBlack) ) { // Mini player bar if playing and not on SwipeFeed if (currentPlayingItem != null && currentScreen != Screen.SwipeFeed && !isAudioSheetVisible) { MiniPlayerBar( mediaItem = currentPlayingItem!!, onClick = { if (currentPlayingItem!!.isVideo) { viewModel.navigateTo(Screen.SwipeFeed) } else { viewModel.showAudioPlayer(true) } } ) } // Compact Horizontal Scrolling AMOLED Navigation Bar val selectedIndex = navItems.indexOfFirst { it.screen == currentScreen }.coerceAtLeast(0) ScrollableTabRow( selectedTabIndex = selectedIndex, containerColor = SurfaceSecondary, contentColor = AccentViolet, edgePadding = 8.dp, indicator = { tabPositions -> if (selectedIndex in tabPositions.indices) { TabRowDefaults.SecondaryIndicator( modifier = Modifier.tabIndicatorOffset(tabPositions[selectedIndex]), color = AccentViolet ) } }, divider = { Box(modifier = Modifier.fillMaxWidth().size(0.5.dp).background(DividerSubtle)) }, modifier = Modifier .fillMaxWidth() .navigationBarsPadding() .testTag("main_navigation_bar") ) { navItems.forEach { item -> val isSelected = currentScreen == item.screen Tab( selected = isSelected, onClick = { viewModel.navigateTo(item.screen) }, modifier = Modifier.testTag("nav_item_${item.label.lowercase()}"), text = { Row(verticalAlignment = Alignment.CenterVertically) { Icon( imageVector = item.icon, contentDescription = item.label, tint = if (isSelected) AccentViolet else TextSecondary, modifier = Modifier.size(18.dp) ) Text( text = " ${item.label}", color = if (isSelected) AccentViolet else TextSecondary, fontSize = 12.sp, fontWeight = if (isSelected) FontWeight.Bold else FontWeight.Normal ) } } ) } } } } ) { paddingValues -> Box( modifier = Modifier .fillMaxSize() .padding(bottom = paddingValues.calculateBottomPadding()) ) { when (currentScreen) { Screen.SwipeFeed -> SwipeFeedScreen( viewModel = viewModel, onOpenPlaylistDialog = { itemForPlaylistDialog = it }, onPickMediaFile = { filePickerLauncher.launch(arrayOf("audio/*", "video/*")) } ) Screen.MusicLibrary -> MusicLibraryScreen( viewModel = viewModel, onPlayMedia = { viewModel.playMedia(it) }, onAddToPlaylist = { itemForPlaylistDialog = it }, onPickMediaFile = { filePickerLauncher.launch(arrayOf("audio/*")) } ) Screen.VideoLibrary -> VideoLibraryScreen( viewModel = viewModel, onPlayVideo = { viewModel.playMedia(it) }, onAddToPlaylist = { itemForPlaylistDialog = it }, onPickMediaFile = { filePickerLauncher.launch(arrayOf("video/*")) } ) Screen.Folders -> FoldersScreen( viewModel = viewModel, onPlayMedia = { viewModel.playMedia(it) } ) Screen.Playlists -> PlaylistsScreen( viewModel = viewModel, onCreatePlaylistClick = { showCreatePlaylistDialog = true }, onPlayMedia = { viewModel.playMedia(it) } ) Screen.Favorites -> FavoritesScreen( viewModel = viewModel, onPlayMedia = { viewModel.playMedia(it) } ) Screen.Queue -> QueueScreen( viewModel = viewModel, onPlayQueueItem = { viewModel.playMedia(it) } ) Screen.History -> HistoryScreen( viewModel = viewModel ) Screen.Settings -> SettingsScreen( viewModel = viewModel ) } // Minimal Audio Player Sheet Overlay AnimatedVisibility( visible = isAudioSheetVisible && currentPlayingItem != null && !currentPlayingItem!!.isVideo, enter = slideInVertically(initialOffsetY = { it }) + fadeIn(), exit = slideOutVertically(targetOffsetY = { it }) + fadeOut(), modifier = Modifier.fillMaxSize() ) { currentPlayingItem?.let { audioItem -> val isFav = favorites.any { it.id == audioItem.id } FullAudioPlayerSheet( mediaItem = audioItem, isFavorite = isFav, onToggleFavorite = { viewModel.toggleFavorite(audioItem) }, onDismiss = { viewModel.showAudioPlayer(false) }, onQueueClick = { viewModel.showAudioPlayer(false) viewModel.navigateTo(Screen.Queue) } ) } } // Media Detail Dialog mediaDetailsItem?.let { item -> MediaDetailDialog( item = item, onDismiss = { viewModel.showMediaDetails(null) } ) } // Add to Playlist Dialog itemForPlaylistDialog?.let { item -> AddToPlaylistDialog( item = item, playlists = playlists, onCreateNewClick = { itemForPlaylistDialog = null showCreatePlaylistDialog = true }, onSelectPlaylist = { plId -> viewModel.addToPlaylist(plId, item) itemForPlaylistDialog = null }, onDismiss = { itemForPlaylistDialog = null } ) } // Create Playlist Dialog if (showCreatePlaylistDialog) { CreatePlaylistDialog( onDismiss = { showCreatePlaylistDialog = false }, onConfirm = { name -> viewModel.createPlaylist(name) showCreatePlaylistDialog = false } ) } } } } private data class NavDestination( val screen: Screen, val label: String, val icon: ImageVector )