Compare commits

...

2 Commits

Author SHA1 Message Date
Sebastian Böckelmann
c7a71fd1bf ADD: Network Communication 2025-05-04 19:35:25 +02:00
Sebastian Böckelmann
c8e9452a7e UPD: Dataformat for sync 2025-05-04 11:23:33 +02:00
10 changed files with 111 additions and 5 deletions

View File

@ -63,4 +63,7 @@ dependencies {
implementation(libs.compose.runtime.livedata) implementation(libs.compose.runtime.livedata)
implementation(libs.coil.compose) implementation(libs.coil.compose)
ksp(libs.androidx.room.compiler) ksp(libs.androidx.room.compiler)
implementation(libs.retrofit)
implementation(libs.converter.gson)
implementation(libs.logging.interceptor)
} }

View File

@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application <application
android:allowBackup="true" android:allowBackup="true"

View File

@ -8,8 +8,9 @@ import androidx.room.RoomDatabase
import androidx.room.TypeConverters import androidx.room.TypeConverters
import come.stormborntales.notevault.data.local.dao.NoteDao import come.stormborntales.notevault.data.local.dao.NoteDao
import come.stormborntales.notevault.data.local.entity.NoteEntity import come.stormborntales.notevault.data.local.entity.NoteEntity
import come.stormborntales.notevault.data.local.entity.SyncTime
@Database(entities = [NoteEntity::class], version = 1) @Database(entities = [NoteEntity::class, SyncTime::class], version = 1)
@TypeConverters(UriListConverter::class) @TypeConverters(UriListConverter::class)
abstract class AppDatabase : RoomDatabase() { abstract class AppDatabase : RoomDatabase() {
abstract fun noteDao(): NoteDao abstract fun noteDao(): NoteDao

View File

@ -3,6 +3,11 @@ package come.stormborntales.notevault.data.local.entity
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
enum class SyncStatus {
PENDING, // Lokale Änderungen warten auf Synchronisation
SYNCHRONIZED, // Der Eintrag wurde mit dem Server synchronisiert
DELETED // Der Eintrag wurde lokal gelöscht und wartet auf Serverabgleich
}
@Entity(tableName = "notes") @Entity(tableName = "notes")
data class NoteEntity( data class NoteEntity(
@PrimaryKey(autoGenerate = true) val id: Int = 0, @PrimaryKey(autoGenerate = true) val id: Int = 0,
@ -12,5 +17,10 @@ data class NoteEntity(
var year: Int?, var year: Int?,
var genre: String?, var genre: String?,
var description: String?, var description: String?,
val imagePreview: String val imagePreview: String,
val remoteId: String? = null,
val localModificationTimestamp: Long = System.currentTimeMillis(), // Zeitstempel der letzten lokalen Änderung
val syncStatus: SyncStatus = SyncStatus.PENDING // Status der Synchronisation
) )

View File

@ -0,0 +1,10 @@
package come.stormborntales.notevault.data.local.entity
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "sync_time_table")
data class SyncTime(
@PrimaryKey val id: Long = 1, // Einziger Eintrag für das Gerät
val lastSyncTimestamp: Long = System.currentTimeMillis() // Zeitstempel des letzten Syncsy
)

View File

@ -0,0 +1,13 @@
package come.stormborntales.notevault.data.remote
import come.stormborntales.notevault.data.remote.model.user.RegisterRequest
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.GET
import retrofit2.http.POST
interface ApiInterface {
@POST("user/register")
suspend fun register(@Body user: RegisterRequest): Response<Unit>
}

View File

@ -0,0 +1,27 @@
package come.stormborntales.notevault.data.remote
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import kotlin.getValue
object ApiClient {
private const val BASE_URL = "https://notevault.fawkes100.de" // < Anpassen!
val logging = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
val client = OkHttpClient.Builder()
.addInterceptor(logging)
.build()
val retrofit = Retrofit.Builder()
.baseUrl(BASE_URL)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
}

View File

@ -0,0 +1,10 @@
package come.stormborntales.notevault.data.remote.model.user
data class RegisterRequest(
val email: String,
val password: String
)
data class RegisterResponse(
val userId: String
)

View File

@ -1,5 +1,6 @@
package come.stormborntales.notevault.ui.screens package come.stormborntales.notevault.ui.screens
import android.util.Log
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
@ -13,12 +14,20 @@ import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Switch import androidx.compose.material3.Switch
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import come.stormborntales.notevault.data.remote.ApiClient
import come.stormborntales.notevault.data.remote.ApiInterface
import come.stormborntales.notevault.data.remote.model.user.RegisterRequest
import kotlinx.coroutines.launch
@Composable @Composable
fun SettingsScreen() { fun SettingsScreen() {
val coroutineScope = rememberCoroutineScope()
val syncApi = remember { ApiClient.retrofit.create(ApiInterface::class.java) }
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@ -70,10 +79,25 @@ fun SettingsScreen() {
Spacer(modifier = Modifier.height(32.dp)) Spacer(modifier = Modifier.height(32.dp))
OutlinedButton( OutlinedButton(
onClick = { /* TODO: Impressum anzeigen */ }, onClick = {
coroutineScope.launch {
try {
val request = RegisterRequest("test@example.com", "123456")
val response = syncApi.register(request)
if(response.isSuccessful) {
Log.d("Response", "Response was successful")
} else {
Log.d("Response", "Error: ${response.errorBody()}")
}
} catch (e: Exception) {
println("Netzwerkfehler: ${e.message}")
}
}
},
modifier = Modifier.fillMaxWidth() modifier = Modifier.fillMaxWidth()
) { ) {
Text("Impressum") Text("Registrierung testen")
} }
} }
} }

View File

@ -1,6 +1,7 @@
[versions] [versions]
agp = "8.8.2" agp = "8.8.2"
composeBomVersion = "2024.01.00" composeBomVersion = "2024.01.00"
converterGson = "2.9.0"
kotlin = "2.0.0" kotlin = "2.0.0"
coreKtx = "1.10.1" coreKtx = "1.10.1"
junit = "4.13.2" junit = "4.13.2"
@ -9,14 +10,17 @@ espressoCore = "3.6.1"
lifecycleRuntimeKtx = "2.8.7" lifecycleRuntimeKtx = "2.8.7"
activityCompose = "1.8.0" activityCompose = "1.8.0"
composeBom = "2024.04.01" composeBom = "2024.04.01"
loggingInterceptor = "4.12.0"
navigationCompose = "2.8.9" navigationCompose = "2.8.9"
retrofit = "2.9.0"
room = "2.7.1" room = "2.7.1"
ksp="2.1.21-RC-2.0.0" ksp="2.0.0-1.0.21"
compose = "1.6.0" # oder was immer deine aktuelle Compose-Version ist compose = "1.6.0" # oder was immer deine aktuelle Compose-Version ist
[libraries] [libraries]
coil-compose = { group = "io.coil-kt", name = "coil-compose", version = "2.5.0" } # oder aktueller coil-compose = { group = "io.coil-kt", name = "coil-compose", version = "2.5.0" } # oder aktueller
compose-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata", version.ref = "compose" } compose-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata", version.ref = "compose" }
converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "converterGson" }
lifecycle-livedata-ktx = "androidx.lifecycle:lifecycle-livedata-ktx:2.8.7" lifecycle-livedata-ktx = "androidx.lifecycle:lifecycle-livedata-ktx:2.8.7"
androidx-compose-bom-v20240100 = { module = "androidx.compose:compose-bom", version.ref = "composeBomVersion" } androidx-compose-bom-v20240100 = { module = "androidx.compose:compose-bom", version.ref = "composeBomVersion" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@ -39,6 +43,8 @@ androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit
androidx-material3 = { group = "androidx.compose.material3", name = "material3" } androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" } androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" } androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
logging-interceptor = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "loggingInterceptor" }
retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
[plugins] [plugins]