ADD: Fetch Remotely Modified Songs

This commit is contained in:
Fawkes100 2025-01-19 14:28:19 +01:00
parent 007f6a8501
commit a53bc4bee7
8 changed files with 146 additions and 8 deletions

View File

@ -163,6 +163,16 @@ public class SongSyncRepository {
});
}
public void markSongsAsRemotelyModified(List<String> serverIDs) {
Executors.newSingleThreadExecutor().execute(() -> {
List<Song> songs = songDao.getSongsByServerIDs(serverIDs);
for(Song song : songs) {
song.setSyncStatus(SyncStatus.REMOTE_MODIFIED);
}
songDao.updateSongs(songs);
});
}
public interface LoadDataCallback<T> {
void onResult(T result);
}

View File

@ -6,10 +6,9 @@ import com.stormtales.notevault.network.sync.models.*;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
import retrofit2.http.*;
import java.time.LocalDateTime;
public interface SongSyncAPI {
@ -29,4 +28,7 @@ public interface SongSyncAPI {
@Multipart
@POST("/sync/songs/note_sheet/update")
Call<UploadResponse> updateNoteSheet(@Part("server_filename") RequestBody server_filename, @Part MultipartBody.Part image);
@GET("/sync/songs/fetch")
Call<FetchResponse> fetchRemoteModifiedSongs(@Query(value = "last_client_sync") String last_client_sync);
}

View File

@ -4,12 +4,10 @@ import android.content.Context;
import com.stormtales.notevault.data.entities.Song;
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.network.sync.models.*;
import com.stormtales.notevault.ui.gallery.GalleryViewModel;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
@ -80,6 +78,17 @@ public class SongSyncModule {
});
}
public void fetchRemoteModifiedSongs() {
//Todo: Determine Last Client Sync; for testing use LocalDateTime.MIN
songSyncService.fetchRemoteModifiedSongs(LocalDateTime.now().minusDays(35), new SongSyncRepository.LoadDataCallback<FetchResponse>() {
@Override
public void onResult(FetchResponse result) {
songSyncRepository.markSongsAsRemotelyModified(result.getServerIDs());
syncViewModel.finishFetching();
}
});
}
public interface FinishSongCreateSyncingCallback {
void finishSongSyncing(BatchCreateResponse response);
}

View File

