ADD: Handle Modified NoteSheeds

This commit is contained in:
Fawkes100 2025-01-19 13:41:09 +01:00
parent d221502bef
commit 007f6a8501
10 changed files with 224 additions and 29 deletions

View File

@ -60,4 +60,13 @@ public interface SongDao {
@Query("SELECT localID FROM Song WHERE serverID IN (:serverIDs)")
List<Integer> getLocalSongIDsByServerIDs(List<String> serverIDs);
@Query("SELECT * FROM NoteSheet WHERE serverFileName IN (:serverFileNames)")
List<NoteSheet> getNoteSheetsByServerFileNames(List<String> serverFileNames);
@Query("SELECT * FROM NoteSheet WHERE syncStatus = :status")
List<NoteSheet> getNoteSheetsBySyncStatus(SyncStatus status);
@Query("SELECT * FROM NoteSheet WHERE songID IN (:localSongIDs)")
List<NoteSheet> getNoteSheetFilesBySongIDs(List<Integer> localSongIDs);
}

View File

@ -2,6 +2,7 @@ package com.stormtales.notevault.data.entities;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
import com.stormtales.notevault.data.sync.SyncStatus;
import java.util.Objects;
@ -14,9 +15,12 @@ public class NoteSheet {
private String serverFileName;
private String hash;
private SyncStatus syncStatus;
public NoteSheet(String localFileName, String hash) {
this.localFileName = localFileName;
this.hash = hash;
this.syncStatus = SyncStatus.CREATED;
}
public int getLocalID() {
@ -70,4 +74,12 @@ public class NoteSheet {
public void setSongID(int songID) {
this.songID = songID;
}
public SyncStatus getSyncStatus() {
return syncStatus;
}
public void setSyncStatus(SyncStatus syncStatus) {
this.syncStatus = syncStatus;
}
}

View File

@ -3,20 +3,20 @@ package com.stormtales.notevault.data.repositories;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import androidx.recyclerview.widget.AsyncListUtil;
import com.stormtales.notevault.data.MusicDatabase;
import com.stormtales.notevault.data.dao.SongDao;
import com.stormtales.notevault.data.entities.NoteSheet;
import com.stormtales.notevault.data.entities.Song;
import com.stormtales.notevault.data.sync.SyncStatus;
import com.stormtales.notevault.network.sync.models.BatchCreateResponse;
import com.stormtales.notevault.network.sync.models.BatchModifyResponse;
import com.stormtales.notevault.network.sync.models.CreateResponse;
import com.stormtales.notevault.network.sync.models.UploadResponse;
import com.stormtales.notevault.network.sync.models.*;
import java.io.File;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
@ -36,11 +36,16 @@ public class SongSyncRepository {
});
}
public void loadModifiedSongs(LoadDataCallback<List<Song>> callback) {
public void loadModifiedSongs(LoadDataCallback<Map<Song, List<NoteSheet>>> callback) {
Executors.newSingleThreadExecutor().execute(() -> {
Map<Song, List<NoteSheet>> result = new HashMap<>();
List<Song> modifiedSongs = songDao.getSongsBySyncStatus(SyncStatus.MODIFIED);
for(Song song : modifiedSongs) {
List<NoteSheet> noteSheets = songDao.getNoteSheetsBySong(song.getLocalID());
result.put(song, noteSheets);
}
Handler mainHandler = new Handler(Looper.getMainLooper());
mainHandler.post(()-> callback.onResult(modifiedSongs));
mainHandler.post(()-> callback.onResult(result));
});
}
@ -78,13 +83,27 @@ public class SongSyncRepository {
}
}
public void markModifiedSongsAsSynced(BatchModifyResponse response) {
public void markModifiedSongsAsSynced(SongModifyBatchResponse modifyBatchResponse) {
Executors.newSingleThreadExecutor().execute(() -> {
List<Song> requestedSongs = songDao.getSongsByServerIDs(response.getModifiedServerObjects());
List<String> serverIDs = modifyBatchResponse.getModifiedServerObjects().stream().map(SongModifyResponse::getServerID).collect(Collectors.toList());
List<Song> requestedSongs = songDao.getSongsByServerIDs(serverIDs);
for(Song song : requestedSongs) {
song.setSyncStatus(SyncStatus.SYNCED);
song.setSyncTime(LocalDateTime.now());
for(SongModifyResponse modifyResponse : modifyBatchResponse.getModifiedServerObjects()) {
if(modifyResponse.getServerID().equals(song.getServerID())) {
List<NoteSheet> outdatedNoteSheets = songDao.getNoteSheetsByServerFileNames(modifyResponse.getOutdated_note_sheets());
for(NoteSheet noteSheet : outdatedNoteSheets) {
noteSheet.setSyncStatus(SyncStatus.MODIFIED);
}
songDao.updateNoteSheets(outdatedNoteSheets);
}
}
}
songDao.updateSongs(requestedSongs);
});
}
@ -96,9 +115,9 @@ public class SongSyncRepository {
});
}
public void getNoteSheetFilesBySongIDs(List<Integer> songIDs, LoadDataCallback<List<NoteSheet>> callback) {
public void getNoteSheetFilesBySongIDs(LoadDataCallback<List<NoteSheet>> callback) {
Executors.newSingleThreadExecutor().execute(() -> {
List<NoteSheet> noteSheets = songDao.getNoteSheetsBySongIDs(songIDs);
List<NoteSheet> noteSheets = songDao.getNoteSheetsBySyncStatus(SyncStatus.CREATED);
Handler mainHandler = new Handler(Looper.getMainLooper());
mainHandler.post(()-> callback.onResult(noteSheets));
});
@ -114,6 +133,7 @@ public class SongSyncRepository {
for(NoteSheet noteSheet : uploadedNoteSheets) {
if(new File(noteSheet.getLocalFileName()).getName().equals(uploadResponse.getLocalFile())) {
noteSheet.setServerFileName(uploadResponse.getServerFile());
noteSheet.setSyncStatus(SyncStatus.SYNCED);
break;
}
}
@ -122,6 +142,27 @@ public class SongSyncRepository {
});
}
public void loadModifiedNoteSheets(LoadDataCallback<List<NoteSheet>> callback) {
Executors.newSingleThreadExecutor().execute(() -> {
List<NoteSheet> noteSheets = songDao.getNoteSheetsBySyncStatus(SyncStatus.MODIFIED);
Handler mainHandler = new Handler(Looper.getMainLooper());
mainHandler.post(()-> callback.onResult(noteSheets));
});
}
public void markModifiedNoteSheetsAsSynced(List<UploadResponse> uploadResponses) {
Executors.newSingleThreadExecutor().execute(() -> {
List<String> serverFileNames = uploadResponses.stream().map(UploadResponse::getServerFile).collect(Collectors.toList());
List<NoteSheet> noteSheets = songDao.getNoteSheetsByServerFileNames(serverFileNames);
for(NoteSheet noteSheet : noteSheets) {
noteSheet.setSyncStatus(SyncStatus.SYNCED);
}
songDao.updateNoteSheets(noteSheets);
});
}
public interface LoadDataCallback<T> {
void onResult(T result);
}

View File

@ -17,7 +17,7 @@ public interface SongSyncAPI {
Call<BatchCreateResponse> syncCreatedSongs(@Body SongCreateBatchRequest songCreateBatchRequest);
@POST("/sync/songs/modify")
Call<BatchModifyResponse> syncModifiedSongs(@Body SongModifyBatchRequest songModifyBatchRequest);
Call<SongModifyBatchResponse> syncModifiedSongs(@Body SongModifyBatchRequest songModifyBatchRequest);
@POST("/sync/songs/delete")
Call<BatchModifyResponse> syncDeletedSongs(@Body SongBatchDeleteRequest songBatchDeleteRequest);
@ -25,4 +25,8 @@ public interface SongSyncAPI {
@Multipart
@POST("/sync/songs/note_sheet/upload")
Call<UploadResponse> uploadNoteSheet(@Part("serverID")RequestBody serverID, @Part("fileName") RequestBody fileName, @Part MultipartBody.Part image);
@Multipart
@POST("/sync/songs/note_sheet/update")
Call<UploadResponse> updateNoteSheet(@Part("server_filename") RequestBody server_filename, @Part MultipartBody.Part image);
}

View File

@ -6,6 +6,7 @@ import com.stormtales.notevault.data.repositories.SongRepository;
import com.stormtales.notevault.data.repositories.SongSyncRepository;
import com.stormtales.notevault.network.sync.models.BatchCreateResponse;
import com.stormtales.notevault.network.sync.models.BatchModifyResponse;
import com.stormtales.notevault.network.sync.models.SongModifyBatchResponse;
import com.stormtales.notevault.network.sync.models.UploadResponse;
import com.stormtales.notevault.ui.gallery.GalleryViewModel;
@ -33,23 +34,39 @@ public class SongSyncModule {
if(response.getCreateResponses().isEmpty()) {
syncViewModel.finishCreateSongSyncing();
} else {
List<Integer> songIDs = result.stream().map(Song::getLocalID).collect(Collectors.toList());
songSyncRepository.getNoteSheetFilesBySongIDs(songIDs, noteSheets -> {
songSyncService.uploadNoteSheetsOfCreatedSongs(noteSheets, response, uploadResponses -> {
songSyncRepository.markCreatedNoteSheetsAsSynced(uploadResponses);
syncViewModel.finishCreateSongSyncing();
});
});
uploadCreatedNoteSheets(result, response);
}
});
});
}
public void uploadCreatedNoteSheets(List<Song> result, BatchCreateResponse response) {
List<Integer> songIDs = result.stream().map(Song::getLocalID).collect(Collectors.toList());
songSyncRepository.getNoteSheetFilesBySongIDs(noteSheets -> {
songSyncService.uploadNoteSheetsOfCreatedSongs(noteSheets, response, uploadResponses -> {
songSyncRepository.markCreatedNoteSheetsAsSynced(uploadResponses);
syncViewModel.finishCreateSongSyncing();
});
});
}
public void syncModifiedSongs() {
songSyncRepository.loadModifiedSongs(result -> {
songSyncService.syncModifiedSongs(result, response -> {
songSyncService.syncModifiedSongs(result, (FinishSongSyncingCallback<SongModifyBatchResponse>) response -> {
songSyncRepository.markModifiedSongsAsSynced(response);
syncViewModel.finishModifiedSongSyncinc();
uploadModifiedNoteSheets();
});
});
}
public void uploadModifiedNoteSheets() {
songSyncRepository.loadModifiedNoteSheets(modifiedNoteSheets -> {
songSyncService.uploadModifiedNoteSheets(modifiedNoteSheets, new SongSyncService.UploadNoteSheetCallback() {
@Override
public void finishUploadNoteSheets(List<UploadResponse> uploadResponses) {
songSyncRepository.markModifiedNoteSheetsAsSynced(uploadResponses);
syncViewModel.finishModifiedSongSyncinc();
}
});
});
}
@ -67,7 +84,7 @@ public class SongSyncModule {
void finishSongSyncing(BatchCreateResponse response);
}
public interface FinishSongSyncingCallback {
void finishSongSyncing(BatchModifyResponse response);
public interface FinishSongSyncingCallback<T> {
void finishSongSyncing(T response);
}
}

View File

@ -1,6 +1,7 @@
package com.stormtales.notevault.network.sync;
import android.content.Context;
import android.util.Log;
import com.stormtales.notevault.data.entities.NoteSheet;
import com.stormtales.notevault.data.entities.Song;
import com.stormtales.notevault.network.NetworkModule;
@ -17,6 +18,7 @@ import retrofit2.Response;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
public class SongSyncService {
private final SongSyncAPI songSyncAPI;
@ -79,20 +81,20 @@ public class SongSyncService {
}
}
public void syncModifiedSongs(List<Song> songs, SongSyncModule.FinishSongSyncingCallback callback) {
public void syncModifiedSongs(Map<Song, List<NoteSheet>> songs, SongSyncModule.FinishSongSyncingCallback<SongModifyBatchResponse> callback) {
List<SongModifyRequest> modifyRequests = new ArrayList<>();
for(Song song : songs) {
modifyRequests.add(new SongModifyRequest(song));
for(Map.Entry<Song, List<NoteSheet>> entry: songs.entrySet()) {
modifyRequests.add(new SongModifyRequest(entry.getKey(), entry.getValue()));
}
SongModifyBatchRequest songModifyBatchRequest = new SongModifyBatchRequest(modifyRequests);
songSyncAPI.syncModifiedSongs(songModifyBatchRequest).enqueue(new Callback<BatchModifyResponse>() {
songSyncAPI.syncModifiedSongs(songModifyBatchRequest).enqueue(new Callback<SongModifyBatchResponse>() {
@Override
public void onResponse(Call<BatchModifyResponse> call, Response<BatchModifyResponse> response) {
public void onResponse(Call<SongModifyBatchResponse> call, Response<SongModifyBatchResponse> response) {
callback.finishSongSyncing(response.body());
}
@Override
public void onFailure(Call<BatchModifyResponse> call, Throwable throwable) {
public void onFailure(Call<SongModifyBatchResponse> call, Throwable throwable) {
}
});
@ -125,6 +127,34 @@ public class SongSyncService {
});
}
public void uploadModifiedNoteSheets(List<NoteSheet> modifiedNoteSheets, UploadNoteSheetCallback callback) {
List<UploadResponse> uploadResponses = new ArrayList<>();
for(NoteSheet noteSheet : modifiedNoteSheets) {
RequestBody server_filename = RequestBody.create(MediaType.parse("text/plain"), noteSheet.getServerFileName());
RequestBody requestFile = RequestBody.create(MediaType.parse("image/**"), new File(noteSheet.getLocalFileName()));
MultipartBody.Part image = MultipartBody.Part.createFormData("image", noteSheet.getLocalFileName(), requestFile);
songSyncAPI.updateNoteSheet(server_filename, image).enqueue(new Callback<UploadResponse>() {
@Override
public void onResponse(Call<UploadResponse> call, Response<UploadResponse> response) {
if(response.isSuccessful() && response.body() != null) {
uploadResponses.add(response.body());
if(uploadResponses.size() == modifiedNoteSheets.size()) {
callback.finishUploadNoteSheets(uploadResponses);
}
} else {
Log.d("SongSyncService", "Something went wrong");
}
}
@Override
public void onFailure(Call<UploadResponse> call, Throwable throwable) {
Log.e("SongSyncService", "Upload failed: " + throwable.getMessage(), throwable);
}
});
}
}
public interface SyncDeletedSongsCallback {

View File

@ -0,0 +1,27 @@
package com.stormtales.notevault.network.sync.models;
public class NoteSheetModifyRequest {
private String serverFileName;
private String clientHash;
public NoteSheetModifyRequest(String serverFileName, String clientHash) {
this.serverFileName = serverFileName;
this.clientHash = clientHash;
}
public String getServerFileName() {
return serverFileName;
}
public void setServerFileName(String serverFileName) {
this.serverFileName = serverFileName;
}
public String getClientHash() {
return clientHash;
}
public void setClientHash(String clientHash) {
this.clientHash = clientHash;
}
}

View File

@ -0,0 +1,16 @@
package com.stormtales.notevault.network.sync.models;
import java.util.List;
public class SongModifyBatchResponse {
private List<SongModifyResponse> modifiedServerObjects;
public List<SongModifyResponse> getModifiedServerObjects() {
return modifiedServerObjects;
}
public void setModifiedServerObjects(List<SongModifyResponse> modifiedServerObjects) {
this.modifiedServerObjects = modifiedServerObjects;
}
}

View File

@ -1,7 +1,11 @@
package com.stormtales.notevault.network.sync.models;
import com.stormtales.notevault.data.entities.NoteSheet;
import com.stormtales.notevault.data.entities.Song;
import java.util.List;
import java.util.stream.Collectors;
public class SongModifyRequest {
private String serverID;
@ -9,6 +13,7 @@ public class SongModifyRequest {
private String composer;
private String genre;
private int year;
private List<NoteSheetModifyRequest> noteSheets;
public SongModifyRequest(String serverID, String title, String composer, String genre, int year) {
this.serverID = serverID;
@ -18,12 +23,13 @@ public class SongModifyRequest {
this.year = year;
}
public SongModifyRequest(Song song) {
public SongModifyRequest(Song song, List<NoteSheet> noteSheets) {
this.serverID = song.getServerID();
this.title = song.getTitle();
this.composer = song.getComposer();
this.genre = song.getGenre();
this.year = song.getYear();
this.noteSheets = noteSheets.stream().map(noteSheet -> new NoteSheetModifyRequest(noteSheet.getServerFileName(), noteSheet.getHash())).collect(Collectors.toList());
}
public String getServerID() {
@ -65,4 +71,12 @@ public class SongModifyRequest {
public void setYear(int year) {
this.year = year;
}
public List<NoteSheetModifyRequest> getNoteSheets() {
return noteSheets;
}
public void setNoteSheets(List<NoteSheetModifyRequest> noteSheets) {
this.noteSheets = noteSheets;
}
}

View File

@ -0,0 +1,25 @@
package com.stormtales.notevault.network.sync.models;
import java.util.List;
public class SongModifyResponse {
private String serverID;
private List<String> outdated_note_sheets;
public List<String> getOutdated_note_sheets() {
return outdated_note_sheets;
}
public void setOutdated_note_sheets(List<String> outdated_note_sheets) {
this.outdated_note_sheets = outdated_note_sheets;
}
public String getServerID() {
return serverID;
}
public void setServerID(String serverID) {
this.serverID = serverID;
}
}