Implement syncing logic (communication with server doesn't work right now)
This commit is contained in:
parent
0c5fffb06c
commit
ff80db973c
124
.idea/uiDesigner.xml
Normal file
124
.idea/uiDesigner.xml
Normal file
@ -0,0 +1,124 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Palette2">
|
||||
<group name="Swing">
|
||||
<item class="com.intellij.uiDesigner.HSpacer" tooltip-text="Horizontal Spacer" icon="/com/intellij/uiDesigner/icons/hspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="1" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="com.intellij.uiDesigner.VSpacer" tooltip-text="Vertical Spacer" icon="/com/intellij/uiDesigner/icons/vspacer.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="1" anchor="0" fill="2" />
|
||||
</item>
|
||||
<item class="javax.swing.JPanel" icon="/com/intellij/uiDesigner/icons/panel.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollPane" icon="/com/intellij/uiDesigner/icons/scrollPane.svg" removable="false" auto-create-binding="false" can-attach-label="true">
|
||||
<default-constraints vsize-policy="7" hsize-policy="7" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JButton" icon="/com/intellij/uiDesigner/icons/button.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="0" fill="1" />
|
||||
<initial-values>
|
||||
<property name="text" value="Button" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JRadioButton" icon="/com/intellij/uiDesigner/icons/radioButton.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="RadioButton" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JCheckBox" icon="/com/intellij/uiDesigner/icons/checkBox.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="3" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="CheckBox" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JLabel" icon="/com/intellij/uiDesigner/icons/label.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="8" fill="0" />
|
||||
<initial-values>
|
||||
<property name="text" value="Label" />
|
||||
</initial-values>
|
||||
</item>
|
||||
<item class="javax.swing.JTextField" icon="/com/intellij/uiDesigner/icons/textField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JPasswordField" icon="/com/intellij/uiDesigner/icons/passwordField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JFormattedTextField" icon="/com/intellij/uiDesigner/icons/formattedTextField.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1">
|
||||
<preferred-size width="150" height="-1" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextArea" icon="/com/intellij/uiDesigner/icons/textArea.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTextPane" icon="/com/intellij/uiDesigner/icons/textPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JEditorPane" icon="/com/intellij/uiDesigner/icons/editorPane.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JComboBox" icon="/com/intellij/uiDesigner/icons/comboBox.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="2" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JTable" icon="/com/intellij/uiDesigner/icons/table.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JList" icon="/com/intellij/uiDesigner/icons/list.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="2" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTree" icon="/com/intellij/uiDesigner/icons/tree.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3">
|
||||
<preferred-size width="150" height="50" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JTabbedPane" icon="/com/intellij/uiDesigner/icons/tabbedPane.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSplitPane" icon="/com/intellij/uiDesigner/icons/splitPane.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="3" hsize-policy="3" anchor="0" fill="3">
|
||||
<preferred-size width="200" height="200" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JSpinner" icon="/com/intellij/uiDesigner/icons/spinner.svg" removable="false" auto-create-binding="true" can-attach-label="true">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSlider" icon="/com/intellij/uiDesigner/icons/slider.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="8" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JSeparator" icon="/com/intellij/uiDesigner/icons/separator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="6" anchor="0" fill="3" />
|
||||
</item>
|
||||
<item class="javax.swing.JProgressBar" icon="/com/intellij/uiDesigner/icons/progressbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar" icon="/com/intellij/uiDesigner/icons/toolbar.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="6" anchor="0" fill="1">
|
||||
<preferred-size width="-1" height="20" />
|
||||
</default-constraints>
|
||||
</item>
|
||||
<item class="javax.swing.JToolBar$Separator" icon="/com/intellij/uiDesigner/icons/toolbarSeparator.svg" removable="false" auto-create-binding="false" can-attach-label="false">
|
||||
<default-constraints vsize-policy="0" hsize-policy="0" anchor="0" fill="1" />
|
||||
</item>
|
||||
<item class="javax.swing.JScrollBar" icon="/com/intellij/uiDesigner/icons/scrollbar.svg" removable="false" auto-create-binding="true" can-attach-label="false">
|
||||
<default-constraints vsize-policy="6" hsize-policy="0" anchor="0" fill="2" />
|
||||
</item>
|
||||
</group>
|
||||
</component>
|
||||
</project>
|
@ -5,12 +5,12 @@ plugins {
|
||||
|
||||
android {
|
||||
namespace = "core.notevault"
|
||||
compileSdk = 34
|
||||
compileSdk = 35
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "core.notevault"
|
||||
minSdk = 28
|
||||
targetSdk = 34
|
||||
targetSdk = 35
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
@ -65,4 +65,5 @@ dependencies {
|
||||
// Optional: Unterstützung für Kotlin Coroutines oder RxJava
|
||||
implementation("androidx.room:room-ktx:$room_version") // Für Kotlin-Extensions
|
||||
implementation("androidx.room:room-rxjava3:$room_version") // Für RxJava-Unterstützung
|
||||
implementation("androidx.work:work-runtime:2.10.0")
|
||||
}
|
@ -12,6 +12,8 @@ import android.widget.Toast;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.navigation.fragment.NavHostFragment;
|
||||
import androidx.work.OneTimeWorkRequest;
|
||||
import androidx.work.WorkManager;
|
||||
import com.google.android.material.snackbar.Snackbar;
|
||||
import com.google.android.material.navigation.NavigationView;
|
||||
import androidx.navigation.NavController;
|
||||
@ -22,6 +24,7 @@ import androidx.drawerlayout.widget.DrawerLayout;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import core.notevault.data.*;
|
||||
import core.notevault.databinding.ActivityMainBinding;
|
||||
import core.notevault.sync.SyncWorker;
|
||||
import core.notevault.sync.auth.AuthRepository;
|
||||
import core.notevault.sync.auth.LoginCallback;
|
||||
import core.notevault.ui.gallery.GalleryFragment;
|
||||
@ -35,8 +38,10 @@ import core.notevault.util.NoteSheetsUtil;
|
||||
import java.io.*;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class MainActivity extends AppCompatActivity implements MetaDataDialog.OnMetadataListener,
|
||||
ConcertEditorDialog.OnConcertEditorListener, ConcertSongSelector.OnSongSelectedListener, LoginCallback {
|
||||
@ -68,6 +73,25 @@ public class MainActivity extends AppCompatActivity implements MetaDataDialog.On
|
||||
|
||||
setupLoginButton();
|
||||
musicDB = MusicDatabase.getDatabase(this);
|
||||
scheduleSync();
|
||||
}
|
||||
|
||||
public void scheduleSync() {
|
||||
// Setze eine Uhrzeit für die Synchronisation
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.set(Calendar.HOUR_OF_DAY,11);
|
||||
calendar.set(Calendar.MINUTE, 32);
|
||||
calendar.set(Calendar.SECOND, 0);
|
||||
|
||||
long triggerTime = calendar.getTimeInMillis(); // Zeitstempel für den Trigger
|
||||
|
||||
// Erstelle die OneTimeWorkRequest für den SyncWorker
|
||||
OneTimeWorkRequest syncRequest = new OneTimeWorkRequest.Builder(SyncWorker.class)
|
||||
.setInitialDelay(triggerTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS) // Verzögerung bis zur Ausführung
|
||||
.build();
|
||||
|
||||
// Planen des Workers mit WorkManager
|
||||
WorkManager.getInstance(getBaseContext()).enqueue(syncRequest);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,26 +1,33 @@
|
||||
package core.notevault.data;
|
||||
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Ignore;
|
||||
import androidx.room.PrimaryKey;
|
||||
import androidx.room.*;
|
||||
import core.notevault.data.sync.SyncStatus;
|
||||
import core.notevault.data.sync.SyncStatusConverter;
|
||||
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
|
||||
@Entity(tableName = "concerts")
|
||||
@TypeConverters(SyncStatusConverter.class)
|
||||
public class Concert {
|
||||
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
private int id;
|
||||
|
||||
private String serverSpecificConcertID;
|
||||
private String title;
|
||||
private String concertDate;
|
||||
private SyncStatus syncStatus;
|
||||
|
||||
private LocalDateTime lastModified;
|
||||
|
||||
@Ignore
|
||||
public Concert(String title, String concertDate) {
|
||||
this.title = title;
|
||||
this.concertDate = concertDate;
|
||||
this.lastModified = LocalDateTime.now();
|
||||
}
|
||||
|
||||
public Concert() {
|
||||
@ -56,4 +63,28 @@ public class Concert {
|
||||
public void setConcertDate(String concertDate) {
|
||||
this.concertDate = concertDate;
|
||||
}
|
||||
|
||||
public String getServerSpecificConcertID() {
|
||||
return serverSpecificConcertID;
|
||||
}
|
||||
|
||||
public void setServerSpecificConcertID(String serverSpecificConcertID) {
|
||||
this.serverSpecificConcertID = serverSpecificConcertID;
|
||||
}
|
||||
|
||||
public SyncStatus getSyncStatus() {
|
||||
return syncStatus;
|
||||
}
|
||||
|
||||
public void setSyncStatus(SyncStatus syncStatus) {
|
||||
this.syncStatus = syncStatus;
|
||||
}
|
||||
|
||||
public LocalDateTime getLastModified() {
|
||||
return lastModified;
|
||||
}
|
||||
|
||||
public void setLastModified(LocalDateTime lastModified) {
|
||||
this.lastModified = lastModified;
|
||||
}
|
||||
}
|
||||
|
20
app/src/main/java/core/notevault/data/DateConverter.java
Normal file
20
app/src/main/java/core/notevault/data/DateConverter.java
Normal file
@ -0,0 +1,20 @@
|
||||
package core.notevault.data;
|
||||
|
||||
import androidx.room.TypeConverter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
public class DateConverter {
|
||||
// Konvertiere LocalDateTime in String
|
||||
@TypeConverter
|
||||
public static String fromLocalDateTime(LocalDateTime localDateTime) {
|
||||
return localDateTime == null ? null : localDateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
|
||||
}
|
||||
|
||||
// Konvertiere String zurück in LocalDateTime
|
||||
@TypeConverter
|
||||
public static LocalDateTime toLocalDateTime(String dateTimeString) {
|
||||
return dateTimeString == null ? null : LocalDateTime.parse(dateTimeString, DateTimeFormatter.ISO_LOCAL_DATE_TIME);
|
||||
}
|
||||
}
|
@ -1,11 +1,12 @@
|
||||
package core.notevault.data;
|
||||
|
||||
import android.content.Context;
|
||||
import androidx.room.Room;
|
||||
import androidx.room.RoomDatabase;
|
||||
import androidx.room.Database;
|
||||
import androidx.room.*;
|
||||
import core.notevault.data.sync.SyncResponse;
|
||||
import core.notevault.data.sync.SyncStatusConverter;
|
||||
|
||||
@Database(entities = {MusicNote.class, NoteSheet.class, Concert.class, ConcertSong.class}, version = 2, exportSchema = false)
|
||||
@Database(entities = {MusicNote.class, NoteSheet.class, Concert.class, ConcertSong.class, SyncResponse.class}, version = 2, exportSchema = false)
|
||||
@TypeConverters(DateConverter.class)
|
||||
public abstract class MusicDatabase extends RoomDatabase {
|
||||
public abstract MusicNoteDAO musicNoteDao();
|
||||
|
||||
|
@ -1,7 +1,10 @@
|
||||
package core.notevault.data;
|
||||
|
||||
import androidx.room.*;
|
||||
import core.notevault.data.sync.SyncResponse;
|
||||
import core.notevault.data.sync.SyncStatus;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Dao
|
||||
@ -19,6 +22,9 @@ public interface MusicNoteDAO {
|
||||
@Query("SELECT * FROM note_sheets WHERE musicNoteId = :musicNoteId")
|
||||
List<NoteSheet> getNoteSheetsForMusicSong(long musicNoteId);
|
||||
|
||||
@Query("SELECT * FROM concerts WHERE syncStatus = :status")
|
||||
List<Concert> getConcertsWithSyncStatus(SyncStatus status);
|
||||
|
||||
@Insert
|
||||
void insertConcert(Concert concert);
|
||||
|
||||
@ -43,5 +49,18 @@ public interface MusicNoteDAO {
|
||||
@Query("DELETE FROM concert_songs WHERE concertID = :concertID AND musicNoteID = :songID")
|
||||
void deleteConcertSong(long songID, int concertID);
|
||||
|
||||
@Query("UPDATE concerts SET serverSpecificConcertID = :serverUUID WHERE id = :concertID")
|
||||
@Transaction
|
||||
void updateConcertServerUUID(String serverUUID, int concertID);
|
||||
|
||||
// Methode für das batchweise Update der Konzert-ServerUUIDs
|
||||
@Query("DELETE FROM concerts WHERE serverSpecificConcertID IN (:serverSpecificIDs)")
|
||||
void deleteConcerts(List<String> serverSpecificIDs);
|
||||
|
||||
@Insert
|
||||
@Transaction
|
||||
void insertSyncResponses(List<SyncResponse> syncResponses);
|
||||
|
||||
@Query("SELECT MAX(lastModified) FROM concerts")
|
||||
LocalDateTime getLatestSyncTimestamp();
|
||||
}
|
||||
|
@ -0,0 +1,6 @@
|
||||
package core.notevault.data.sync;
|
||||
|
||||
public enum SyncAction {
|
||||
TOBEUPLOADED,
|
||||
TOBEDOWNLOADED
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package core.notevault.data.sync;
|
||||
|
||||
import androidx.room.TypeConverter;
|
||||
|
||||
public class SyncActionConverter {
|
||||
@TypeConverter
|
||||
public static SyncAction fromInt(int value) {
|
||||
return SyncAction.values()[value];
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static int toInt(SyncAction action) {
|
||||
return action.ordinal();
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package core.notevault.data.sync;
|
||||
|
||||
public enum SyncObject {
|
||||
CONCERT
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package core.notevault.data.sync;
|
||||
|
||||
import androidx.room.TypeConverter;
|
||||
|
||||
public class SyncObjectConverter {
|
||||
@TypeConverter
|
||||
public static SyncObject fromInt(int value) {
|
||||
return SyncObject.values()[value];
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static int toInt(SyncObject object) {
|
||||
return object.ordinal();
|
||||
}
|
||||
}
|
59
app/src/main/java/core/notevault/data/sync/SyncResponse.java
Normal file
59
app/src/main/java/core/notevault/data/sync/SyncResponse.java
Normal file
@ -0,0 +1,59 @@
|
||||
package core.notevault.data.sync;
|
||||
|
||||
|
||||
import androidx.room.Entity;
|
||||
import androidx.room.Ignore;
|
||||
import androidx.room.PrimaryKey;
|
||||
import androidx.room.TypeConverters;
|
||||
|
||||
@Entity(tableName = "sync_actions")
|
||||
@TypeConverters({SyncStatusConverter.class, SyncObjectConverter.class})
|
||||
public class SyncResponse {
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
private long id;
|
||||
private String serverUUID;
|
||||
private SyncAction syncAction;
|
||||
private SyncObject syncObject;
|
||||
|
||||
@Ignore
|
||||
public SyncResponse(String serverUUID, SyncAction syncAction, SyncObject syncObject) {
|
||||
this.serverUUID = serverUUID;
|
||||
this.syncAction = syncAction;
|
||||
this.syncObject = syncObject;
|
||||
}
|
||||
|
||||
public SyncResponse() {
|
||||
}
|
||||
|
||||
public String getServerUUID() {
|
||||
return serverUUID;
|
||||
}
|
||||
|
||||
public void setServerUUID(String serverUUID) {
|
||||
this.serverUUID = serverUUID;
|
||||
}
|
||||
|
||||
public SyncAction getSyncAction() {
|
||||
return syncAction;
|
||||
}
|
||||
|
||||
public void setSyncAction(SyncAction syncAction) {
|
||||
this.syncAction = syncAction;
|
||||
}
|
||||
|
||||
public SyncObject getSyncObject() {
|
||||
return syncObject;
|
||||
}
|
||||
|
||||
public void setSyncObject(SyncObject syncObject) {
|
||||
this.syncObject = syncObject;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(long id) {
|
||||
this.id = id;
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package core.notevault.data.sync;
|
||||
|
||||
public enum SyncStatus {
|
||||
CREATED,
|
||||
DELETED,
|
||||
MODIFIED,
|
||||
REMOTE_MODIFIED,
|
||||
SYNCED;
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package core.notevault.data.sync;
|
||||
|
||||
import androidx.room.TypeConverter;
|
||||
|
||||
public class SyncStatusConverter {
|
||||
@TypeConverter
|
||||
public static SyncStatus fromInt(int value) {
|
||||
return SyncStatus.values()[value];
|
||||
}
|
||||
|
||||
@TypeConverter
|
||||
public static int toInt(SyncStatus syncStatus) {
|
||||
return syncStatus.ordinal();
|
||||
}
|
||||
}
|
@ -4,6 +4,7 @@ import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import core.notevault.sync.auth.AuthInterceptor;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.logging.HttpLoggingInterceptor;
|
||||
import retrofit2.Retrofit;
|
||||
import retrofit2.converter.gson.GsonConverterFactory;
|
||||
|
||||
@ -12,9 +13,13 @@ public class ApiClient {
|
||||
|
||||
public static Retrofit getRetrofitInstance(Context context) {
|
||||
if (retrofit == null) {
|
||||
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
|
||||
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
|
||||
|
||||
SharedPreferences sharedPreferences = context.getSharedPreferences("app_prefs", Context.MODE_PRIVATE);
|
||||
OkHttpClient client = new OkHttpClient.Builder()
|
||||
.addInterceptor(new AuthInterceptor(sharedPreferences))
|
||||
.addInterceptor(loggingInterceptor)
|
||||
.build();
|
||||
|
||||
retrofit = new Retrofit.Builder()
|
||||
|
127
app/src/main/java/core/notevault/sync/SyncWorker.java
Normal file
127
app/src/main/java/core/notevault/sync/SyncWorker.java
Normal file
@ -0,0 +1,127 @@
|
||||
package core.notevault.sync;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.work.Worker;
|
||||
import androidx.work.WorkerParameters;
|
||||
import core.notevault.data.Concert;
|
||||
import core.notevault.data.MusicDatabase;
|
||||
import core.notevault.data.MusicNoteDAO;
|
||||
import core.notevault.data.sync.SyncAction;
|
||||
import core.notevault.data.sync.SyncObject;
|
||||
import core.notevault.data.sync.SyncResponse;
|
||||
import core.notevault.data.sync.SyncStatus;
|
||||
import core.notevault.sync.synchronisation.*;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SyncWorker extends Worker{
|
||||
private SyncService syncService;
|
||||
private MusicNoteDAO database;
|
||||
public SyncWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
|
||||
super(context, workerParams);
|
||||
this.syncService = ApiClient.getRetrofitInstance(context).create(SyncService.class);
|
||||
MusicDatabase musicDatabase = MusicDatabase.getDatabase(context);
|
||||
database = musicDatabase.musicNoteDao();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Result doWork() {
|
||||
SyncRequest concertSync = buildConcertSyncRequest();
|
||||
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
final Result[] result = {Result.failure()};
|
||||
|
||||
syncWithServer(concertSync, new Callback<SyncResponseModel>() {
|
||||
@Override
|
||||
public void onResponse(Call<SyncResponseModel> call, Response<SyncResponseModel> response) {
|
||||
if(response.isSuccessful()) {
|
||||
result[0] = Result.success();
|
||||
Log.d("SyncWorker", "Sync finished, process now");
|
||||
processConcertSynResponse(response.body());
|
||||
} else {
|
||||
Log.d("SyncWorker", "Sync failed");
|
||||
if (response.errorBody() != null) {
|
||||
try {
|
||||
String errorBody = response.errorBody().string();
|
||||
Log.d("SyncWorker", "Error Body: " + errorBody);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
result[0] = Result.failure();
|
||||
}
|
||||
latch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<SyncResponseModel> call, Throwable throwable) {
|
||||
result[0] = Result.failure();
|
||||
Log.d("SyncWorker", "Sync failure");
|
||||
latch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
latch.await();
|
||||
} catch (InterruptedException e) {
|
||||
// throw new RuntimeException(e);
|
||||
}
|
||||
return result[0];
|
||||
}
|
||||
|
||||
private SyncRequest buildConcertSyncRequest() {
|
||||
List<Concert> createdConcerts = database.getConcertsWithSyncStatus(SyncStatus.CREATED);
|
||||
List<Concert> deletedConcerts = database.getConcertsWithSyncStatus(SyncStatus.DELETED);
|
||||
List<Concert> modifiedConcerts = database.getConcertsWithSyncStatus(SyncStatus.MODIFIED);
|
||||
|
||||
|
||||
List<Integer> deviceIDsCreatedConcerts = createdConcerts.stream().map(Concert::getId).collect(Collectors.toList());
|
||||
List<SyncChangeRequest> modifiedSyncRequests = modifiedConcerts.stream().map(concert ->
|
||||
new SyncChangeRequest(concert.getServerSpecificConcertID(), concert.getLastModified())).collect(Collectors.toList());
|
||||
List<SyncChangeRequest> deletedSyncRequests = deletedConcerts.stream().map(concert ->
|
||||
new SyncChangeRequest(concert.getServerSpecificConcertID(), concert.getLastModified())).collect(Collectors.toList());
|
||||
|
||||
Log.d("SyncWorker", "Build SyncRequest");
|
||||
// Datenbankabfrage: Nur IDs mit Zustand "Modified" oder "Deleted" abrufen
|
||||
// Dies ist abhängig von der spezifischen Implementierung der Datenbank
|
||||
return new SyncRequest(database.getLatestSyncTimestamp(), deletedSyncRequests, modifiedSyncRequests, deviceIDsCreatedConcerts);
|
||||
}
|
||||
|
||||
private void syncWithServer(SyncRequest syncRequest, Callback<SyncResponseModel> callback) {
|
||||
syncService.syncConcerts(syncRequest).enqueue(callback);
|
||||
}
|
||||
|
||||
private void processConcertSynResponse(SyncResponseModel syncResponse) {
|
||||
Log.d("SyncWorker", "Process SyncResponse");
|
||||
//Assign local Concert entries the provided Server ID
|
||||
List<SyncResponse> syncResponses = new ArrayList<>();
|
||||
for(SyncCreateResponse createResponse : syncResponse.getCreateResonses()) {
|
||||
database.updateConcertServerUUID(createResponse.getServerObjectUUID(), createResponse.getDeviceSpecificObjectID());
|
||||
syncResponses.add(new SyncResponse(createResponse.getServerObjectUUID(), SyncAction.TOBEUPLOADED, SyncObject.CONCERT));
|
||||
}
|
||||
|
||||
//Delete Concerts that the server has marked as deleted
|
||||
database.deleteConcerts(syncResponse.getToBeDeleted());
|
||||
|
||||
//Persist SyncResponse
|
||||
|
||||
for(String toBeDownloaded : syncResponse.getToBeDownloaded()) {
|
||||
syncResponses.add(new SyncResponse(toBeDownloaded, SyncAction.TOBEDOWNLOADED, SyncObject.CONCERT));
|
||||
}
|
||||
|
||||
for(String toBeUploaded : syncResponse.getToBeUploaded()) {
|
||||
syncResponses.add(new SyncResponse(toBeUploaded, SyncAction.TOBEUPLOADED, SyncObject.CONCERT));
|
||||
}
|
||||
database.insertSyncResponses(syncResponses);
|
||||
}
|
||||
}
|
@ -1,6 +1,7 @@
|
||||
package core.notevault.sync.auth;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.util.Log;
|
||||
import okhttp3.Interceptor;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
@ -13,12 +14,17 @@ public class AuthInterceptor implements Interceptor {
|
||||
|
||||
public AuthInterceptor(SharedPreferences sharedPreferences) {
|
||||
this.sharedPreferences = sharedPreferences;
|
||||
String token = sharedPreferences.getString("jwt_token", null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response intercept(Chain chain) throws IOException {
|
||||
Request originalRequest = chain.request();
|
||||
|
||||
if(originalRequest.url().encodedPath().equals("/api/v1/auth/login")) {
|
||||
return chain.proceed(originalRequest);
|
||||
}
|
||||
|
||||
String token = sharedPreferences.getString("jwt_token", null);
|
||||
if (token == null) {
|
||||
return chain.proceed(originalRequest);
|
||||
|
@ -0,0 +1,29 @@
|
||||
package core.notevault.sync.synchronisation;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
public class SyncChangeRequest {
|
||||
private String serverUUID;
|
||||
private LocalDateTime lastModified;
|
||||
|
||||
public SyncChangeRequest(String serverUUID, LocalDateTime lastModified) {
|
||||
this.serverUUID = serverUUID;
|
||||
this.lastModified = lastModified;
|
||||
}
|
||||
|
||||
public String getServerUUID() {
|
||||
return serverUUID;
|
||||
}
|
||||
|
||||
public void setServerUUID(String serverUUID) {
|
||||
this.serverUUID = serverUUID;
|
||||
}
|
||||
|
||||
public LocalDateTime getLastModified() {
|
||||
return lastModified;
|
||||
}
|
||||
|
||||
public void setLastModified(LocalDateTime lastModified) {
|
||||
this.lastModified = lastModified;
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package core.notevault.sync.synchronisation;
|
||||
|
||||
public class SyncCreateResponse {
|
||||
private String serverObjectUUID;
|
||||
private int deviceSpecificObjectID;
|
||||
|
||||
public String getServerObjectUUID() {
|
||||
return serverObjectUUID;
|
||||
}
|
||||
|
||||
public void setServerObjectUUID(String serverObjectUUID) {
|
||||
this.serverObjectUUID = serverObjectUUID;
|
||||
}
|
||||
|
||||
public int getDeviceSpecificObjectID() {
|
||||
return deviceSpecificObjectID;
|
||||
}
|
||||
|
||||
public void setDeviceSpecificObjectID(int deviceSpecificObjectID) {
|
||||
this.deviceSpecificObjectID = deviceSpecificObjectID;
|
||||
}
|
||||
}
|
@ -0,0 +1,50 @@
|
||||
package core.notevault.sync.synchronisation;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
public class SyncRequest {
|
||||
private LocalDateTime lastSync;
|
||||
private List<SyncChangeRequest> deletedConcerts;
|
||||
private List<SyncChangeRequest> modifiedConcerts;
|
||||
private List<Integer> createdConcerts;
|
||||
|
||||
public SyncRequest(LocalDateTime lastSync, List<SyncChangeRequest> deletedConcerts, List<SyncChangeRequest> modifiedConcerts, List<Integer> createdConcerts) {
|
||||
this.lastSync = lastSync;
|
||||
this.deletedConcerts = deletedConcerts;
|
||||
this.modifiedConcerts = modifiedConcerts;
|
||||
this.createdConcerts = createdConcerts;
|
||||
}
|
||||
|
||||
public LocalDateTime getLastSync() {
|
||||
return lastSync;
|
||||
}
|
||||
|
||||
public void setLastSync(LocalDateTime lastSync) {
|
||||
this.lastSync = lastSync;
|
||||
}
|
||||
|
||||
public List<SyncChangeRequest> getDeletedConcerts() {
|
||||
return deletedConcerts;
|
||||
}
|
||||
|
||||
public void setDeletedConcerts(List<SyncChangeRequest> deletedConcerts) {
|
||||
this.deletedConcerts = deletedConcerts;
|
||||
}
|
||||
|
||||
public List<SyncChangeRequest> getModifiedConcerts() {
|
||||
return modifiedConcerts;
|
||||
}
|
||||
|
||||
public void setModifiedConcerts(List<SyncChangeRequest> modifiedConcerts) {
|
||||
this.modifiedConcerts = modifiedConcerts;
|
||||
}
|
||||
|
||||
public List<Integer> getCreatedConcerts() {
|
||||
return createdConcerts;
|
||||
}
|
||||
|
||||
public void setCreatedConcerts(List<Integer> createdConcerts) {
|
||||
this.createdConcerts = createdConcerts;
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package core.notevault.sync.synchronisation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SyncResponseModel {
|
||||
private List<String> toBeUploaded;
|
||||
private List<String> toBeDownloaded;
|
||||
private List<String> toBeDeleted;
|
||||
private List<SyncCreateResponse> createResonses;
|
||||
|
||||
public List<String> getToBeUploaded() {
|
||||
return toBeUploaded;
|
||||
}
|
||||
|
||||
public void setToBeUploaded(List<String> toBeUploaded) {
|
||||
this.toBeUploaded = toBeUploaded;
|
||||
}
|
||||
|
||||
public List<String> getToBeDownloaded() {
|
||||
return toBeDownloaded;
|
||||
}
|
||||
|
||||
public void setToBeDownloaded(List<String> toBeDownloaded) {
|
||||
this.toBeDownloaded = toBeDownloaded;
|
||||
}
|
||||
|
||||
public List<String> getToBeDeleted() {
|
||||
return toBeDeleted;
|
||||
}
|
||||
|
||||
public void setToBeDeleted(List<String> toBeDeleted) {
|
||||
this.toBeDeleted = toBeDeleted;
|
||||
}
|
||||
|
||||
public List<SyncCreateResponse> getCreateResonses() {
|
||||
return createResonses;
|
||||
}
|
||||
|
||||
public void setCreateResonses(List<SyncCreateResponse> createResonses) {
|
||||
this.createResonses = createResonses;
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package core.notevault.sync.synchronisation;
|
||||
|
||||
import core.notevault.data.sync.SyncResponse;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.Body;
|
||||
import retrofit2.http.POST;
|
||||
|
||||
public interface SyncService {
|
||||
|
||||
@POST("api/v1/sync/concerts")
|
||||
Call<SyncResponseModel> syncConcerts(@Body SyncRequest syncRequest);
|
||||
}
|
Loading…
Reference in New Issue
Block a user