@ -4,6 +4,7 @@ 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.data.repositories.SongSyncRepository;
import com.stormtales.notevault.network.NetworkModule;
import com.stormtales.notevault.network.auth.AuthAPI;
import com.stormtales.notevault.network.sync.models.*;
@ -16,6 +17,8 @@ import retrofit2.Callback;
import retrofit2.Response;
import java.io.File;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@ -156,6 +159,23 @@ public class SongSyncService {
}
}
public void fetchRemoteModifiedSongs(LocalDateTime lastClientSync, SongSyncRepository.LoadDataCallback<FetchResponse> callback) {
String formattedTime = lastClientSync.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
songSyncAPI.fetchRemoteModifiedSongs(formattedTime).enqueue(new Callback<FetchResponse>() {
@Override
public void onResponse(Call<FetchResponse> call, Response<FetchResponse> response) {
if(response.isSuccessful() && response.body() != null) {
callback.onResult(response.body());
}
}
@Override
public void onFailure(Call<FetchResponse> call, Throwable throwable) {
Log.d("SongSyncService", "Fetch failed: " + throwable.getMessage(), throwable);
}
});
}
public interface SyncDeletedSongsCallback {
void finishSongSyncing(List<String> remoteDeletedSongs, List<Integer> localDeletedSongs);

View File

@ -0,0 +1,15 @@
package com.stormtales.notevault.network.sync.models;
import java.util.List;
public class FetchResponse {
private List<String> serverIDs;
public List<String> getServerIDs() {
return serverIDs;
}
public void setServerIDs(List<String> serverIDs) {
this.serverIDs = serverIDs;
}
}

View File

@ -27,6 +27,9 @@ public class GalleryFragment extends Fragment {
private ProgressBar progress_sync_deleted_songs;
private Button sync_deleted_songs_btn;
private ProgressBar progress_fetching_songs;
private Button sync_fetching_songs_btn;
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
@ -47,6 +50,10 @@ public class GalleryFragment extends Fragment {
sync_deleted_songs_btn = binding.syncDeletedBtn;
sync_deleted_songs_btn.setOnClickListener(v -> onSyncDeletedSongs());
progress_fetching_songs = binding.progressFetchSongs;
sync_fetching_songs_btn = binding.fetchSongsBtn;
sync_fetching_songs_btn.setOnClickListener(v -> onFetchRemoteModifiedSongs());
galleryViewModel.setSongSyncModule(new SongSyncModule(getContext(), galleryViewModel));
galleryViewModel.getIsCreatedSongSyncing().observe(getViewLifecycleOwner(), isCreatedSyncinc -> {
@ -78,9 +85,23 @@ public class GalleryFragment extends Fragment {
sync_deleted_songs_btn.setEnabled(true);
}
});
galleryViewModel.getIsFetchingActive().observe(getViewLifecycleOwner(), isFetchingActiveSyncinc -> {
if(isFetchingActiveSyncinc) {
progress_fetching_songs.setIndeterminate(true);
sync_fetching_songs_btn.setEnabled(false);
} else {
progress_fetching_songs.setIndeterminate(false);
sync_fetching_songs_btn.setEnabled(true);
}
});
return root;
}
private void onFetchRemoteModifiedSongs() {
galleryViewModel.startFetchingActive();
}
private void onSyncDeletedSongs() {
galleryViewModel.startDeletedSongSyncing();
}

View File

@ -11,6 +11,7 @@ public class GalleryViewModel extends ViewModel {
private final MutableLiveData<Boolean> isCreatedSongSyncing;
private final MutableLiveData<Boolean> isModifiedSongSyncing;
private final MutableLiveData<Boolean> isDeletedSongSyncing;
private final MutableLiveData<Boolean> isFetchingActive;
private SongSyncModule songSyncModule;
public GalleryViewModel() {
@ -23,6 +24,9 @@ public class GalleryViewModel extends ViewModel {
isDeletedSongSyncing = new MutableLiveData<>();
isDeletedSongSyncing.setValue(false);
isFetchingActive = new MutableLiveData<>();
isFetchingActive.setValue(false);
}
public MutableLiveData<Boolean> getIsCreatedSongSyncing() {
@ -71,4 +75,18 @@ public class GalleryViewModel extends ViewModel {
public void finishDeleteSongSyncinc() {
this.isDeletedSongSyncing.setValue(false);
}
public void startFetchingActive() {
if(this.songSyncModule == null) throw new RuntimeException("SongSyncModule is not initialized");
this.isFetchingActive.setValue(true);
this.songSyncModule.fetchRemoteModifiedSongs();
}
public void finishFetching() {
this.isFetchingActive.setValue(false);
}
public MutableLiveData<Boolean> getIsFetchingActive() {
return isFetchingActive;
}
}

View File

@ -147,5 +147,48 @@
android:layout_marginEnd="16dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- Label -->
<TextView
android:id="@+id/fetch_songs"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Fetch Remote Modified Songs"
android:textSize="16sp"
android:textColor="@android:color/black"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginEnd="8dp" />
<!-- Progress Bar -->
<ProgressBar
android:id="@+id/progress_fetch_songs"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="44dp"
android:indeterminate="true"
android:layout_marginEnd="16dp"
android:layout_marginStart="16dp"
app:layout_constraintStart_toEndOf="@id/fetch_songs"
app:layout_constraintEnd_toStartOf="@id/fetch_songs_btn"
app:layout_constraintTop_toTopOf="@id/fetch_songs"
app:layout_constraintBottom_toBottomOf="@id/fetch_songs"/>
<!-- Sync Button -->
<Button
android:id="@+id/fetch_songs_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sync"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="@id/fetch_songs"
app:layout_constraintBottom_toBottomOf="@id/fetch_songs"
android:layout_marginEnd="16dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>