Skip to content

Commit

Permalink
refactor: Improved code quality and functionality
Browse files Browse the repository at this point in the history
- Updated ReleaseDate formatting to use a custom separator.
- Refactored Int extension to format to minutes using string format.
- Refactored PermissionType to store permission strings directly and added a helper function to convert strings to PermissionType.
- Renamed Array extension `joinOrNullToString` to `joinToStringOrNull`.
- Implemented new methods in `SearchRepository` and `SearchRepositoryImpl` for searching different types of content.
- Updated app icon and splash screen.
- Replaced `isNotNullOrBlank` with `isNeitherNullNorBlank` in `MetadataEditorPage`.
- Converted `ConcurrentList` to a thread-safe implementation with read-write lock and added documentation.
- Updated `PropertyMap` extensions to use `joinToStringOrNull` for better handling of null values.
- Updated `toPermissionType` function call in `HomePage`.
- Updated Android Target SDK to 35.
  • Loading branch information
BobbyESP committed Nov 29, 2024
1 parent df90eb2 commit 01d5f78
Show file tree
Hide file tree
Showing 13 changed files with 256 additions and 61 deletions.
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Metadator.Starting"
tools:targetApi="34">
tools:targetApi="35">
<activity
android:name=".MainActivity"
android:configChanges="orientation"
Expand Down
6 changes: 3 additions & 3 deletions app/src/main/java/com/bobbyesp/metadator/ext/ReleaseDate.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@ package com.bobbyesp.metadator.ext

import com.adamratzman.spotify.models.ReleaseDate

