Skip to content

Commit

Permalink
Merge pull request #19 from blackcandy-org/native-bridge
Browse files Browse the repository at this point in the history
Add native bridge to play song from web page
  • Loading branch information
aidewoode authored Mar 21, 2024
2 parents 2ebec37 + fdc10dd commit 57e76b6
Show file tree
Hide file tree
Showing 10 changed files with 299 additions and 8 deletions.
42 changes: 42 additions & 0 deletions app/src/main/java/org/blackcandy/android/api/BlackCandyService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,16 @@ interface BlackCandyService {
songId: Int,
destinationSongId: Int,
): ApiResponse<Unit>

suspend fun replaceCurrentPlaylistWithAlbumSongs(albumId: Int): ApiResponse<List<Song>>

suspend fun replaceCurrentPlaylistWithPlaylistSongs(playlistId: Int): ApiResponse<List<Song>>

suspend fun addSongToCurrentPlaylist(
songId: Int,
currentSongId: Int?,
location: String?,
): ApiResponse<Song>
}

class BlackCandyServiceImpl(
Expand Down Expand Up @@ -143,6 +153,38 @@ class BlackCandyServiceImpl(
}
}

override suspend fun replaceCurrentPlaylistWithAlbumSongs(albumId: Int): ApiResponse<List<Song>> {
return handleResponse {
client.put("current_playlist/songs/albums/$albumId").body()
}
}

override suspend fun replaceCurrentPlaylistWithPlaylistSongs(playlistId: Int): ApiResponse<List<Song>> {
return handleResponse {
client.put("current_playlist/songs/playlists/$playlistId").body()
}
}

override suspend fun addSongToCurrentPlaylist(
songId: Int,
currentSongId: Int?,
location: String?,
): ApiResponse<Song> {
return handleResponse {
client.post("current_playlist/songs") {
parameter("song_id", songId.toString())

if (currentSongId != null) {
parameter("current_song_id", currentSongId.toString())
}

if (location != null) {
parameter("location", location)
}
}.body()
}
}

private suspend fun <T> handleResponse(request: suspend () -> T): ApiResponse<T> {
return try {
ApiResponse.Success(request())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.blackcandy.android.data

import org.blackcandy.android.api.BlackCandyService
import org.blackcandy.android.models.Song
import org.blackcandy.android.utils.PlayableResource
import org.blackcandy.android.utils.TaskResult

class CurrentPlaylistRepository(
Expand All @@ -25,4 +26,28 @@ class CurrentPlaylistRepository(
): TaskResult<Unit> {
return service.moveSongInCurrentPlaylist(songId, destinationSongId).asResult()
}

suspend fun replaceWith(
resourceType: PlayableResource,
resourceId: Int,
): TaskResult<List<Song>> {
val response =
when (resourceType) {
PlayableResource.ALBUM -> service.replaceCurrentPlaylistWithAlbumSongs(resourceId)
PlayableResource.PLAYLIST -> service.replaceCurrentPlaylistWithPlaylistSongs(resourceId)
}

return response.asResult()
}

suspend fun addSongToNext(
songId: Int,
currentSongId: Int,
): TaskResult<Song> {
return service.addSongToCurrentPlaylist(songId, currentSongId, null).asResult()
}

suspend fun addSongToLast(songId: Int): TaskResult<Song> {
return service.addSongToCurrentPlaylist(songId, null, "last").asResult()
}
}
2 changes: 1 addition & 1 deletion app/src/main/java/org/blackcandy/android/di/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ val appModule =
viewModel { LoginViewModel(get(), get(), get()) }
viewModel { MainViewModel(get(), get(), get()) }
viewModel { AccountSheetViewModel(get(), get()) }
viewModel { NavHostViewModel(get()) }
viewModel { NavHostViewModel(get(), get(), get()) }
viewModel { HomeViewModel(get()) }
viewModel { MiniPlayerViewModel(get()) }
viewModel { PlayerViewModel(get(), get(), get()) }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
package org.blackcandy.android.fragments.navs

import android.os.Bundle
import android.webkit.JavascriptInterface
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import dev.hotwire.turbo.config.TurboPathConfiguration
import dev.hotwire.turbo.session.TurboSessionNavHostFragment
import kotlinx.coroutines.launch
import org.blackcandy.android.fragments.sheets.AccountSheetFragment
import org.blackcandy.android.fragments.web.WebBottomSheetFragment
import org.blackcandy.android.fragments.web.WebFragment
import org.blackcandy.android.fragments.web.WebHomeFragment
import org.blackcandy.android.fragments.web.WebLibraryFragment
import org.blackcandy.android.utils.BLACK_CANDY_USER_AGENT
import org.blackcandy.android.utils.PlayableResource
import org.blackcandy.android.utils.SnackbarUtil.Companion.showSnackbar
import org.blackcandy.android.utils.Theme
import org.blackcandy.android.viewmodels.NavHostViewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
import kotlin.reflect.KClass
Expand Down Expand Up @@ -45,6 +53,22 @@ open class MainNavHostFragment : TurboSessionNavHostFragment() {
assetFilePath = "json/configuration.json",
)

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect {
if (it.alertMessage != null) {
showSnackbar(requireActivity(), it.alertMessage) {
viewModel.alertMessageShown()
}
}
}
}
}
}

