0.0.1
This commit is contained in:
parent
7d6dec2d4f
commit
133e06db1e
@ -1,6 +1,9 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.application)
|
||||
alias(libs.plugins.kotlin.android)
|
||||
|
||||
id("org.jetbrains.kotlin.plugin.serialization") version "1.9.10"
|
||||
id("com.google.devtools.ksp") version "1.6.21-1.0.6"
|
||||
}
|
||||
|
||||
android {
|
||||
@ -53,6 +56,17 @@ dependencies {
|
||||
implementation(libs.coil.compose)
|
||||
implementation(libs.coil.network.okhttp)
|
||||
|
||||
implementation(libs.androidx.navigation.compose)
|
||||
|
||||
implementation(libs.androidx.room.runtime)
|
||||
implementation(libs.androidx.room.ktx)
|
||||
ksp(libs.androidx.room.compiler)
|
||||
|
||||
// Retrofit
|
||||
implementation(libs.converter.kotlinx.serialization)
|
||||
implementation(libs.retrofit)
|
||||
implementation(libs.kotlinx.serialization.json)
|
||||
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.androidx.lifecycle.runtime.ktx)
|
||||
implementation(libs.androidx.activity.compose)
|
||||
|
@ -1,7 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
@ -11,6 +13,7 @@
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.GameLibraryApp"
|
||||
android:name=".LibraryApplication"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
|
@ -0,0 +1,28 @@
|
||||
package at.xaxa.gamelibraryapp
|
||||
|
||||
import android.app.Application
|
||||
import at.xaxa.gamelibraryapp.data.GameRepository
|
||||
import at.xaxa.gamelibraryapp.data.db.LibraryDatabase
|
||||
import at.xaxa.gamelibraryapp.data.remote.GameRemoteService
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.MediaType.Companion.toMediaType
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.kotlinx.serialization.asConverterFactory
|
||||
|
||||
class LibraryApplication: Application() {
|
||||
val libraryRepository by lazy {
|
||||
val retrofit = Retrofit.Builder()
|
||||
.baseUrl("https://api.rawg.io/api/") // Only the root URL
|
||||
.addConverterFactory(Json { ignoreUnknownKeys = true }
|
||||
.asConverterFactory("application/json".toMediaType()))
|
||||
.build()
|
||||
|
||||
val gameRemoteService = retrofit.create(GameRemoteService::class.java)
|
||||
|
||||
GameRepository(
|
||||
LibraryDatabase.getDatabase(this).libraryDao(),
|
||||
gameRemoteService
|
||||
)
|
||||
}
|
||||
}
|
@ -11,6 +11,8 @@ import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import at.xaxa.gamelibraryapp.ui.LibraryApp
|
||||
import at.xaxa.gamelibraryapp.ui.theme.GameLibraryAppTheme
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
@ -20,8 +22,7 @@ class MainActivity : ComponentActivity() {
|
||||
setContent {
|
||||
GameLibraryAppTheme {
|
||||
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
|
||||
Greeting(
|
||||
name = "Android",
|
||||
LibraryApp(
|
||||
modifier = Modifier.padding(innerPadding)
|
||||
)
|
||||
}
|
||||
@ -29,19 +30,3 @@ class MainActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Greeting(name: String, modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
text = "Hello $name!",
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun GreetingPreview() {
|
||||
GameLibraryAppTheme {
|
||||
Greeting("Android")
|
||||
}
|
||||
}
|
8
app/src/main/java/at/xaxa/gamelibraryapp/data/Game.kt
Normal file
8
app/src/main/java/at/xaxa/gamelibraryapp/data/Game.kt
Normal file
@ -0,0 +1,8 @@
|
||||
package at.xaxa.gamelibraryapp.data
|
||||
|
||||
data class Game(
|
||||
val id: Int,
|
||||
val name: String,
|
||||
val details: String,
|
||||
val note: String
|
||||
)
|
@ -0,0 +1,43 @@
|
||||
package at.xaxa.gamelibraryapp.data
|
||||
|
||||
import at.xaxa.gamelibraryapp.data.db.LibraryDao
|
||||
import at.xaxa.gamelibraryapp.data.db.LibraryEntity
|
||||
import at.xaxa.gamelibraryapp.data.remote.GameRemoteService
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
class GameRepository(private val libraryDao: LibraryDao, private val gameRemoteService: GameRemoteService) {
|
||||
private val apiKey = "7a2a800a085d41e88621a8a59dc5ea82"
|
||||
|
||||
/*suspend fun loadInitialContacts() {
|
||||
try {
|
||||
val contactDtoList = gameRemoteService.getAllContacts()
|
||||
contactDtoList.map {
|
||||
Game(0, it., "${it.telephoneNumber}", it.age)
|
||||
}.forEach {
|
||||
insertContact(it)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("Repository", "Something went wrong! ${e.message}", e)
|
||||
}
|
||||
}*/
|
||||
|
||||
fun getAllGamesInLibrary(): Flow<List<Game>> {
|
||||
return libraryDao.getAllEntries().map {
|
||||
it.map {item -> Game(item._id, item.name, item.details, item.note) }
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun findGameById(id: Int): LibraryEntity {
|
||||
val item = libraryDao.findEntryById(id)
|
||||
return LibraryEntity(item._id, item.name, item.details, item.note)
|
||||
}
|
||||
|
||||
suspend fun addEntry(libraryEntity: LibraryEntity) {
|
||||
libraryDao.addEntry(LibraryEntity(libraryEntity._id, libraryEntity.name, libraryEntity.details, libraryEntity.note))
|
||||
}
|
||||
|
||||
suspend fun updateEntry(game: LibraryEntity) {
|
||||
libraryDao.updateEntry(LibraryEntity(game._id, game.name, game.details, game.note))
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
package at.xaxa.gamelibraryapp.data.db
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.Query
|
||||
import androidx.room.Update
|
||||
import at.xaxa.gamelibraryapp.data.Game
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface LibraryDao {
|
||||
@Insert
|
||||
suspend fun addEntry(libraryEntity: LibraryEntity)
|
||||
|
||||
@Update
|
||||
suspend fun updateEntry(libraryEntity: LibraryEntity)
|
||||
|
||||
@Delete
|
||||
suspend fun deleteEntry(libraryEntity: LibraryEntity)
|
||||
|
||||
@Query("SELECT * FROM library WHERE _id = :id")
|
||||
suspend fun findEntryById(id: Int): LibraryEntity
|
||||
|
||||
@Query("SELECT * FROM library")
|
||||
fun getAllEntries(): Flow<List<LibraryEntity>>
|
||||
|
||||
@Query("SELECT * FROM library WHERE name = :entryName")
|
||||
fun getEntriesWithName(entryName: String): Flow<List<LibraryEntity>>
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
package at.xaxa.gamelibraryapp.data.db
|
||||
|
||||
import androidx.room.Database
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import at.xaxa.gamelibraryapp.LibraryApplication
|
||||
|
||||
@Database(entities = [LibraryEntity::class], version = 2)
|
||||
abstract class LibraryDatabase : RoomDatabase() {
|
||||
abstract fun libraryDao(): LibraryDao
|
||||
|
||||
companion object {
|
||||
@Volatile
|
||||
private var Instance: LibraryDatabase? = null
|
||||
|
||||
fun getDatabase(context: LibraryApplication): LibraryDatabase {
|
||||
// if the Instance is not null, return it, otherwise create a new database instance.
|
||||
return Instance ?: synchronized(this) {
|
||||
val instance = Room.databaseBuilder(context, LibraryDatabase::class.java, "library_database")
|
||||
/**
|
||||
* Setting this option in your app's database builder means that Room
|
||||
* permanently deletes all data from the tables in your database when it
|
||||
* attempts to perform a migration with no defined migration path.
|
||||
*/
|
||||
.fallbackToDestructiveMigration()
|
||||
.build()
|
||||
Instance = instance
|
||||
return instance
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package at.xaxa.gamelibraryapp.data.db
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "library")
|
||||
data class LibraryEntity(
|
||||
@PrimaryKey
|
||||
val _id: Int,
|
||||
val name: String,
|
||||
val details: String,
|
||||
val note: String
|
||||
)
|
@ -0,0 +1,25 @@
|
||||
package at.xaxa.gamelibraryapp.data.remote
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ApiResponse(
|
||||
val count: Int,
|
||||
val next: String?,
|
||||
val previous: String?,
|
||||
val results: List<GameDto>
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GameDto(
|
||||
val id: Int,
|
||||
val name: String,
|
||||
val background_image: String,
|
||||
val genres: List<GenreDto>
|
||||
)
|
||||
|
||||
@Serializable
|
||||
data class GenreDto(
|
||||
val id: Int,
|
||||
val name: String
|
||||
)
|
@ -0,0 +1,22 @@
|
||||
package at.xaxa.gamelibraryapp.data.remote
|
||||
|
||||
import retrofit2.http.Body
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.POST
|
||||
import retrofit2.http.Path
|
||||
import retrofit2.http.Query
|
||||
|
||||
// TODO Implement Remote Service
|
||||
interface GameRemoteService {
|
||||
@GET("games")
|
||||
suspend fun getAllGames(@Query("key") apiKey: String): List<ApiResponse>
|
||||
|
||||
@GET("contacts/{contactId}")
|
||||
suspend fun getContactById(@Path("contactId") contactId: Int)
|
||||
|
||||
@GET("contacts/search") // becomes contacts/search?filter=<content of filterText>
|
||||
suspend fun findContacts(@Query("filter") filterText : String): List<ApiResponse>
|
||||
|
||||
@POST("contacts")
|
||||
suspend fun addContact(@Body contactDto: ApiResponse)
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
package at.xaxa.gamelibraryapp.ui
|
||||
|
||||
import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY
|
||||
import androidx.lifecycle.createSavedStateHandle
|
||||
import androidx.lifecycle.viewmodel.initializer
|
||||
import androidx.lifecycle.viewmodel.viewModelFactory
|
||||
import at.xaxa.gamelibraryapp.LibraryApplication
|
||||
import at.xaxa.gamelibraryapp.ui.Details.DetailsViewModel
|
||||
|
||||
object AppViewModelProvider {
|
||||
val Factory = viewModelFactory {
|
||||
initializer {
|
||||
DetailsViewModel(this.createSavedStateHandle(), (this[APPLICATION_KEY] as LibraryApplication).libraryRepository)
|
||||
}
|
||||
|
||||
/*initializer {
|
||||
ContactsViewModel((this[APPLICATION_KEY] as ContactsApplication).contactsRepository)
|
||||
}
|
||||
|
||||
initializer {
|
||||
ContactDetailsViewModel(this.createSavedStateHandle(), (this[APPLICATION_KEY] as ContactsApplication).contactsRepository)
|
||||
}
|
||||
|
||||
initializer {
|
||||
ContactEditViewModel(this.createSavedStateHandle(), (this[APPLICATION_KEY] as ContactsApplication).contactsRepository)
|
||||
}*/
|
||||
}
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package at.xaxa.gamelibraryapp.ui.Details
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import androidx.navigation.NavBackStackEntry
|
||||
import at.xaxa.gamelibraryapp.ui.AppViewModelProvider
|
||||
import at.xaxa.gamelibraryapp.ui.DetailCard
|
||||
|
||||
@Composable
|
||||
fun DetailView(
|
||||
navBackStackEntry: NavBackStackEntry,
|
||||
viewModel: DetailsViewModel = viewModel(factory = AppViewModelProvider.Factory),
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
// Extract arguments here
|
||||
val gameId = navBackStackEntry.arguments?.getInt("gameId") // Or `Int` if needed
|
||||
|
||||
DetailCard(modifier,
|
||||
title = "Game Title $gameId", // Use the extracted gameId
|
||||
details = "details",
|
||||
imageUrl = "https://media.rawg.io/media/games/b7f/b7ffc4c4776e61eca19d36d3c227f89a.jpg",
|
||||
note = "note"
|
||||
)
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
package at.xaxa.gamelibraryapp.ui.Details
|
||||
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import at.xaxa.gamelibraryapp.data.GameRepository
|
||||
import at.xaxa.gamelibraryapp.data.db.LibraryEntity
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
data class GameEditUi(
|
||||
val libraryEntity: LibraryEntity = LibraryEntity(0, "", "", "")
|
||||
)
|
||||
|
||||
class DetailsViewModel(private val savedStateHandle: SavedStateHandle, private val gameRepository: GameRepository) : ViewModel() {
|
||||
|
||||
private val gameId: Int = checkNotNull(savedStateHandle["gameId"])
|
||||
|
||||
var editUiState by mutableStateOf(GameEditUi())
|
||||
private set
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
val game = withContext(Dispatchers.IO) {
|
||||
gameRepository.findGameById(gameId)
|
||||
}
|
||||
editUiState = GameEditUi(game)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateGame(libraryEntity: LibraryEntity) {
|
||||
editUiState = editUiState.copy(libraryEntity=libraryEntity)
|
||||
}
|
||||
|
||||
fun saveLibrary() {
|
||||
viewModelScope.launch {
|
||||
gameRepository.updateEntry(editUiState.libraryEntity)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package at.xaxa.gamelibraryapp.ui
|
||||
|
||||
import androidx.lifecycle.SavedStateHandle
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import at.xaxa.gamelibraryapp.data.Game
|
||||
import at.xaxa.gamelibraryapp.data.GameRepository
|
||||
import at.xaxa.gamelibraryapp.data.db.LibraryEntity
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
data class GameDetailUi(
|
||||
val game: LibraryEntity = LibraryEntity(0, "", "", "")
|
||||
)
|
||||
|
||||
class GameDetailsViewModel(savedStateHandle: SavedStateHandle, private val gameRepository: GameRepository): ViewModel() {
|
||||
|
||||
private val gameId: Int = checkNotNull(savedStateHandle["gameId"])
|
||||
|
||||
private val _detailUiState = MutableStateFlow(GameDetailUi())
|
||||
val detailUiState = _detailUiState.asStateFlow()
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
val libraryEntity = withContext(Dispatchers.IO) {
|
||||
gameRepository.findGameById(gameId)
|
||||
}
|
||||
_detailUiState.update {
|
||||
GameDetailUi(libraryEntity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
package at.xaxa.gamelibraryapp.ui.GameList
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
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.unit.dp
|
||||
import at.xaxa.gamelibraryapp.ui.HorizontalCard
|
||||
|
||||
@Composable
|
||||
fun GameList(modifier: Modifier = Modifier, onCardClick: (Int) -> Unit) {
|
||||
var searchText by remember { mutableStateOf("") }
|
||||
|
||||
Column(modifier = modifier) {
|
||||
// Search Bar
|
||||
OutlinedTextField(
|
||||
value = searchText,
|
||||
onValueChange = { searchText = it },
|
||||
label = { Text("Search games") },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 4.dp)
|
||||
)
|
||||
|
||||
// Game List
|
||||
LazyColumn(modifier = Modifier.fillMaxSize()) {
|
||||
items(16) { index ->
|
||||
HorizontalCard(
|
||||
modifier = modifier,
|
||||
title = "Game $index",
|
||||
details = "Details about game $index",
|
||||
imageUrl = "https://media.rawg.io/media/games/b7f/b7ffc4c4776e61eca19d36d3c227f89a.jpg",
|
||||
onClick = {
|
||||
onCardClick(index)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
package at.xaxa.gamelibraryapp.ui.GameList
|
||||
|
@ -1,47 +1,32 @@
|
||||
package at.xaxa.gamelibraryapp.ui
|
||||
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredHeight
|
||||
import androidx.compose.foundation.layout.requiredWidth
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.List
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material.icons.outlined.Edit
|
||||
import androidx.compose.material3.FilledTonalButton
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.NavigationBarItem
|
||||
import androidx.compose.material3.OutlinedButton
|
||||
import androidx.compose.material3.OutlinedCard
|
||||
import androidx.compose.material3.OutlinedTextField
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
@ -53,56 +38,70 @@ import androidx.navigation.compose.composable
|
||||
import androidx.navigation.compose.currentBackStackEntryAsState
|
||||
import androidx.navigation.compose.rememberNavController
|
||||
import androidx.navigation.navArgument
|
||||
import at.xaxa.gamelibraryapp.R
|
||||
import at.xaxa.gamelibraryapp.ui.Details.DetailView
|
||||
import at.xaxa.gamelibraryapp.ui.GameList.GameList
|
||||
import at.xaxa.gamelibraryapp.ui.Search.SearchList
|
||||
import coil3.compose.AsyncImage
|
||||
import java.time.format.TextStyle
|
||||
|
||||
enum class DemoRoutes(val route: String) {
|
||||
enum class LibraryRoutes(val route: String) {
|
||||
List("list"),
|
||||
Add("add"),
|
||||
Detail("task/{taskId}")
|
||||
Detail("game/{gameId}")
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LibraryApp(modifier: Modifier = Modifier) {
|
||||
val navController = rememberNavController()
|
||||
val currentRoute = navController.currentBackStackEntryAsState().value?.destination?.route
|
||||
|
||||
Scaffold(
|
||||
bottomBar = {
|
||||
// Hide bottom bar only when in the Detail view
|
||||
if (currentRoute != LibraryRoutes.Detail.route) {
|
||||
androidx.compose.material3.NavigationBar {
|
||||
NavigationBarItem(
|
||||
icon = { Icon(Icons.AutoMirrored.Filled.List, contentDescription = "List") },
|
||||
label = { Text("List") },
|
||||
selected = navController.currentBackStackEntryAsState().value?.destination?.route == "list",
|
||||
onClick = { navController.navigate(DemoRoutes.List.route) }
|
||||
selected = currentRoute == LibraryRoutes.List.route,
|
||||
onClick = { navController.navigate(LibraryRoutes.List.route) }
|
||||
)
|
||||
NavigationBarItem(
|
||||
icon = { Icon(Icons.Default.Add, contentDescription = "Add") },
|
||||
label = { Text("Add") },
|
||||
selected = navController.currentBackStackEntryAsState().value?.destination?.route == "add",
|
||||
onClick = { navController.navigate(DemoRoutes.Add.route) }
|
||||
label = { Text("Search") },
|
||||
selected = currentRoute == LibraryRoutes.Add.route,
|
||||
onClick = { navController.navigate(LibraryRoutes.Add.route) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
) { innerPadding ->
|
||||
NavHost(
|
||||
navController = navController,
|
||||
startDestination = DemoRoutes.List.route,
|
||||
modifier = Modifier.padding(innerPadding)
|
||||
startDestination = LibraryRoutes.List.route,
|
||||
modifier = Modifier.padding(innerPadding),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
composable(DemoRoutes.List.route){
|
||||
// screen
|
||||
composable(LibraryRoutes.List.route){
|
||||
GameList(onCardClick = {
|
||||
navController.navigate("game/$it")
|
||||
})
|
||||
}
|
||||
composable(DemoRoutes.Add.route) {
|
||||
// screen
|
||||
composable(LibraryRoutes.Add.route) {
|
||||
SearchList(onCardClick = {
|
||||
navController.navigate("game/$it")
|
||||
})
|
||||
}
|
||||
composable(
|
||||
route = DemoRoutes.Detail.route,
|
||||
arguments = listOf(navArgument("taskId") {
|
||||
route = LibraryRoutes.Detail.route,
|
||||
arguments = listOf(navArgument("gameId") {
|
||||
type = NavType.IntType
|
||||
})
|
||||
) {
|
||||
// screen
|
||||
backStackEntry ->
|
||||
DetailView(
|
||||
navBackStackEntry = backStackEntry,
|
||||
modifier = Modifier
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -110,23 +109,93 @@ fun LibraryApp(modifier: Modifier = Modifier) {
|
||||
|
||||
|
||||
@Composable
|
||||
fun HorizontalCard(modifier: Modifier = Modifier, title: String, details: String) {
|
||||
fun DetailCard(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
details: String,
|
||||
note: String,
|
||||
imageUrl: String) {
|
||||
Surface(
|
||||
modifier = modifier
|
||||
.requiredWidth(360.dp)
|
||||
.requiredHeight(80.dp)
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 4.dp)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.border(1.dp, Color.LightGray, RoundedCornerShape(12.dp))
|
||||
) {
|
||||
Row(modifier = Modifier.fillMaxSize()) {
|
||||
LayoutMediaText(modifier = Modifier.weight(1f), title, details)
|
||||
ShowPicture(modifier = Modifier.weight(0.5f))
|
||||
Column(modifier) {
|
||||
ShowPicture(modifier = Modifier.height(200.dp), imageUrl)
|
||||
LayoutMediaText(modifier = Modifier, title, details)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HorizontalCard(
|
||||
modifier: Modifier = Modifier,
|
||||
title: String,
|
||||
details: String,
|
||||
imageUrl: String,
|
||||
onClick: () -> Unit) {
|
||||
Surface(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp, vertical = 4.dp)
|
||||
.requiredHeight(80.dp)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.border(1.dp, Color.LightGray, RoundedCornerShape(12.dp))
|
||||
.clickable { onClick() }
|
||||
) {
|
||||
Row(modifier = Modifier.fillMaxSize()) {
|
||||
LayoutMediaTextHor(modifier = Modifier.weight(1f), title, details)
|
||||
ShowPicture(modifier = Modifier.weight(0.5f), imageUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LayoutMediaText(modifier: Modifier = Modifier, title: String, details: String) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.clip(shape = RoundedCornerShape(12.dp))
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp, Alignment.Start),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.padding(all = 16.dp)
|
||||
) {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp, Alignment.Top),
|
||||
modifier = Modifier
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
color = Color.DarkGray,
|
||||
lineHeight = 1.5.em,
|
||||
style = androidx.compose.ui.text.TextStyle(
|
||||
fontSize = 16.sp,
|
||||
fontWeight = FontWeight.Medium,
|
||||
letterSpacing = 0.15.sp
|
||||
),
|
||||
modifier = Modifier
|
||||
.fillMaxWidth())
|
||||
Text(
|
||||
text = details,
|
||||
color = Color.DarkGray,
|
||||
lineHeight = 1.43.em,
|
||||
style = androidx.compose.ui.text.TextStyle(
|
||||
fontSize = 14.sp,
|
||||
letterSpacing = 0.25.sp
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LayoutMediaTextHor(modifier: Modifier = Modifier, title: String, details: String) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = modifier
|
||||
@ -173,7 +242,7 @@ fun LayoutMediaText(modifier: Modifier = Modifier, title: String, details: Strin
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ShowPicture(modifier: Modifier = Modifier) {
|
||||
fun ShowPicture(modifier: Modifier = Modifier, imageUrl: String) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
color = Color(0xfffef7ff),
|
||||
@ -182,9 +251,10 @@ fun ShowPicture(modifier: Modifier = Modifier) {
|
||||
) {
|
||||
//Display Image here
|
||||
AsyncImage(
|
||||
model = "https://media.rawg.io/media/games/b7f/b7ffc4c4776e61eca19d36d3c227f89a.jpg",
|
||||
model = imageUrl,
|
||||
contentDescription = "Translated description of what the image contains",
|
||||
Modifier.fillMaxHeight()
|
||||
Modifier.fillMaxHeight(),
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -192,5 +262,13 @@ fun ShowPicture(modifier: Modifier = Modifier) {
|
||||
@Preview(widthDp = 360, heightDp = 80)
|
||||
@Composable
|
||||
private fun HorizontalCardPreview() {
|
||||
HorizontalCard(Modifier, "Uncharted 3", "Action Adventure, 2011")
|
||||
HorizontalCard(Modifier, "Uncharted 3", "Action Adventure, 2011", "https://media.rawg.io/media/games/b7f/b7ffc4c4776e61eca19d36d3c227f89a.jpg", onClick = {
|
||||
println("clicked")
|
||||
})
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun DetailCardPreview() {
|
||||
DetailCard(Modifier, "Uncharted 3", "Action Adventure, 2011", "test", "https://media.rawg.io/media/games/b7f/b7ffc4c4776e61eca19d36d3c227f89a.jpg")
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package at.xaxa.gamelibraryapp.ui.Search
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import at.xaxa.gamelibraryapp.ui.HorizontalCard
|
||||
|
||||
@Composable
|
||||
fun SearchList(modifier: Modifier = Modifier, onCardClick: (Int) -> Unit){
|
||||
LazyColumn{
|
||||
items(16) { index ->
|
||||
HorizontalCard(modifier,
|
||||
"test $index",
|
||||
"details $index",
|
||||
"https://media.rawg.io/media/games/6e0/6e0c19bb111bd4fa20cf0eb72a049519.jpg",
|
||||
onClick = {
|
||||
onCardClick(index)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,2 @@
|
||||
package at.xaxa.gamelibraryapp.ui.Search
|
||||
|
@ -1,20 +1,27 @@
|
||||
[versions]
|
||||
agp = "8.6.0"
|
||||
coilCompose = "3.0.4"
|
||||
converterKotlinxSerialization = "2.11.0"
|
||||
kotlin = "1.9.0"
|
||||
coreKtx = "1.15.0"
|
||||
junit = "4.13.2"
|
||||
junitVersion = "1.2.1"
|
||||
espressoCore = "3.6.1"
|
||||
kotlinxSerializationJson = "1.6.3"
|
||||
lifecycleRuntimeKtx = "2.8.7"
|
||||
activityCompose = "1.9.3"
|
||||
composeBom = "2024.04.01"
|
||||
navigationCompose = "2.8.4"
|
||||
roomRuntime = "2.6.1"
|
||||
|
||||
[libraries]
|
||||
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
||||
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomRuntime" }
|
||||
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "roomRuntime" }
|
||||
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" }
|
||||
coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coilCompose" }
|
||||
coil-network-okhttp = { module = "io.coil-kt.coil3:coil-network-okhttp", version.ref = "coilCompose" }
|
||||
converter-kotlinx-serialization = { module = "com.squareup.retrofit2:converter-kotlinx-serialization", version.ref = "converterKotlinxSerialization" }
|
||||
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
||||
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
|
||||
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
|
||||
@ -29,6 +36,8 @@ androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-man
|
||||
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
||||
androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
|
||||
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
|
||||
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
|
||||
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "converterKotlinxSerialization" }
|
||||
|
||||
[plugins]
|
||||
android-application = { id = "com.android.application", version.ref = "agp" }
|
||||
|
Loading…
Reference in New Issue
Block a user