diff --git a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/home/HomePage.kt b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/home/HomePage.kt index 2e1883a..4bd5530 100644 --- a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/home/HomePage.kt +++ b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/home/HomePage.kt @@ -98,7 +98,7 @@ fun HomePage( var desiredLayout by remember { mutableStateOf( - Preferences.EnumPrefs.getValue( + Preferences.Enumerations.getValue( DESIRED_OVERLAY, LayoutType.Grid ) ) @@ -256,7 +256,7 @@ private fun DropdownMenuContent( var desiredOverlay by remember { mutableIntStateOf( - Preferences.EnumPrefs.getValue( + Preferences.Enumerations.getValue( DESIRED_OVERLAY, LayoutType.Grid ).ordinal ) @@ -279,7 +279,7 @@ private fun DropdownMenuContent( selected = desiredOverlay == listType.ordinal, onClick = { desiredOverlay = listType.ordinal - Preferences.EnumPrefs.encodeValue( + Preferences.Enumerations.encodeValue( DESIRED_OVERLAY, listType ) onLayoutChanged(listType) diff --git a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/ID3MetadataEditorPage.kt b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/ID3MetadataEditorPage.kt index d062ff1..97d39fb 100644 --- a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/ID3MetadataEditorPage.kt +++ b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/ID3MetadataEditorPage.kt @@ -2,6 +2,7 @@ package com.bobbyesp.metadator.presentation.pages.utilities.tageditor import android.app.Activity import android.net.Uri +import androidx.activity.compose.BackHandler import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.IntentSenderRequest import androidx.activity.result.PickVisualMediaRequest @@ -145,6 +146,17 @@ fun ID3MetadataEditorPage( }) + BackHandler { + if (scaffoldState.bottomSheetState.isVisible) { + scope.launch { + scaffoldState.bottomSheetState.hide() + } + } else { + navController.popBackStack() + } + } + + BottomSheetScaffold(topBar = { TopAppBar(title = { Column(modifier = Modifier.fillMaxWidth()) { @@ -182,7 +194,7 @@ fun ID3MetadataEditorPage( }, modifier = Modifier.fillMaxSize(), scaffoldState = scaffoldState, - sheetPeekHeight = 128.dp, + sheetPeekHeight = 148.dp, sheetShadowElevation = 8.dp, sheetContent = { SpMetadataBottomSheetContent( diff --git a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/spotify/SpMetadataBottomSheetContent.kt b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/spotify/SpMetadataBottomSheetContent.kt index 8a31ebc..0d3bf89 100644 --- a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/spotify/SpMetadataBottomSheetContent.kt +++ b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/spotify/SpMetadataBottomSheetContent.kt @@ -1,8 +1,6 @@ package com.bobbyesp.metadator.presentation.pages.utilities.tageditor.spotify import androidx.compose.animation.AnimatedContent -import androidx.compose.animation.ExperimentalSharedTransitionApi -import androidx.compose.animation.SharedTransitionLayout import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut import androidx.compose.animation.togetherWith @@ -12,6 +10,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.SheetState import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -22,7 +21,7 @@ import com.bobbyesp.ui.motion.MotionConstants.DURATION_EXIT_SHORT import com.bobbyesp.ui.motion.tweenEnter import com.bobbyesp.ui.motion.tweenExit -@OptIn(ExperimentalMaterial3Api::class, ExperimentalSharedTransitionApi::class) +@OptIn(ExperimentalMaterial3Api::class) @Composable fun SpMetadataBottomSheetContent( name: String, @@ -30,6 +29,7 @@ fun SpMetadataBottomSheetContent( state: SheetState, viewModel: SpMetadataBottomSheetContentViewModel = hiltViewModel() ) { + val scope = rememberCoroutineScope() val viewState = viewModel.viewStateFlow.collectAsStateWithLifecycle().value val bottomSheetState = viewState.stage @@ -39,40 +39,34 @@ fun SpMetadataBottomSheetContent( if (name.isEmpty() && artist.isEmpty()) return LaunchedEffect(state.isVisible, name, artist) { - if (state.isVisible) { + val query = "$name $artist" + if (state.isVisible && viewState.lastQuery != query) { viewModel.updateStage(BottomSheetStage.SEARCH) - viewModel.searchTracks("$name $artist") + viewModel.searchTracks(query) } } - SharedTransitionLayout { - AnimatedContent( - targetState = bottomSheetState, - label = "Transition between bs states", - transitionSpec = { - fadeIn( - tweenEnter(delayMillis = DURATION_EXIT_SHORT) - ) togetherWith fadeOut( - tweenExit(durationMillis = DURATION_EXIT_SHORT) + AnimatedContent(targetState = bottomSheetState, + label = "Transition between bs states", + transitionSpec = { + fadeIn( + tweenEnter(delayMillis = DURATION_EXIT_SHORT) + ) togetherWith fadeOut( + tweenExit(durationMillis = DURATION_EXIT_SHORT) + ) + }) { actualStage -> + when (actualStage) { + BottomSheetStage.SEARCH -> { + SpMetadataBsSearch( + name = name, artist = artist, listState = lazyListState, viewModel = viewModel ) - }) { actualStage -> - when (actualStage) { - BottomSheetStage.SEARCH -> { - SpMetadataBsSearch( - name = name, - artist = artist, - listState = lazyListState, - viewModel = viewModel - ) - } + } - BottomSheetStage.TRACK_DETAILS -> { - SpMetadataBsDetails( - modifier = Modifier.fillMaxSize(), - viewModel = viewModel - ) - } + BottomSheetStage.TRACK_DETAILS -> { + SpMetadataBsDetails( + modifier = Modifier.fillMaxSize(), viewModel = viewModel + ) } } } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/spotify/SpMetadataBottomSheetContentViewModel.kt b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/spotify/SpMetadataBottomSheetContentViewModel.kt index e3e8746..76d36a0 100644 --- a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/spotify/SpMetadataBottomSheetContentViewModel.kt +++ b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/spotify/SpMetadataBottomSheetContentViewModel.kt @@ -12,7 +12,6 @@ import com.bobbyesp.metadator.features.spotify.domain.pagination.TracksPagingSou import com.bobbyesp.metadator.features.spotify.domain.services.SpotifyService import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -33,25 +32,21 @@ class SpMetadataBottomSheetContentViewModel @Inject constructor( } } - private var searchJob: Job? = null - private val mutableViewStateFlow = MutableStateFlow(ViewState()) val viewStateFlow = mutableViewStateFlow.asStateFlow() data class ViewState( val stage: BottomSheetStage = BottomSheetStage.SEARCH, - val viewState: SearchStageViewState = SearchStageViewState.Idle, val searchedTracks: Flow> = emptyFlow(), - val selectedTrack: Track? = null + val selectedTrack: Track? = null, + val lastQuery: String = "", ) fun searchTracks(query: String) { - searchJob?.cancel() - updateViewState(SearchStageViewState.Loading) - searchJob = viewModelScope.launch(Dispatchers.IO) { + updateQuery(query) + viewModelScope.launch(Dispatchers.IO) { getTracksPaginatedData(query) } - updateViewState(SearchStageViewState.Success) } private fun getTracksPaginatedData(query: String) { @@ -86,12 +81,12 @@ class SpMetadataBottomSheetContentViewModel @Inject constructor( } fun clearTrack() { + updateStage(BottomSheetStage.SEARCH) mutableViewStateFlow.update { it.copy( selectedTrack = null ) } - updateStage(BottomSheetStage.SEARCH) } fun updateStage(stage: BottomSheetStage) { @@ -102,23 +97,15 @@ class SpMetadataBottomSheetContentViewModel @Inject constructor( } } - private fun updateViewState(viewState: SearchStageViewState) { + fun updateQuery(query: String) { mutableViewStateFlow.update { it.copy( - viewState = viewState + lastQuery = query ) } } companion object { - sealed class SearchStageViewState { - data object Idle : SearchStageViewState() // Initial state - May be deleted - data object Loading : SearchStageViewState() - data object Success : SearchStageViewState() - data class Error(val error: String) : SearchStageViewState() - } - - enum class BottomSheetStage { SEARCH, TRACK_DETAILS diff --git a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/spotify/stages/SpMetadataBsDetails.kt b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/spotify/stages/SpMetadataBsDetails.kt index 4efd0db..451597c 100644 --- a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/spotify/stages/SpMetadataBsDetails.kt +++ b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/spotify/stages/SpMetadataBsDetails.kt @@ -1,11 +1,7 @@ package com.bobbyesp.metadator.presentation.pages.utilities.tageditor.spotify.stages import androidx.activity.compose.BackHandler -import androidx.compose.animation.AnimatedVisibilityScope import androidx.compose.animation.ExperimentalSharedTransitionApi -import androidx.compose.animation.SharedTransitionScope -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -23,15 +19,10 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.adamratzman.spotify.models.Track import com.bobbyesp.metadator.presentation.components.cards.songs.spotify.SpotifyHorizontalSongCard import com.bobbyesp.metadator.presentation.pages.utilities.tageditor.spotify.SpMetadataBottomSheetContentViewModel -import com.bobbyesp.metadator.presentation.utils.SongCardBoundsTransformation -import com.bobbyesp.ui.motion.MotionConstants.DURATION_EXIT_SHORT -import com.bobbyesp.ui.motion.tweenEnter -import com.bobbyesp.ui.motion.tweenExit -context(SharedTransitionScope) @OptIn(ExperimentalSharedTransitionApi::class) @Composable -fun AnimatedVisibilityScope.SpMetadataBsDetails( +fun SpMetadataBsDetails( modifier: Modifier = Modifier, viewModel: SpMetadataBottomSheetContentViewModel ) { @@ -50,34 +41,11 @@ fun AnimatedVisibilityScope.SpMetadataBsDetails( .fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally ) { - LaunchedEffect(true) { + LaunchedEffect(viewState.selectedTrack) { selectedTrack = viewState.selectedTrack } selectedTrack?.let { SpotifyHorizontalSongCard( - modifier = Modifier - .sharedBounds( - sharedContentState = rememberSharedContentState( - key = "container_${it.id}" - ), - animatedVisibilityScope = this@SpMetadataBsDetails, - placeHolderSize = SharedTransitionScope.PlaceHolderSize.animatedSize, - boundsTransform = SongCardBoundsTransformation, - enter = fadeIn( - tweenEnter(delayMillis = DURATION_EXIT_SHORT) - ), - exit = fadeOut( - tweenExit(durationMillis = DURATION_EXIT_SHORT) - ) - ), - imageModifier = Modifier.sharedElement( - state = rememberSharedContentState( - key = it.id - ), - animatedVisibilityScope = this@SpMetadataBsDetails, - placeHolderSize = SharedTransitionScope.PlaceHolderSize.animatedSize, - boundsTransform = SongCardBoundsTransformation, - ), innerModifier = Modifier.padding(8.dp), surfaceColor = Color.Transparent, track = it, diff --git a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/spotify/stages/SpMetadataBsSearch.kt b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/spotify/stages/SpMetadataBsSearch.kt index 9a726ed..4649d30 100644 --- a/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/spotify/stages/SpMetadataBsSearch.kt +++ b/app/src/main/java/com/bobbyesp/metadator/presentation/pages/utilities/tageditor/spotify/stages/SpMetadataBsSearch.kt @@ -1,11 +1,9 @@ package com.bobbyesp.metadator.presentation.pages.utilities.tageditor.spotify.stages -import androidx.compose.animation.AnimatedVisibilityScope -import androidx.compose.animation.ExperimentalSharedTransitionApi -import androidx.compose.animation.SharedTransitionScope -import androidx.compose.animation.fadeIn -import androidx.compose.animation.fadeOut +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.background import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -13,11 +11,13 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.rememberLazyListState +import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontWeight @@ -30,15 +30,11 @@ import androidx.paging.compose.itemKey import com.bobbyesp.metadator.R import com.bobbyesp.metadator.presentation.components.cards.songs.spotify.SpotifyHorizontalSongCard import com.bobbyesp.metadator.presentation.pages.utilities.tageditor.spotify.SpMetadataBottomSheetContentViewModel -import com.bobbyesp.metadator.presentation.utils.SongCardBoundsTransformation -import com.bobbyesp.ui.motion.MotionConstants.DURATION_EXIT_SHORT -import com.bobbyesp.ui.motion.tweenEnter -import com.bobbyesp.ui.motion.tweenExit +import com.bobbyesp.metadator.presentation.utils.pagination.pagingStateHandler -context(SharedTransitionScope) -@OptIn(ExperimentalSharedTransitionApi::class) +@OptIn(ExperimentalFoundationApi::class) @Composable -fun AnimatedVisibilityScope.SpMetadataBsSearch( +fun SpMetadataBsSearch( name: String, artist: String, listState: LazyListState = rememberLazyListState(), @@ -51,10 +47,18 @@ fun AnimatedVisibilityScope.SpMetadataBsSearch( state = listState, modifier = Modifier.fillMaxSize() ) { - item { + stickyHeader { Row( modifier = Modifier .fillMaxWidth() + .background( + brush = Brush.verticalGradient( + colors = listOf( + MaterialTheme.colorScheme.surfaceContainerLow, Color.Transparent + ), + startY = 0f, + ) + ) .padding(8.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center @@ -80,30 +84,6 @@ fun AnimatedVisibilityScope.SpMetadataBsSearch( ) { index -> val item = paginatedTracks[index] ?: return@items SpotifyHorizontalSongCard( - modifier = Modifier - .animateItem() - .sharedBounds( - sharedContentState = rememberSharedContentState( - key = "container_${item.id}" - ), - animatedVisibilityScope = this@SpMetadataBsSearch, - placeHolderSize = SharedTransitionScope.PlaceHolderSize.animatedSize, - boundsTransform = SongCardBoundsTransformation, - enter = fadeIn( - tweenEnter(delayMillis = DURATION_EXIT_SHORT) - ), - exit = fadeOut( - tweenExit(durationMillis = DURATION_EXIT_SHORT) - ) - ), - imageModifier = Modifier.sharedElement( - state = rememberSharedContentState( - key = item.id - ), - animatedVisibilityScope = this@SpMetadataBsSearch, - placeHolderSize = SharedTransitionScope.PlaceHolderSize.animatedSize, - boundsTransform = SongCardBoundsTransformation, - ), innerModifier = Modifier.padding(8.dp), surfaceColor = Color.Transparent, track = item, @@ -112,12 +92,23 @@ fun AnimatedVisibilityScope.SpMetadataBsSearch( } ) } -// pagingStateHandler(paginatedTracks) { -// LinearProgressIndicator( -// modifier = Modifier -// .fillMaxWidth() -// .padding(8.dp) -// ) -// } + pagingStateHandler(paginatedTracks, itemCount = 1) { + Column( + modifier = Modifier.fillMaxWidth(), + verticalArrangement = Arrangement.spacedBy(8.dp, Alignment.CenterVertically), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = stringResource(id = R.string.loading), + style = MaterialTheme.typography.bodyMedium, + textAlign = TextAlign.Center, + fontWeight = FontWeight.Bold, + modifier = Modifier.fillMaxWidth() + ) + LinearProgressIndicator( + modifier = Modifier.fillMaxWidth(0.8f) + ) + } + } } } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8b4bf72..4a7c65a 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -61,5 +61,6 @@ Others Unknown Showing results for %1$s by %2$s + Loading \ No newline at end of file diff --git a/app/utilities/src/main/java/com/bobbyesp/ext/ArrayExt.kt b/app/utilities/src/main/java/com/bobbyesp/ext/Array.kt similarity index 65% rename from app/utilities/src/main/java/com/bobbyesp/ext/ArrayExt.kt rename to app/utilities/src/main/java/com/bobbyesp/ext/Array.kt index 993ed76..b3012b6 100644 --- a/app/utilities/src/main/java/com/bobbyesp/ext/ArrayExt.kt +++ b/app/utilities/src/main/java/com/bobbyesp/ext/Array.kt @@ -1,5 +1,5 @@ package com.bobbyesp.ext fun Array?.joinOrNullToString(separator: String = ", "): String? { - return this?.joinToString(separator = ", ") + return this?.joinToString(separator = separator) } \ No newline at end of file diff --git a/app/utilities/src/main/java/com/bobbyesp/ext/IntExt.kt b/app/utilities/src/main/java/com/bobbyesp/ext/Int.kt similarity index 100% rename from app/utilities/src/main/java/com/bobbyesp/ext/IntExt.kt rename to app/utilities/src/main/java/com/bobbyesp/ext/Int.kt diff --git a/app/utilities/src/main/java/com/bobbyesp/ext/Long.kt b/app/utilities/src/main/java/com/bobbyesp/ext/Long.kt new file mode 100644 index 0000000..dc74e49 --- /dev/null +++ b/app/utilities/src/main/java/com/bobbyesp/ext/Long.kt @@ -0,0 +1,15 @@ +package com.bobbyesp.ext + +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.stringResource +import com.bobbyesp.utilities.R + +private const val GIGA_BYTES = 1024f * 1024f * 1024f +private const val MEGA_BYTES = 1024f * 1024f + +@Composable +fun Long.toFileSizeText() = this.toFloat().run { + if (this > GIGA_BYTES) + stringResource(R.string.filesize_gb).format(this / GIGA_BYTES) + else stringResource(R.string.filesize_mb).format(this / MEGA_BYTES) +} diff --git a/app/utilities/src/main/java/com/bobbyesp/ext/String.kt b/app/utilities/src/main/java/com/bobbyesp/ext/String.kt index 6e04166..5fd0331 100644 --- a/app/utilities/src/main/java/com/bobbyesp/ext/String.kt +++ b/app/utilities/src/main/java/com/bobbyesp/ext/String.kt @@ -1,5 +1,11 @@ package com.bobbyesp.ext +import androidx.core.text.isDigitsOnly + fun String?.formatForField(separator: String = ","): Array { return this?.split(separator)?.map { it.trim() }?.toTypedArray() ?: arrayOf(this ?: "") +} + +fun String.isNumberInRange(start: Int, end: Int): Boolean { + return this.isNotEmpty() && this.isDigitsOnly() && this.length < 10 && this.toInt() >= start && this.toInt() <= end } \ No newline at end of file diff --git a/app/utilities/src/main/java/com/bobbyesp/utilities/AndroidVersion.kt b/app/utilities/src/main/java/com/bobbyesp/utilities/AndroidVersion.kt deleted file mode 100644 index b6ac0df..0000000 --- a/app/utilities/src/main/java/com/bobbyesp/utilities/AndroidVersion.kt +++ /dev/null @@ -1,15 +0,0 @@ -package com.bobbyesp.utilities - -import androidx.annotation.ChecksSdkIntAtLeast - -object AndroidVersion { - @ChecksSdkIntAtLeast(parameter = 0) - fun isAndroidVersionOrLater(version: Int): Boolean { - return android.os.Build.VERSION.SDK_INT >= version - } - - @ChecksSdkIntAtLeast(parameter = 0) - fun isAndroidVersionOrOlder(version: Int): Boolean { - return android.os.Build.VERSION.SDK_INT <= version - } -} \ No newline at end of file diff --git a/app/utilities/src/main/java/com/bobbyesp/utilities/ExternalApps.kt b/app/utilities/src/main/java/com/bobbyesp/utilities/ExternalApps.kt deleted file mode 100644 index 6f6a72f..0000000 --- a/app/utilities/src/main/java/com/bobbyesp/utilities/ExternalApps.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.bobbyesp.utilities - -import android.content.Context -import android.content.Intent -import android.graphics.drawable.Drawable -import androidx.core.net.toUri - -object ExternalApps { - object Icons { - fun getIconForIntent(context: Context, intent: Intent): Drawable? { - return try { - val icon = context.packageManager.getActivityIcon(intent) - icon - } catch (e: Exception) { - e.printStackTrace() - null - } - } - - fun getIconForPackageName(context: Context, packageName: String): Drawable? { - return try { - val icon = context.packageManager.getApplicationIcon(packageName) - icon - } catch (e: Exception) { - e.printStackTrace() - null - } - } - - fun getBrowserIconForUrl(context: Context, url: String): Drawable? { - return try { - val intent = Intent(Intent.ACTION_VIEW, url.toUri()) - val icon = context.packageManager.getActivityIcon(intent) - icon - } catch (e: Exception) { - e.printStackTrace() - null - } - } - } -} \ No newline at end of file diff --git a/app/utilities/src/main/java/com/bobbyesp/utilities/Files.kt b/app/utilities/src/main/java/com/bobbyesp/utilities/Files.kt deleted file mode 100644 index 155f626..0000000 --- a/app/utilities/src/main/java/com/bobbyesp/utilities/Files.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.bobbyesp.utilities - -import android.content.Context -import java.io.File - -object Files { - fun getTempDirectory(context: Context): File { - return context.cacheDir - } - - fun createTempFile(context: Context, prefix: String, suffix: String): File { - return File.createTempFile(prefix, suffix, getTempDirectory(context)) - } - - fun deleteTempFile(context: Context, prefix: String, suffix: String) { - val tempFile = File(getTempDirectory(context), prefix + suffix) - tempFile.delete() - } - - fun deleteTempFile(context: Context, fileName: String) { - val tempFile = File(getTempDirectory(context), fileName) - tempFile.delete() - } -} \ No newline at end of file diff --git a/app/utilities/src/main/java/com/bobbyesp/utilities/Resource.kt b/app/utilities/src/main/java/com/bobbyesp/utilities/Resource.kt index bfa2e29..2c32466 100644 --- a/app/utilities/src/main/java/com/bobbyesp/utilities/Resource.kt +++ b/app/utilities/src/main/java/com/bobbyesp/utilities/Resource.kt @@ -1,7 +1,7 @@ package com.bobbyesp.utilities -sealed class Resource { - class Loading : Resource() +sealed class Resource { + data object Loading : Resource() data class Success(val data: T?) : Resource() - data class Error(val message: String) : Resource() + data class Error(val message: String) : Resource() } \ No newline at end of file diff --git a/app/utilities/src/main/java/com/bobbyesp/utilities/String.kt b/app/utilities/src/main/java/com/bobbyesp/utilities/String.kt deleted file mode 100644 index c821196..0000000 --- a/app/utilities/src/main/java/com/bobbyesp/utilities/String.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.bobbyesp.utilities - -import kotlin.random.Random - -object StringUtils { - fun generateRandomString(length: Int, useLetters: Boolean, useNumbers: Boolean): String { - val letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" - val numbers = "0123456789" - val characters = mutableListOf() - - if (useLetters) { - characters.addAll(letters.toList()) - } - - if (useNumbers) { - characters.addAll(numbers.toList()) - } - - if (characters.isEmpty()) { - throw IllegalArgumentException("At least one of useLetters or useNumbers must be true") - } - - return (1..length) - .map { characters[Random.nextInt(characters.size)] } - .joinToString("") - } -} \ No newline at end of file diff --git a/app/utilities/src/main/java/com/bobbyesp/utilities/Text.kt b/app/utilities/src/main/java/com/bobbyesp/utilities/Text.kt deleted file mode 100644 index d2b6f8b..0000000 --- a/app/utilities/src/main/java/com/bobbyesp/utilities/Text.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.bobbyesp.utilities - -import androidx.compose.runtime.Composable -import androidx.compose.ui.res.stringResource -import androidx.core.text.isDigitsOnly -import java.util.regex.Pattern - -private const val GIGA_BYTES = 1024f * 1024f * 1024f -private const val MEGA_BYTES = 1024f * 1024f - -@Composable -fun Long.toFileSizeText() = this.toFloat().run { - if (this > GIGA_BYTES) - stringResource(R.string.filesize_gb).format(this / GIGA_BYTES) - else stringResource(R.string.filesize_mb).format(this / MEGA_BYTES) -} - -fun String.isNumberInRange(start: Int, end: Int): Boolean { - return this.isNotEmpty() && this.isDigitsOnly() && this.length < 10 && this.toInt() >= start && this.toInt() <= end -} - -fun String?.toHttpsUrl(): String = - this?.run { - if (matches(Regex("^(http:).*"))) replaceFirst("http", "https") else this - } ?: "" - -private fun matchUrlFromString(s: String, isMatchingMultiLink: Boolean = false): String { - val builder = StringBuilder() - val pattern = - Pattern.compile("(http|https)://[\\w\\-_]+(\\.[\\w\\-_]+)+([\\w\\-.,@?^=%&:/~+#]*[\\w\\-@?^=%&/~+#])?") - with(pattern.matcher(s)) { - if (isMatchingMultiLink) - while (find()) { - if (builder.isNotEmpty()) - builder.append("\n") - builder.append(group()) - } - else if (find()) - builder.append(group()) - } - return builder.toString() -} - - -fun connectWithDelimiter(vararg strings: String, delimiter: String = " ยท "): String { - val builder = StringBuilder(strings.first()) - for (s in strings.asList().subList(1, strings.size)) { - builder.append(delimiter) - builder.append(s) - } - return builder.toString() -} - -fun connectWithBlank(s1: String, s2: String): String { - val f1 = s1.toEmpty() - val f2 = s2.toEmpty() - val blank = if (f1.isEmpty() || f2.isEmpty()) "" else " " - return f1 + blank + f2 -} - -fun String.toEmpty() = if (equals("none") || equals("null")) "" else this \ No newline at end of file diff --git a/app/utilities/src/main/java/com/bobbyesp/utilities/Time.kt b/app/utilities/src/main/java/com/bobbyesp/utilities/Time.kt index b7ec641..7a776ae 100644 --- a/app/utilities/src/main/java/com/bobbyesp/utilities/Time.kt +++ b/app/utilities/src/main/java/com/bobbyesp/utilities/Time.kt @@ -1,14 +1,8 @@ package com.bobbyesp.utilities import kotlinx.datetime.Clock -import kotlinx.datetime.Instant -import kotlinx.datetime.LocalDateTime import kotlinx.datetime.TimeZone -import kotlinx.datetime.toInstant -import kotlinx.datetime.toJavaLocalDateTime import kotlinx.datetime.toLocalDateTime -import java.text.SimpleDateFormat -import java.util.Date import java.util.Locale object Time { @@ -17,147 +11,9 @@ object Time { return instant.toLocalDateTime(TimeZone.currentSystemDefault()).toString() } - fun getZuluTime(timestamp: Long): String { - val instant = timestamp.toInstant() - return instant.toLocalDateTime(TimeZone.currentSystemDefault()).toString() - } - - fun getDate(timestamp: Long): String { - val localDateTime = timestamp.toLocalDateTime() - val day = localDateTime.dayOfMonth.toString() - val month = localDateTime.month.name.lowercase(Locale.getDefault()) - val year = localDateTime.year.toString() - return "$day $month $year" - } - - fun getTime(timestamp: Long): String { - val localDateTime = timestamp.toLocalDateTime() - val hour = localDateTime.hour.toString() - val minute = localDateTime.minute.toString() - val second = localDateTime.second.toString() - return "$hour:$minute:$second" - } - - fun convertTimestampToDateString(timestamp: Long): String { - // Create a SimpleDateFormat object with the desired date format - val sdf = SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()) - - // Convert the timestamp to a Date object - val date = Date(timestamp) - - // Use the SimpleDateFormat to format the Date object as a string - return sdf.format(date) - } - - - fun getFormattedDate(timestamp: Long): String { - val localDateTime = timestamp.toLocalDateTime() - val date = getDate(timestamp) - val time = getTime(timestamp) - //AM or PM - val amOrPm = if (localDateTime.hour < 12) "AM" else "PM" - return "$date $time$amOrPm" - } - - fun isSameDay(timestamp1: Long, timestamp2: Long): Boolean { - val localDateTime1 = timestamp1.toLocalDateTime() - val localDateTime2 = timestamp2.toLocalDateTime() - return localDateTime1.dayOfMonth == localDateTime2.dayOfMonth && - localDateTime1.month == localDateTime2.month && - localDateTime1.year == localDateTime2.year - } - fun formatDuration(duration: Long): String { val minutes: Long = duration / 60000 val seconds: Long = (duration % 60000) / 1000 return String.format(Locale.getDefault(), "%02d:%02d", minutes, seconds) } - - fun parseDateStringToLocalTime(dateString: String): String? { - val timeZoneUTC = TimeZone.UTC - - return try { - val parsedDate: Instant = Instant.parse(dateString) - val localDateTime = parsedDate.toLocalDateTime(timeZoneUTC) - localDateTime.toString() - } catch (e: Exception) { - e.printStackTrace() - null - } - } - - object Localized { - fun getDate(timestamp: Long): String { - val date = Date(timestamp) - val dateFormat = SimpleDateFormat("d MMMM y", Locale.getDefault()) - return dateFormat.format(date) - } - - fun getFormattedDate(timestamp: Long): String { - val localDateTime = timestamp.toLocalDateTime() - val date = getDate(timestamp) - val time = getTime(timestamp) - //AM or PM - val amOrPm = if (localDateTime.hour < 12) "AM" else "PM" - return "$date $time$amOrPm" - } - } - - fun getTimeNow(): java.time.LocalDateTime { - val instant = Clock.System.now() - return instant.toLocalDateTime(TimeZone.currentSystemDefault()).toJavaLocalDateTime() - } - - fun getTimeNowKotlin(): LocalDateTime { - val instant = Clock.System.now() - return instant.toLocalDateTime(TimeZone.currentSystemDefault()) - } -} - -fun String.calculateTimeDifference(): String { - val timeDifference = System.currentTimeMillis() - toTimestampInMillis() - val seconds = timeDifference / 1000 - val minutes = seconds / 60 - val hours = minutes / 60 - val days = hours / 24 - - return when { - days > 0 -> "${days}d" - hours > 0 -> "${hours}h" - minutes > 0 -> "${minutes}m" - else -> "${seconds}s" - } -} - -fun String.toTimestampInMillis(): Long { - val dateFormat = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault()) - dateFormat.timeZone = java.util.TimeZone.getTimeZone("UTC") - - return try { - val date = dateFormat.parse(this) - date?.time ?: 0 - } catch (e: Exception) { - val dateFormatWithoutMillis = - SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.getDefault()) - dateFormatWithoutMillis.timeZone = java.util.TimeZone.getTimeZone("UTC") - val dateWithoutMillis = dateFormatWithoutMillis.parse(this) - dateWithoutMillis?.time ?: 0 - } -} - -fun Int.toInstant(): Instant { - return Instant.fromEpochMilliseconds(this.toLong()) -} - -fun Long.toInstant(): Instant { - return Instant.fromEpochMilliseconds(this) -} - -fun Long.toLocalDateTime(): LocalDateTime { - val instant = this.toInstant() - return instant.toLocalDateTime(TimeZone.currentSystemDefault()) -} - -fun LocalDateTime.toLong(): Long { - return this.toInstant(TimeZone.currentSystemDefault()).toEpochMilliseconds() } \ No newline at end of file diff --git a/app/utilities/src/main/java/com/bobbyesp/utilities/Toast.kt b/app/utilities/src/main/java/com/bobbyesp/utilities/Toast.kt deleted file mode 100644 index 3e3fb62..0000000 --- a/app/utilities/src/main/java/com/bobbyesp/utilities/Toast.kt +++ /dev/null @@ -1,14 +0,0 @@ -package com.bobbyesp.utilities - -import android.content.Context -import android.widget.Toast - -object Toast { - fun makeToast(context: Context, text: String) { - Toast.makeText(context, text, Toast.LENGTH_SHORT).show() - } - - fun makeToast(context: Context, stringId: Int) { - Toast.makeText(context, context.getString(stringId), Toast.LENGTH_SHORT).show() - } -} \ No newline at end of file diff --git a/app/utilities/src/main/java/com/bobbyesp/utilities/URL.kt b/app/utilities/src/main/java/com/bobbyesp/utilities/URL.kt deleted file mode 100644 index b64fcab..0000000 --- a/app/utilities/src/main/java/com/bobbyesp/utilities/URL.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.bobbyesp.utilities - -object URL { - val urlRegex = Regex( - "((http|https)://)?(www.)?" + "[a-zA-Z0-9@:%._\\+~#?&//=]" + "{2,256}\\.[a-z]" + "{2,6}\\b([-a-zA-Z0-9@:%" + "._\\+~#?&//=]*)" - ) - - fun getUrls(text: String): List { - return urlRegex.findAll(text).map { it.value }.toList() - } -} \ No newline at end of file diff --git a/app/utilities/src/main/java/com/bobbyesp/utilities/preferences/Preferences.kt b/app/utilities/src/main/java/com/bobbyesp/utilities/preferences/Preferences.kt index 28934bd..bb55f51 100644 --- a/app/utilities/src/main/java/com/bobbyesp/utilities/preferences/Preferences.kt +++ b/app/utilities/src/main/java/com/bobbyesp/utilities/preferences/Preferences.kt @@ -21,7 +21,7 @@ private val BooleanPreferenceDefaults: Map = private val IntPreferenceDefaults: Map = mapOf( - DARK_THEME_VALUE to DarkThemePreference.FOLLOW_SYSTEM, + DARK_THEME_VALUE to DarkThemePreference.FOLLOW_SYSTEM ) object Preferences { @@ -47,7 +47,7 @@ object Preferences { fun encodeString(key: String, string: String) = key.updateString(string) fun containsKey(key: String) = kv.containsKey(key) - object EnumPrefs { + object Enumerations { inline fun > encodeValue( key: String, value: T diff --git a/app/utilities/src/main/java/com/bobbyesp/utilities/states/PageStates.kt b/app/utilities/src/main/java/com/bobbyesp/utilities/states/PageStates.kt deleted file mode 100644 index e62f18f..0000000 --- a/app/utilities/src/main/java/com/bobbyesp/utilities/states/PageStates.kt +++ /dev/null @@ -1,19 +0,0 @@ -package com.bobbyesp.utilities.states - -sealed class PageState { - data object Loading : PageState() - data object Error : PageState() - data object Success : PageState() -} - -sealed class PageStateWithThrowable { - data object Loading : PageStateWithThrowable() - data class Error(val exception: Throwable) : PageStateWithThrowable() - data object Success : PageStateWithThrowable() -} - -sealed class PageStateWithData(open val data: T?) { - data object Loading : PageStateWithData(data = null) - class Error(override val data: Exception) : PageStateWithData(data) - class Success(override val data: T) : PageStateWithData(data) -} diff --git a/app/utilities/src/main/java/com/bobbyesp/utilities/states/ScreenState.kt b/app/utilities/src/main/java/com/bobbyesp/utilities/states/ScreenState.kt new file mode 100644 index 0000000..baf239e --- /dev/null +++ b/app/utilities/src/main/java/com/bobbyesp/utilities/states/ScreenState.kt @@ -0,0 +1,7 @@ +package com.bobbyesp.utilities.states + +sealed class ScreenState { + data object Loading : ScreenState() + data class Success(val data: T?) : ScreenState() + data class Error(val exception: Exception) : ScreenState() +} \ No newline at end of file diff --git a/app/utilities/src/main/java/com/bobbyesp/utilities/storage/StorageAccessFrameworkHelper.kt b/app/utilities/src/main/java/com/bobbyesp/utilities/storage/StorageAccessFrameworkHelper.kt deleted file mode 100644 index ee8d8b7..0000000 --- a/app/utilities/src/main/java/com/bobbyesp/utilities/storage/StorageAccessFrameworkHelper.kt +++ /dev/null @@ -1,3 +0,0 @@ -package com.bobbyesp.utilities.storage - -object StorageAccessFrameworkHelper \ No newline at end of file diff --git a/app/utilities/src/main/java/com/bobbyesp/utilities/storage/StorageHelper.kt b/app/utilities/src/main/java/com/bobbyesp/utilities/storage/StorageHelper.kt deleted file mode 100644 index 7195d8e..0000000 --- a/app/utilities/src/main/java/com/bobbyesp/utilities/storage/StorageHelper.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.bobbyesp.utilities.storage - -import android.content.Context -import android.net.Uri -import androidx.documentfile.provider.DocumentFile -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext - -object StorageHelper { - object Compose - - suspend fun writeTextToFile(context: Context, text: String, fileUri: Uri?) { - withContext(Dispatchers.IO) { - if (fileUri != null) { - val documentFile = DocumentFile.fromSingleUri(context, fileUri) - if (documentFile != null && documentFile.canWrite()) { - val outputStream = context.contentResolver.openOutputStream(fileUri) - outputStream?.bufferedWriter().use { writer -> - writer?.write(text) - } - outputStream?.close() - } - } - } - } -} \ No newline at end of file diff --git a/app/utilities/src/main/java/com/bobbyesp/utilities/ui/LazyGridStateSaver.kt b/app/utilities/src/main/java/com/bobbyesp/utilities/ui/LazyGridStateSaver.kt index 03fa733..f84be0f 100644 --- a/app/utilities/src/main/java/com/bobbyesp/utilities/ui/LazyGridStateSaver.kt +++ b/app/utilities/src/main/java/com/bobbyesp/utilities/ui/LazyGridStateSaver.kt @@ -5,7 +5,6 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.saveable.rememberSaveable - /** * Static field, contains all scroll values */