override fun onSessionCreated() {
super.onSessionCreated()

Expand All @@ -54,7 +78,37 @@ open class MainNavHostFragment : TurboSessionNavHostFragment() {
object {
@JavascriptInterface
fun updateTheme(theme: String) {
viewModel.updateTheme(theme)
val themeValue = Theme.values().find { it.name == theme.uppercase() } ?: return
viewModel.updateTheme(themeValue)
}

@JavascriptInterface
fun playAll(
resourceType: String,
resourceId: Int,
) {
val resourceTypeValue = PlayableResource.values().find { it.name == resourceType.uppercase() } ?: return
viewModel.playAll(resourceTypeValue, resourceId)
}

@JavascriptInterface
fun playSong(songId: Int) {
viewModel.playSong(songId)
}

@JavascriptInterface
fun playNext(songId: Int) {
viewModel.playNext(songId)
}

@JavascriptInterface
fun playLast(songId: Int) {
viewModel.playLast(songId)
}

@JavascriptInterface
fun showFlashMessage(message: String) {
viewModel.showFlashMessage(message)
}
},
"NativeBridge",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,30 @@ class MusicServiceController(
_musicState.update { it.copy(playbackMode = playbackMode) }
}

fun getSongIndex(songId: Int): Int {
return musicState.value.playlist.indexOfFirst { it.id == songId }
}

fun addSongToNext(song: Song): Int {
val currentSong = musicState.value.currentSong
val songs =
if (currentSong != null) {
val index = musicState.value.playlist.indexOf(currentSong)
musicState.value.playlist.toMutableList().apply { add(index + 1, song) }
} else {
musicState.value.playlist.toMutableList().apply { add(0, song) }
}

updatePlaylist(songs)

return songs.indexOf(song)
}

fun addSongToLast(song: Song) {
val songs = musicState.value.playlist.toMutableList().apply { add(song) }
updatePlaylist(songs)
}

private fun updateCurrentSong() {
val currentMediaId = controller?.currentMediaItem?.mediaId
val currentSong = musicState.value.playlist.find { it.id.toString() == currentMediaId }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.blackcandy.android.utils

enum class PlayableResource {
ALBUM,
PLAYLIST,
}
31 changes: 29 additions & 2 deletions app/src/main/java/org/blackcandy/android/utils/SnackbarUtil.kt
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package org.blackcandy.android.utils

import android.app.Activity
import android.view.View
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.res.stringResource
import com.google.android.material.snackbar.Snackbar
import org.blackcandy.android.R
import org.blackcandy.android.models.AlertMessage

class SnackbarUtil {
Expand All @@ -12,7 +16,7 @@ class SnackbarUtil {
fun ShowSnackbar(
message: AlertMessage,
state: SnackbarHostState,
onShowed: () -> Unit,
onShown: () -> Unit,
) {
val snackbarText =
when (message) {
Expand All @@ -22,8 +26,31 @@ class SnackbarUtil {

LaunchedEffect(state) {
state.showSnackbar(snackbarText)
onShowed()
onShown()
}
}

fun showSnackbar(
activity: Activity,
message: AlertMessage,
onShown: () -> Unit,
) {
val rootView = activity.findViewById<View>(R.id.main_container)

val snackbarText =
when (message) {
is AlertMessage.String -> message.value
is AlertMessage.StringResource -> rootView.context.getString(message.value)
} ?: return

Snackbar.make(rootView, snackbarText, Snackbar.LENGTH_SHORT).addCallback(
object : Snackbar.Callback() {
override fun onShown(sb: Snackbar?) {
super.onShown(sb)
onShown()
}
},
).show()
}
}
}
7 changes: 7 additions & 0 deletions app/src/main/java/org/blackcandy/android/utils/Theme.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.blackcandy.android.utils

enum class Theme {
DARK,
LIGHT,
AUTO,
}
Loading

0 comments on commit 57e76b6

Please sign in to comment.