fun ReleaseDate.format(precision: String?): String {
fun ReleaseDate.format(precision: String?, separator: String = "/"): String {
return when (precision) {
"year" -> {
"$year"
}

"month" -> {
"$month - $year"
"$month $separator $year"
}

"day" -> {
"$day-$month-$year"
"$day $separator $month $separator $year"
}

else -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,26 @@
package com.bobbyesp.metadator.features.spotify.data.repository

import androidx.paging.Pager
import com.adamratzman.spotify.endpoints.pub.SearchApi
import com.adamratzman.spotify.models.Album
import com.adamratzman.spotify.models.Artist
import com.adamratzman.spotify.models.Playlist
import com.adamratzman.spotify.models.Track
import com.bobbyesp.metadator.features.spotify.domain.repositories.SearchRepository
import com.bobbyesp.metadator.features.spotify.domain.services.search.SpotifySearchService
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject

/**
* Implementation of the [SearchRepository] interface. This is the class that should be used to
* search for tracks, albums, playlists, and artists on Spotify.
*
* ⚠️We are currently using the [SpotifySearchService] to search for tracks on Spotify. Using that
* service directly is not recommended. Instead, we should use this repository to search for tracks,
* but this implementation is not complete.
*
* @property searchService The service that will be used to search for tracks on Spotify.
*/
class SearchRepositoryImpl : SearchRepository, KoinComponent {
private val searchService by inject<SpotifySearchService>()

Expand All @@ -28,4 +42,32 @@ class SearchRepositoryImpl : SearchRepository, KoinComponent {
return Result.failure(th)
}
}

override suspend fun searchAlbums(query: String): Result<List<Album>> {
TODO("Not yet implemented")
}

override suspend fun searchPlaylists(query: String): Result<List<Playlist>> {
TODO("Not yet implemented")
}

override suspend fun searchArtists(query: String): Result<List<Artist>> {
TODO("Not yet implemented")
}

override suspend fun searchPaginatedTracks(query: String): Pager<Int, Track> {
TODO("Not yet implemented")
}

override suspend fun searchPaginatedAlbums(query: String): Pager<Int, Album> {
TODO("Not yet implemented")
}

override suspend fun searchPaginatedPlaylists(query: String): Pager<Int, Playlist> {
TODO("Not yet implemented")
}

override suspend fun searchPaginatedArtists(query: String): Pager<Int, Artist> {
TODO("Not yet implemented")
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
package com.bobbyesp.metadator.features.spotify.domain.repositories

import androidx.paging.Pager
import com.adamratzman.spotify.models.Album
import com.adamratzman.spotify.models.Artist
import com.adamratzman.spotify.models.Playlist
import com.adamratzman.spotify.models.Track

interface SearchRepository {
suspend fun searchTracks(query: String): Result<List<Track>>
suspend fun searchAlbums(query: String): Result<List<Album>>
suspend fun searchPlaylists(query: String): Result<List<Playlist>>
suspend fun searchArtists(query: String): Result<List<Artist>>
suspend fun searchPaginatedTracks(query: String): Pager<Int, Track>
suspend fun searchPaginatedAlbums(query: String): Pager<Int, Album>
suspend fun searchPaginatedPlaylists(query: String): Pager<Int, Playlist>
suspend fun searchPaginatedArtists(query: String): Pager<Int, Artist>
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ import com.bobbyesp.utilities.preferences.PreferencesKeys.DESIRED_OVERLAY
import com.bobbyesp.utilities.states.ResourceState
import com.bobbyesp.utilities.ui.permission.PermissionNotGrantedDialog
import com.bobbyesp.utilities.ui.permission.PermissionRequestHandler
import com.bobbyesp.utilities.ui.permission.toPermissionType
import com.bobbyesp.utilities.ui.permission.PermissionType.Companion.toPermissionType
import com.bobbyesp.utilities.ui.rememberForeverLazyGridState
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ import com.bobbyesp.ui.components.others.MetadataTag
import com.bobbyesp.ui.components.text.LargeCategoryTitle
import com.bobbyesp.ui.components.text.MarqueeText
import com.bobbyesp.ui.components.text.PreConfiguredOutlinedTextField
import com.bobbyesp.utilities.ext.isNotNullOrBlank
import com.bobbyesp.utilities.ext.isNeitherNullNorBlank
import com.bobbyesp.utilities.ext.toMinutes
import com.bobbyesp.utilities.states.ResourceState
import com.bobbyesp.utilities.states.ScreenState
Expand Down Expand Up @@ -129,7 +129,7 @@ fun MetadataEditorPage(
when (result.resultCode) {
Activity.RESULT_OK -> {
val receivedLyrics = result.data?.getStringExtra("lyrics")
if (receivedLyrics.isNotNullOrBlank()) {
if (receivedLyrics.isNeitherNullNorBlank()) {
pageState.mutablePropertiesMap["LYRICS"] = receivedLyrics!!
} else {
scope.launch { snackbarHost.showSnackbar(context.getString(R.string.empty_lyrics_received)) }
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/res/values/themes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@

<style name="Theme.Metadator.Starting" parent="Theme.SplashScreen">
<item name="postSplashScreenTheme">@style/Theme.Metadator</item>
<item name="windowSplashScreenAnimatedIcon">@mipmap/ic_launcher</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/metadator_logo_foreground</item>
</style>
</resources>
156 changes: 151 additions & 5 deletions app/utilities/src/main/java/com/bobbyesp/utilities/ConcurrentList.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,183 @@ import java.util.concurrent.locks.ReentrantReadWriteLock
import kotlin.concurrent.read
import kotlin.concurrent.write

/**
* A thread-safe implementation of a mutable list using a read-write lock.
*
* @param T the type of elements in this list
*/
class ConcurrentList<T> : MutableList<T> {
private val list = mutableListOf<T>()
private val lock = ReentrantReadWriteLock()

/**
* Returns the number of elements in the list.
*/
override val size: Int get() = lock.read { list.size }

/**
* Replaces the element at the specified position in this list with the specified element.
*
* @param index the index of the element to replace
* @param element the element to be stored at the specified position
* @return the element previously at the specified position
*/
override operator fun set(index: Int, element: T) = lock.write { list.set(index, element) }

/**
* Returns the element at the specified position in this list.
*
* @param index the index of the element to return
* @return the element at the specified position in this list
*/
override operator fun get(index: Int) = lock.read { list[index] }

/**
* Returns `true` if this list contains the specified element.
*
* @param element the element whose presence in this list is to be tested
* @return `true` if this list contains the specified element
*/
override fun contains(element: T) = lock.read { list.contains(element) }

/**
* Returns `true` if this list contains all of the elements in the specified collection.
*
* @param elements collection to be checked for containment in this list
* @return `true` if this list contains all of the elements in the specified collection
*/
override fun containsAll(elements: Collection<T>) = lock.read { list.containsAll(elements) }

/**
* Returns the index of the first occurrence of the specified element in this list,
* or -1 if this list does not contain the element.
*
* @param element the element to search for
* @return the index of the first occurrence of the specified element in this list,
* or -1 if this list does not contain the element
*/
override fun indexOf(element: T) = lock.read { list.indexOf(element) }

/**
* Returns the index of the last occurrence of the specified element in this list,
* or -1 if this list does not contain the element.
*
* @param element the element to search for
* @return the index of the last occurrence of the specified element in this list,
* or -1 if this list does not contain the element
*/
override fun lastIndexOf(element: T) = lock.read { list.lastIndexOf(element) }

/**
* Returns `true` if this list contains no elements.
*
* @return `true` if this list contains no elements
*/
override fun isEmpty() = lock.read { list.isEmpty() }
override fun subList(fromIndex: Int, toIndex: Int) =
lock.read { list.subList(fromIndex, toIndex) }

/**
* Returns a view of the portion of this list between the specified `fromIndex`, inclusive,
* and `toIndex`, exclusive.
*
* @param fromIndex low endpoint (inclusive) of the subList
* @param toIndex high endpoint (exclusive) of the subList
* @return a view of the specified range within this list
*/
override fun subList(fromIndex: Int, toIndex: Int) = lock.read { list.subList(fromIndex, toIndex) }

/**
* Appends the specified element to the end of this list.
*
* @param element element to be appended to this list
* @return `true` (as specified by `Collection.add`)
*/
override fun add(element: T) = lock.write { list.add(element) }

/**
* Inserts the specified element at the specified position in this list.
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
*/
override fun add(index: Int, element: T) = lock.write { list.add(index, element) }

/**
* Appends all of the elements in the specified collection to the end of this list,
* in the order that they are returned by the specified collection's iterator.
*
* @param elements collection containing elements to be added to this list
* @return `true` if this list changed as a result of the call
*/
override fun addAll(elements: Collection<T>) = lock.write { list.addAll(elements) }
override fun addAll(index: Int, elements: Collection<T>) =
lock.write { list.addAll(index, elements) }

/**
* Inserts all of the elements in the specified collection into this list,
* starting at the specified position.
*
* @param index index at which to insert the first element from the specified collection
* @param elements collection containing elements to be added to this list
* @return `true` if this list changed as a result of the call
*/
override fun addAll(index: Int, elements: Collection<T>) = lock.write { list.addAll(index, elements) }

/**
* Removes all of the elements from this list.
*/
override fun clear() = lock.write { list.clear() }

/**
* Removes the first occurrence of the specified element from this list, if it is present.
*
* @param element element to be removed from this list, if present
* @return `true` if this list contained the specified element
*/
override fun remove(element: T) = lock.write { list.remove(element) }

/**
* Removes from this list all of its elements that are contained in the specified collection.
*
* @param elements collection containing elements to be removed from this list
* @return `true` if this list changed as a result of the call
*/
override fun removeAll(elements: Collection<T>) = lock.write { list.removeAll(elements) }

/**
* Removes the element at the specified position in this list.
*
* @param index the index of the element to be removed
* @return the element that was removed from the list
*/
override fun removeAt(index: Int) = lock.write { list.removeAt(index) }

/**
* Retains only the elements in this list that are contained in the specified collection.
*
* @param elements collection containing elements to be retained in this list
* @return `true` if this list changed as a result of the call
*/
override fun retainAll(elements: Collection<T>) = lock.write { list.retainAll(elements) }

// NOTE: `write` lock since it returns `MutableIterator`s
/**
* Returns an iterator over the elements in this list in proper sequence.
*
* @return an iterator over the elements in this list in proper sequence
*/
override fun iterator() = lock.write { list.iterator() }

/**
* Returns a list iterator over the elements in this list (in proper sequence).
*
* @return a list iterator over the elements in this list (in proper sequence)
*/
override fun listIterator() = lock.write { list.listIterator() }

/**
* Returns a list iterator over the elements in this list (in proper sequence),
* starting at the specified position in the list.
*
* @param index index of the first element to be returned from the list iterator
* @return a list iterator over the elements in this list (in proper sequence),
* starting at the specified position in the list
*/
override fun listIterator(index: Int) = lock.write { list.listIterator(index) }
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package com.bobbyesp.utilities.ext

fun Array<String>?.joinOrNullToString(separator: String = ", "): String? {
fun Array<String>?.joinToStringOrNull(separator: String = ", "): String? {
return this?.joinToString(separator = separator)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@ fun Int.bigQuantityFormatter(): String {
}

fun Int.toMinutes(): String {
return java.text.SimpleDateFormat("mm:ss").format(java.util.Date(this.toLong()))
val minutes = this / 60
val seconds = this % 60
return "%02d:%02d".format(minutes, seconds)
}
Loading

0 comments on commit 01d5f78

Please sign in to comment.