Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ android {
"SPOTIFY_REFRESH_URI",
"\"https://accounts.spotify.com/api/token\"",
)
buildConfigField("String", "API_URL", "\"http://10.0.2.2:3000/api/v1\"")
}

release {
Expand Down Expand Up @@ -71,6 +72,7 @@ android {
"SPOTIFY_REFRESH_URI",
"\"https://accounts.spotify.com/api/token\"",
)
buildConfigField("String", "API_URL", "\"http://localhost:3000/api/v1\"")
}
}
compileOptions {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import com.github.feelbeatapp.androidclient.auth.OauthConfig
import com.github.feelbeatapp.androidclient.auth.spotify.SpotifyAuthManager
import com.github.feelbeatapp.androidclient.auth.storage.AuthStorage
import com.github.feelbeatapp.androidclient.auth.storage.PreferencesAuthStorage
import com.github.feelbeatapp.androidclient.network.api.FeelBeatApi
import com.github.feelbeatapp.androidclient.network.api.KtorFeelBeatApi
import com.github.feelbeatapp.androidclient.network.fullduplex.NetworkAgent
import com.github.feelbeatapp.androidclient.network.fullduplex.WebsocketClient
import com.github.feelbeatapp.androidclient.network.spotify.KtorSpotifyAPI
Expand All @@ -21,6 +23,7 @@ import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.plugins.websocket.WebSockets
import io.ktor.http.Url
import io.ktor.serialization.kotlinx.json.json
import javax.inject.Named
import javax.inject.Singleton
import kotlinx.serialization.json.Json

Expand Down Expand Up @@ -58,11 +61,20 @@ abstract class AppModule {
refreshUri = BuildConfig.SPOTIFY_REFRESH_URI,
)
}

@Provides
@Singleton
@Named("API_URL")
fun provideApiUrl(): String {
return BuildConfig.API_URL
}
}

@Singleton @Binds abstract fun bindAuthManager(authManager: SpotifyAuthManager): AuthManager

@Singleton @Binds abstract fun bindAuthStorage(authStorage: PreferencesAuthStorage): AuthStorage

@Singleton @Binds abstract fun bindSpotifyAPI(spotifyAPI: KtorSpotifyAPI): SpotifyAPI

@Singleton @Binds abstract fun bindFeelBeatAPI(feelBeatApi: KtorFeelBeatApi): FeelBeatApi
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@ enum class ErrorCode {
AUTHORIZATION_ACCESS_TOKEN_ERROR,
AUTHORIZATION_REFRESH_TOKEN_ERROR,
AUTHENTICATION_LOGOUT_ERROR,
FEELBEAT_SERVER_UNREACHABLE,
FEELBEAT_SERVER_ERROR,
FEELBEAT_SERVER_INCORRECT_RESPONSE_FORMAT,
}

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.github.feelbeatapp.androidclient.network.api

import com.github.feelbeatapp.androidclient.ui.model.RoomSettings

