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
15 changed files with 113 additions and 58 deletions

1
.idea/.gitignore vendored
View File

@ -6,4 +6,3 @@
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
/AndroidProjectSystem.xml

View File

@ -63,4 +63,7 @@ dependencies {
implementation(libs.compose.runtime.livedata)
implementation(libs.coil.compose)
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"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"

View File

@ -45,7 +45,6 @@ import come.stormborntales.notevault.data.local.entity.NoteEntity
import come.stormborntales.notevault.data.repository.NoteRepository
import come.stormborntales.notevault.ui.screens.AddNoteDialog
import come.stormborntales.notevault.ui.screens.MainScreen
import come.stormborntales.notevault.ui.screens.NotesScreen
import come.stormborntales.notevault.ui.screens.SettingsScreen
import come.stormborntales.notevault.ui.theme.NoteVaultTheme
import come.stormborntales.notevault.ui.viewmodel.NoteViewModel
@ -91,7 +90,6 @@ class MainActivity : ComponentActivity() {
val navItems = listOf(
"Home" to "main",
"Notes" to "notes",
"Einstellungen" to "settings"
)
@ -205,9 +203,6 @@ class MainActivity : ComponentActivity() {
composable("settings") {
SettingsScreen()
}
composable("notes") {
NotesScreen()
}
}
if (showDialog) {

View File

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

View File

@ -1,11 +0,0 @@
package come.stormborntales.notevault.data.local.entity
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity("note_collections")
data class NoteCollection(
@PrimaryKey(autoGenerate = true) var id: Int = 0,
var name: String,
var parentId: Int? = null
)

View File

@ -1,16 +1,14 @@
package come.stormborntales.notevault.data.local.entity
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.PrimaryKey
@Entity(tableName = "notes",
foreignKeys = [ForeignKey(
entity = NoteCollection::class,
parentColumns = ["id"],
childColumns = ["collectionId"],
onDelete = ForeignKey.CASCADE
)])
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")
data class NoteEntity(
@PrimaryKey(autoGenerate = true) val id: Int = 0,
var title: String,
@ -20,5 +18,9 @@ data class NoteEntity(
var genre: String?,
var description: String?,
val imagePreview: String,
val collectionId: Int?
)
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,24 +0,0 @@
package come.stormborntales.notevault.ui.screens
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
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.material3.Divider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
fun NotesScreen() {
}

View File

@ -1,5 +1,6 @@
package come.stormborntales.notevault.ui.screens
import android.util.Log
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@ -13,12 +14,20 @@ import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Switch
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
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
fun SettingsScreen() {
val coroutineScope = rememberCoroutineScope()
val syncApi = remember { ApiClient.retrofit.create(ApiInterface::class.java) }
Column(
modifier = Modifier
.fillMaxSize()
@ -70,10 +79,25 @@ fun SettingsScreen() {
Spacer(modifier = Modifier.height(32.dp))
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()
) {
Text("Impressum")
Text("Registrierung testen")
}
}
}

View File

@ -93,8 +93,7 @@ class NoteViewModel(
year = year,
genre = genre,
description = description,
imagePreview = preview_image_path.toString(),
collectionId = null
imagePreview = preview_image_path.toString()
)
repository.insert(note)

View File

@ -1,6 +1,7 @@
[versions]
agp = "8.8.2"
composeBomVersion = "2024.01.00"
converterGson = "2.9.0"
kotlin = "2.0.0"
coreKtx = "1.10.1"
junit = "4.13.2"
@ -9,14 +10,17 @@ espressoCore = "3.6.1"
lifecycleRuntimeKtx = "2.8.7"
activityCompose = "1.8.0"
composeBom = "2024.04.01"
loggingInterceptor = "4.12.0"
navigationCompose = "2.8.9"
retrofit = "2.9.0"
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
[libraries]
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" }
converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "converterGson" }
lifecycle-livedata-ktx = "androidx.lifecycle:lifecycle-livedata-ktx:2.8.7"
androidx-compose-bom-v20240100 = { module = "androidx.compose:compose-bom", version.ref = "composeBomVersion" }
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-room-ktx = { module = "androidx.room:room-ktx", 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]