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 {
|
android {
|
||||||
namespace = "core.notevault"
|
namespace = "core.notevault"
|
||||||
compileSdk = 34
|
compileSdk = 35
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "core.notevault"
|
applicationId = "core.notevault"
|
||||||
minSdk = 28
|
minSdk = 28
|
||||||
targetSdk = 34
|
targetSdk = 35
|
||||||
versionCode = 1
|
versionCode = 1
|
||||||
versionName = "1.0"
|
versionName = "1.0"
|
||||||
|
|
||||||
@ -65,4 +65,5 @@ dependencies {
|
|||||||
// Optional: Unterstützung für Kotlin Coroutines oder RxJava
|
// Optional: Unterstützung für Kotlin Coroutines oder RxJava
|
||||||
implementation("androidx.room:room-ktx:$room_version") // Für Kotlin-Extensions
|
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.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.Fragment;
|
||||||
import androidx.fragment.app.FragmentManager;
|
import androidx.fragment.app.FragmentManager;
|
||||||
import androidx.navigation.fragment.NavHostFragment;
|
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.snackbar.Snackbar;
|
||||||
import com.google.android.material.navigation.NavigationView;
|
import com.google.android.material.navigation.NavigationView;
|
||||||
import androidx.navigation.NavController;
|
import androidx.navigation.NavController;
|
||||||
@ -22,6 +24,7 @@ import androidx.drawerlayout.widget.DrawerLayout;
|
|||||||
import androidx.appcompat.app.AppCompatActivity;
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
import core.notevault.data.*;
|
import core.notevault.data.*;
|
||||||
import core.notevault.databinding.ActivityMainBinding;
|
import core.notevault.databinding.ActivityMainBinding;
|
||||||
|
import core.notevault.sync.SyncWorker;
|
||||||
import core.notevault.sync.auth.AuthRepository;
|
import core.notevault.sync.auth.AuthRepository;
|
||||||
import core.notevault.sync.auth.LoginCallback;
|
import core.notevault.sync.auth.LoginCallback;
|
||||||
import core.notevault.ui.gallery.GalleryFragment;
|
import core.notevault.ui.gallery.GalleryFragment;
|
||||||
@ -35,8 +38,10 @@ import core.notevault.util.NoteSheetsUtil;
|
|||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Calendar;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
public class MainActivity extends AppCompatActivity implements MetaDataDialog.OnMetadataListener,
|
public class MainActivity extends AppCompatActivity implements MetaDataDialog.OnMetadataListener,
|
||||||
ConcertEditorDialog.OnConcertEditorListener, ConcertSongSelector.OnSongSelectedListener, LoginCallback {
|
ConcertEditorDialog.OnConcertEditorListener, ConcertSongSelector.OnSongSelectedListener, LoginCallback {
|
||||||
@ -68,6 +73,25 @@ public class MainActivity extends AppCompatActivity implements MetaDataDialog.On
|
|||||||
|
|
||||||
setupLoginButton();
|
setupLoginButton();
|
||||||
musicDB = MusicDatabase.getDatabase(this);
|
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
|
@Override
|
||||||
|
@ -1,26 +1,33 @@
|
|||||||
package core.notevault.data;
|
package core.notevault.data;
|
||||||
|
|
||||||
import androidx.room.Entity;
|
import androidx.room.*;
|
||||||
import androidx.room.Ignore;
|
import core.notevault.data.sync.SyncStatus;
|
||||||
import androidx.room.PrimaryKey;
|
import core.notevault.data.sync.SyncStatusConverter;
|
||||||
|
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
@Entity(tableName = "concerts")
|
@Entity(tableName = "concerts")
|
||||||
|
@TypeConverters(SyncStatusConverter.class)
|
||||||
public class Concert {
|
public class Concert {
|
||||||
|
|
||||||
@PrimaryKey(autoGenerate = true)
|
@PrimaryKey(autoGenerate = true)
|
||||||
private int id;
|
private int id;
|
||||||
|
|
||||||
|
private String serverSpecificConcertID;
|
||||||
private String title;
|
private String title;
|
||||||
private String concertDate;
|
private String concertDate;
|
||||||
|
private SyncStatus syncStatus;
|
||||||
|
|
||||||
|
private LocalDateTime lastModified;
|
||||||
|
|
||||||
@Ignore
|
@Ignore
|
||||||
public Concert(String title, String concertDate) {
|
public Concert(String title, String concertDate) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.concertDate = concertDate;
|
this.concertDate = concertDate;
|
||||||
|
this.lastModified = LocalDateTime.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Concert() {
|
public Concert() {
|
||||||
@ -56,4 +63,28 @@ public class Concert {
|
|||||||
public void setConcertDate(String concertDate) {
|
public void setConcertDate(String concertDate) {
|
||||||
this.concertDate = 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;
|
package core.notevault.data;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import androidx.room.Room;
|
import androidx.room.*;
|
||||||
import androidx.room.RoomDatabase;
|
import core.notevault.data.sync.SyncResponse;
|
||||||
import androidx.room.Database;
|
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 class MusicDatabase extends RoomDatabase {
|
||||||
public abstract MusicNoteDAO musicNoteDao();
|
public abstract MusicNoteDAO musicNoteDao();
|
||||||
|
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
package core.notevault.data;
|
package core.notevault.data;
|
||||||
|
|
||||||
import androidx.room.*;
|
import androidx.room.*;
|
||||||
|
import core.notevault.data.sync.SyncResponse;
|
||||||
|
import core.notevault.data.sync.SyncStatus;
|
||||||
|
|
||||||
|
import java.time.LocalDateTime;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@Dao
|
@Dao
|
||||||
@ -19,6 +22,9 @@ public interface MusicNoteDAO {
|
|||||||
@Query("SELECT * FROM note_sheets WHERE musicNoteId = :musicNoteId")
|
@Query("SELECT * FROM note_sheets WHERE musicNoteId = :musicNoteId")
|
||||||
List<NoteSheet> getNoteSheetsForMusicSong(long musicNoteId);
|
List<NoteSheet> getNoteSheetsForMusicSong(long musicNoteId);
|
||||||
|
|
||||||
|
@Query("SELECT * FROM concerts WHERE syncStatus = :status")
|
||||||
|
List<Concert> getConcertsWithSyncStatus(SyncStatus status);
|
||||||
|
|
||||||
@Insert
|
@Insert
|
||||||
void insertConcert(Concert concert);
|
void insertConcert(Concert concert);
|
||||||
|
|
||||||
@ -43,5 +49,18 @@ public interface MusicNoteDAO {
|
|||||||
@Query("DELETE FROM concert_songs WHERE concertID = :concertID AND musicNoteID = :songID")
|
@Query("DELETE FROM concert_songs WHERE concertID = :concertID AND musicNoteID = :songID")
|
||||||
void deleteConcertSong(long songID, int concertID);
|
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 android.content.SharedPreferences;
|
||||||
import core.notevault.sync.auth.AuthInterceptor;
|
import core.notevault.sync.auth.AuthInterceptor;
|
||||||
import okhttp3.OkHttpClient;
|
import okhttp3.OkHttpClient;
|
||||||
|
import okhttp3.logging.HttpLoggingInterceptor;
|
||||||
import retrofit2.Retrofit;
|
import retrofit2.Retrofit;
|
||||||
import retrofit2.converter.gson.GsonConverterFactory;
|
import retrofit2.converter.gson.GsonConverterFactory;
|
||||||
|
|
||||||
@ -12,9 +13,13 @@ public class ApiClient {
|
|||||||
|
|
||||||
public static Retrofit getRetrofitInstance(Context context) {
|
public static Retrofit getRetrofitInstance(Context context) {
|
||||||
if (retrofit == null) {
|
if (retrofit == null) {
|
||||||
|
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
|
||||||
|
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
|
||||||
|
|
||||||
SharedPreferences sharedPreferences = context.getSharedPreferences("app_prefs", Context.MODE_PRIVATE);
|
SharedPreferences sharedPreferences = context.getSharedPreferences("app_prefs", Context.MODE_PRIVATE);
|
||||||
OkHttpClient client = new OkHttpClient.Builder()
|
OkHttpClient client = new OkHttpClient.Builder()
|
||||||
.addInterceptor(new AuthInterceptor(sharedPreferences))
|
.addInterceptor(new AuthInterceptor(sharedPreferences))
|
||||||
|
.addInterceptor(loggingInterceptor)
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
retrofit = new Retrofit.Builder()
|
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;
|
package core.notevault.sync.auth;
|
||||||
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.util.Log;
|
||||||
import okhttp3.Interceptor;
|
import okhttp3.Interceptor;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
import okhttp3.Response;
|
import okhttp3.Response;
|
||||||
@ -13,12 +14,17 @@ public class AuthInterceptor implements Interceptor {
|
|||||||
|
|
||||||
public AuthInterceptor(SharedPreferences sharedPreferences) {
|
public AuthInterceptor(SharedPreferences sharedPreferences) {
|
||||||
this.sharedPreferences = sharedPreferences;
|
this.sharedPreferences = sharedPreferences;
|
||||||
|
String token = sharedPreferences.getString("jwt_token", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Response intercept(Chain chain) throws IOException {
|
public Response intercept(Chain chain) throws IOException {
|
||||||
Request originalRequest = chain.request();
|
Request originalRequest = chain.request();
|
||||||
|
|
||||||
|
if(originalRequest.url().encodedPath().equals("/api/v1/auth/login")) {
|
||||||
|
return chain.proceed(originalRequest);
|
||||||
|
}
|
||||||
|
|
||||||
String token = sharedPreferences.getString("jwt_token", null);
|
String token = sharedPreferences.getString("jwt_token", null);
|
||||||
if (token == null) {
|
if (token == null) {
|
||||||
return chain.proceed(originalRequest);
|
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