interface FeelBeatApi {
suspend fun createRoom(settings: RoomSettings): String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.github.feelbeatapp.androidclient.network.api

import com.github.feelbeatapp.androidclient.auth.AuthManager
import com.github.feelbeatapp.androidclient.error.ErrorCode
import com.github.feelbeatapp.androidclient.error.FeelBeatException
import com.github.feelbeatapp.androidclient.network.api.payloads.CreateRoomPayload
import com.github.feelbeatapp.androidclient.network.api.responses.CreateRoomResponse
import com.github.feelbeatapp.androidclient.ui.model.RoomSettings
import io.ktor.client.HttpClient
import io.ktor.client.call.body
import io.ktor.client.request.headers
import io.ktor.client.request.post
import io.ktor.client.request.setBody
import io.ktor.client.statement.bodyAsText
import io.ktor.http.ContentType
import io.ktor.http.HttpStatusCode
import io.ktor.http.contentType
import io.ktor.util.network.UnresolvedAddressException
import java.io.IOException
import javax.inject.Inject
import javax.inject.Named

class KtorFeelBeatApi
@Inject
constructor(
@Named("API_URL") private val baseUrl: String,
private val httpClient: HttpClient,
private val authManager: AuthManager,
) : FeelBeatApi {
override suspend fun createRoom(settings: RoomSettings): String {
val payload = CreateRoomPayload.fromRoomSettings(settings)
val token = authManager.getAccessToken()

val res =
try {
httpClient.post("$baseUrl/create") {
headers { set("Authorization", "Bearer $token") }
contentType(ContentType.Application.Json)
setBody(payload)
}
} catch (e: Exception) {
when (e) {
is IOException,
is UnresolvedAddressException ->
throw FeelBeatException(ErrorCode.FEELBEAT_SERVER_UNREACHABLE, e)
else -> throw e
}
}

if (res.status != HttpStatusCode.OK) {
throw FeelBeatException(ErrorCode.FEELBEAT_SERVER_ERROR, res.bodyAsText())
}

val (roomId) =
try {
res.body<CreateRoomResponse>()
} catch (e: UnsupportedOperationException) {
throw FeelBeatException(
ErrorCode.FEELBEAT_SERVER_INCORRECT_RESPONSE_FORMAT,
"Failed to parse server response",
e,
)
}

return roomId
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.github.feelbeatapp.androidclient.network.api.payloads

import com.github.feelbeatapp.androidclient.ui.model.RoomSettings
import io.ktor.http.Url
import kotlinx.serialization.Serializable

@Serializable
data class CreateRoomPayload(
val maxPlayers: Int,
val turnCount: Int,
val timePenaltyPerSecond: Int,
val basePoints: Int,
val incorrectGuessPenalty: Int,
val playListId: String,
) {
companion object {
fun fromRoomSettings(settings: RoomSettings): CreateRoomPayload {
return CreateRoomPayload(
maxPlayers = settings.maxPlayers,
turnCount = settings.turnCount,
timePenaltyPerSecond = settings.timePenaltyPerSecond,
basePoints = settings.basePoints,
incorrectGuessPenalty = settings.incorrectGuessPenalty,
playListId = Url(settings.playlistLink).segments.last(),
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.github.feelbeatapp.androidclient.network.api.responses

import kotlinx.serialization.Serializable

@Serializable data class CreateRoomResponse(val roomId: String)
Original file line number Diff line number Diff line change
@@ -1,30 +1,23 @@
package com.github.feelbeatapp.androidclient.ui

import androidx.compose.foundation.layout.Box
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.github.feelbeatapp.androidclient.ui.acceptgame.AcceptGameScreen
import com.github.feelbeatapp.androidclient.ui.gameresult.GameResultScreen
import com.github.feelbeatapp.androidclient.ui.guesssong.GuessResultScreen
import com.github.feelbeatapp.androidclient.ui.guesssong.GuessSongScreen
import com.github.feelbeatapp.androidclient.ui.home.HomeScreen
import com.github.feelbeatapp.androidclient.ui.login.LoginScreen
import com.github.feelbeatapp.androidclient.ui.roomsettings.screens.EditRoomSettingsScreen
import com.github.feelbeatapp.androidclient.ui.roomsettings.screens.NewRoomSettingsScreen
import com.github.feelbeatapp.androidclient.ui.startgame.StartGameScreen
import com.github.feelbeatapp.androidclient.ui.theme.FeelBeatTheme

@Composable
fun FeelBeatApp(
@Suppress("UnusedParameter") widthSizeClass: WindowWidthSizeClass,
startScreen: FeelBeatRoute,
modifier: Modifier = Modifier,
) {
fun FeelBeatApp(startScreen: FeelBeatRoute, modifier: Modifier = Modifier) {
FeelBeatTheme {
val navController = rememberNavController()

Expand All @@ -35,7 +28,10 @@ fun FeelBeatApp(
HomeScreen(navController = navController)
}
composable(route = FeelBeatRoute.NEW_ROOM_SETTINGS.name) {
NewRoomSettingsScreen(navController = navController)
NewRoomSettingsScreen(
onNavigateTo = { navController.navigate(it) },
onNavigateBack = { navController.popBackStack() },
)
}
composable(route = FeelBeatRoute.GAME_RESULT.name) {
GameResultScreen(navController = navController)
Expand All @@ -57,5 +53,6 @@ fun FeelBeatApp(
@Preview
@Composable
fun AppPreview() {
FeelBeatApp(WindowWidthSizeClass.Compact, FeelBeatRoute.LOGIN)
FeelBeatApp(FeelBeatRoute.LOGIN)

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,12 @@ enum class FeelBeatRoute {
GAME_RESULT,
GUESS_SONG,
GUESS_RESULT,
START_GAME,
START_GAME;

fun withArgs(vararg args: String): String {
return buildString {
append(name)
args.forEach { append("/$it") }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@ class MainActivity : ComponentActivity() {

enableEdgeToEdge()
setContent {
val widthSizeClass = calculateWindowSizeClass(this).widthSizeClass
FeelBeatApp(widthSizeClass, startRoute)
FeelBeatApp(startRoute)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import com.github.feelbeatapp.androidclient.R
import com.github.feelbeatapp.androidclient.model.Song
import com.github.feelbeatapp.androidclient.ui.model.Song
import com.github.feelbeatapp.androidclient.ui.FeelBeatRoute
import com.github.feelbeatapp.androidclient.ui.home.HomeRoute
import com.github.feelbeatapp.androidclient.ui.startgame.PlayerCard
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package com.github.feelbeatapp.androidclient.ui.acceptgame
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.github.feelbeatapp.androidclient.R
import com.github.feelbeatapp.androidclient.model.Player
import com.github.feelbeatapp.androidclient.model.Song
import com.github.feelbeatapp.androidclient.ui.model.Player
import com.github.feelbeatapp.androidclient.ui.model.Song
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.github.feelbeatapp.androidclient.ui.acceptgame

import com.github.feelbeatapp.androidclient.model.Player
import com.github.feelbeatapp.androidclient.model.Playlist
import com.github.feelbeatapp.androidclient.model.Room
import com.github.feelbeatapp.androidclient.model.Song
import com.github.feelbeatapp.androidclient.ui.model.Player
import com.github.feelbeatapp.androidclient.ui.model.Playlist
import com.github.feelbeatapp.androidclient.ui.model.Room
import com.github.feelbeatapp.androidclient.ui.model.Song

data class GameState(
val players: List<Player> = emptyList(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import com.github.feelbeatapp.androidclient.R
import com.github.feelbeatapp.androidclient.ui.FeelBeatRoute
import com.github.feelbeatapp.androidclient.model.PlayerWithResult
import com.github.feelbeatapp.androidclient.ui.model.PlayerWithResult

@Composable
fun GameResultScreen(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package com.github.feelbeatapp.androidclient.ui.gameresult
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.github.feelbeatapp.androidclient.R
import com.github.feelbeatapp.androidclient.model.Player
import com.github.feelbeatapp.androidclient.model.PlayerWithResult
import com.github.feelbeatapp.androidclient.ui.model.Player
import com.github.feelbeatapp.androidclient.ui.model.PlayerWithResult
import com.github.feelbeatapp.androidclient.ui.guesssong.ResultStatus
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ import androidx.navigation.NavController
import androidx.navigation.compose.rememberNavController
import com.github.feelbeatapp.androidclient.R
import com.github.feelbeatapp.androidclient.ui.FeelBeatRoute
import com.github.feelbeatapp.androidclient.model.PlayerWithResult
import com.github.feelbeatapp.androidclient.model.Song
import com.github.feelbeatapp.androidclient.ui.model.PlayerWithResult
import com.github.feelbeatapp.androidclient.ui.model.Song

@OptIn(ExperimentalMaterial3Api::class)
@Composable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import androidx.compose.ui.text.input.TextFieldValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.github.feelbeatapp.androidclient.R
import com.github.feelbeatapp.androidclient.model.Player
import com.github.feelbeatapp.androidclient.model.PlayerWithResult
import com.github.feelbeatapp.androidclient.model.Song
import com.github.feelbeatapp.androidclient.ui.model.Player
import com.github.feelbeatapp.androidclient.ui.model.PlayerWithResult
import com.github.feelbeatapp.androidclient.ui.model.Song
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package com.github.feelbeatapp.androidclient.ui.guesssong

import androidx.compose.ui.text.input.TextFieldValue
import com.github.feelbeatapp.androidclient.model.PlayerWithResult
import com.github.feelbeatapp.androidclient.model.Playlist
import com.github.feelbeatapp.androidclient.model.Room
import com.github.feelbeatapp.androidclient.model.Song
import com.github.feelbeatapp.androidclient.ui.model.PlayerWithResult
import com.github.feelbeatapp.androidclient.ui.model.Playlist
import com.github.feelbeatapp.androidclient.ui.model.Room
import com.github.feelbeatapp.androidclient.ui.model.Song

data class GuessState(
val players: List<PlayerWithResult> = emptyList(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import coil3.compose.AsyncImage
import com.github.feelbeatapp.androidclient.R
import com.github.feelbeatapp.androidclient.model.Room
import com.github.feelbeatapp.androidclient.ui.model.Room
import com.github.feelbeatapp.androidclient.ui.FeelBeatRoute
import com.github.feelbeatapp.androidclient.ui.acceptgame.AcceptGameScreen
import com.github.feelbeatapp.androidclient.ui.roomsettings.screens.EditRoomSettingsScreen
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package com.github.feelbeatapp.androidclient.ui.home
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.github.feelbeatapp.androidclient.auth.AuthManager
import com.github.feelbeatapp.androidclient.model.Room
import com.github.feelbeatapp.androidclient.network.spotify.KtorSpotifyAPI
import com.github.feelbeatapp.androidclient.ui.model.Room
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.github.feelbeatapp.androidclient.ui.model

data class Player(val name: String, val image: Int)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.github.feelbeatapp.androidclient.model
package com.github.feelbeatapp.androidclient.ui.model

import com.github.feelbeatapp.androidclient.ui.guesssong.ResultStatus

Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
package com.github.feelbeatapp.androidclient.model
package com.github.feelbeatapp.androidclient.ui.model

data class Playlist(val name: String, val songs: List<Song>)
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.github.feelbeatapp.androidclient.model
package com.github.feelbeatapp.androidclient.ui.model

data class Room(
val id: Int,
Expand Down
Loading
Loading