ADD: Sync Created Songs with Server
This commit is contained in:
parent
272e1f4242
commit
b3c418968d
@ -4,6 +4,7 @@ import androidx.lifecycle.LiveData;
|
||||
import androidx.room.*;
|
||||
import com.stormtales.notevault.data.entities.NoteSheet;
|
||||
import com.stormtales.notevault.data.entities.Song;
|
||||
import com.stormtales.notevault.data.sync.SyncStatus;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -29,4 +30,13 @@ public interface SongDao {
|
||||
|
||||
@Query("SELECT localFileName FROM NoteSheet WHERE songID = :songID")
|
||||
List<String> getNoteSheetFilesBySongID(int songID);
|
||||
|
||||
@Query("SELECT * FROM Song WHERE syncStatus = :syncStatus")
|
||||
List<Song> getSongsBySyncStatus(SyncStatus syncStatus);
|
||||
|
||||
@Update
|
||||
void updateSongs(List<Song> songs);
|
||||
|
||||
@Query("SELECT * FROM Song WHERE localID IN (:localIDs)")
|
||||
List<Song> getSongsByLocalIDs(List<Integer> localIDs);
|
||||
}
|
||||
|
@ -0,0 +1,64 @@
|
||||
package com.stormtales.notevault.data.repositories;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import com.stormtales.notevault.data.MusicDatabase;
|
||||
import com.stormtales.notevault.data.dao.SongDao;
|
||||
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.CreateResponse;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class SongSyncRepository {
|
||||
private SongDao songDao;
|
||||
public SongSyncRepository(Context context) {
|
||||
MusicDatabase database = MusicDatabase.getDatabase(context);
|
||||
songDao = database.getSongTable();
|
||||
}
|
||||
|
||||
public void loadCreatedSongs(LoadDataCallback<List<Song>> callback) {
|
||||
Executors.newSingleThreadExecutor().execute(() -> {
|
||||
List<Song> createdSongs = songDao.getSongsBySyncStatus(SyncStatus.CREATED);
|
||||
Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
mainHandler.post(()-> callback.onResult(createdSongs));
|
||||
});
|
||||
}
|
||||
|
||||
public void markCreatedSongsAsSynced(BatchCreateResponse createdSongs) {
|
||||
Executors.newSingleThreadExecutor().execute(() -> {
|
||||
List<Integer> localIDs = createdSongs.getCreateResponses().stream().map(CreateResponse::getLocalID).collect(Collectors.toList());
|
||||
List<Song> requestedSongs = songDao.getSongsByLocalIDs(localIDs);
|
||||
|
||||
for(Song song : requestedSongs) {
|
||||
song.setSyncTime(LocalDateTime.now());
|
||||
song.setSyncStatus(SyncStatus.SYNCED);
|
||||
|
||||
for(CreateResponse createResponse : createdSongs.getCreateResponses()) {
|
||||
if(createResponse.getLocalID() == song.getLocalID()) {
|
||||
song.setServerID(createResponse.getServerID());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
songDao.updateSongs(requestedSongs);
|
||||
});
|
||||
}
|
||||
|
||||
public void markSongsAsSynced(List<Song> songs) {
|
||||
for(Song song : songs) {
|
||||
song.setSyncStatus(SyncStatus.SYNCED);
|
||||
}
|
||||
}
|
||||
|
||||
public interface LoadDataCallback<T> {
|
||||
void onResult(T result);
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package com.stormtales.notevault.network.sync;
|
||||
|
||||
import com.stormtales.notevault.network.auth.LoginRequest;
|
||||
import com.stormtales.notevault.network.auth.LoginResponse;
|
||||
import com.stormtales.notevault.network.sync.models.BatchCreateResponse;
|
||||
import com.stormtales.notevault.network.sync.models.SongCreateBatchRequest;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.http.Body;
|
||||
import retrofit2.http.POST;
|
||||
|
||||
public interface SongSyncAPI {
|
||||
|
||||
@POST("/sync/songs/create")
|
||||
Call<BatchCreateResponse> syncCreatedSongs(@Body SongCreateBatchRequest songCreateBatchRequest);
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.stormtales.notevault.network.sync;
|
||||
|
||||
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.ui.gallery.GalleryViewModel;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SongSyncModule {
|
||||
private SongRepository songRepository;
|
||||
private SongSyncRepository songSyncRepository;
|
||||
private SongSyncService songSyncService;
|
||||
private GalleryViewModel syncViewModel;
|
||||
|
||||
public SongSyncModule(Context context, GalleryViewModel syncViewModel) {
|
||||
this.songRepository = new SongRepository(context);
|
||||
this.songSyncRepository = new SongSyncRepository(context);
|
||||
this.songSyncService = new SongSyncService(context);
|
||||
this.syncViewModel = syncViewModel;
|
||||
}
|
||||
|
||||
public void syncCreatedSongs(FinishSongSyncingCallback callback) {
|
||||
songSyncRepository.loadCreatedSongs(result -> {
|
||||
songSyncService.syncCreatedSongs(result, new FinishSongSyncingCallback() {
|
||||
@Override
|
||||
public void finishSongSyncing(BatchCreateResponse response) {
|
||||
songSyncRepository.markCreatedSongsAsSynced(response);
|
||||
callback.finishSongSyncing(response);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
public interface FinishSongSyncingCallback {
|
||||
void finishSongSyncing(BatchCreateResponse response);
|
||||
}
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
package com.stormtales.notevault.network.sync;
|
||||
|
||||
import android.content.Context;
|
||||
import com.stormtales.notevault.data.entities.Song;
|
||||
import com.stormtales.notevault.network.NetworkModule;
|
||||
import com.stormtales.notevault.network.auth.AuthAPI;
|
||||
import com.stormtales.notevault.network.sync.models.BatchCreateResponse;
|
||||
import com.stormtales.notevault.network.sync.models.SongCreateBatchRequest;
|
||||
import com.stormtales.notevault.network.sync.models.SongCreateRequest;
|
||||
import com.stormtales.notevault.ui.gallery.GalleryViewModel;
|
||||
import retrofit2.Call;
|
||||
import retrofit2.Callback;
|
||||
import retrofit2.Response;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class SongSyncService {
|
||||
private final SongSyncAPI songSyncAPI;
|
||||
private GalleryViewModel syncStatusViewModel;
|
||||
|
||||
public SongSyncService(Context context) {
|
||||
songSyncAPI = NetworkModule.getRetrofitInstance(context).create(SongSyncAPI.class);
|
||||
}
|
||||
|
||||
public void syncCreatedSongs(List<Song> songs, SongSyncModule.FinishSongSyncingCallback callback) {
|
||||
List<SongCreateRequest> createRequests = new ArrayList<>();
|
||||
for(Song song : songs) {
|
||||
createRequests.add(new SongCreateRequest(song));
|
||||
}
|
||||
SongCreateBatchRequest songCreateBatchRequest = new SongCreateBatchRequest(createRequests);
|
||||
songSyncAPI.syncCreatedSongs(songCreateBatchRequest).enqueue(new Callback<BatchCreateResponse>() {
|
||||
@Override
|
||||
public void onResponse(Call<BatchCreateResponse> call, Response<BatchCreateResponse> response) {
|
||||
callback.finishSongSyncing(response.body());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Call<BatchCreateResponse> call, Throwable throwable) {
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
package com.stormtales.notevault.network.sync.models;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class BatchCreateResponse {
|
||||
private List<CreateResponse> createResponses;
|
||||
|
||||
public List<CreateResponse> getCreateResponses() {
|
||||
return createResponses;
|
||||
}
|
||||
|
||||
public void setCreateResponses(List<CreateResponse> createResponses) {
|
||||
this.createResponses = createResponses;
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package com.stormtales.notevault.network.sync.models;
|
||||
|
||||
public class CreateResponse {
|
||||
private int localID;
|
||||
private String serverID;
|
||||
|
||||
public int getLocalID() {
|
||||
return localID;
|
||||
}
|
||||
|
||||
public void setLocalID(int localID) {
|
||||
this.localID = localID;
|
||||
}
|
||||
|
||||
public String getServerID() {
|
||||
return serverID;
|
||||
}
|
||||
|
||||
public void setServerID(String serverID) {
|
||||
this.serverID = serverID;
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package com.stormtales.notevault.network.sync.models;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SongCreateBatchRequest {
|
||||
|
||||
private List<SongCreateRequest> songs;
|
||||
|
||||
public SongCreateBatchRequest(List<SongCreateRequest> songs) {
|
||||
this.songs = songs;
|
||||
}
|
||||
|
||||
public List<SongCreateRequest> getSongs() {
|
||||
return songs;
|
||||
}
|
||||
|
||||
public void setSongs(List<SongCreateRequest> songs) {
|
||||
this.songs = songs;
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package com.stormtales.notevault.network.sync.models;
|
||||
|
||||
import com.stormtales.notevault.data.entities.Song;
|
||||
|
||||
public class SongCreateRequest {
|
||||
private int localID;
|
||||
private String title;
|
||||
private String composer;
|
||||
private String genre;
|
||||
private int year;
|
||||
|
||||
public SongCreateRequest(int localID, String title, String composer, String genre, int year) {
|
||||
this.localID = localID;
|
||||
this.title = title;
|
||||
this.composer = composer;
|
||||
this.genre = genre;
|
||||
this.year = year;
|
||||
}
|
||||
|
||||
public SongCreateRequest(Song song) {
|
||||
this.localID = song.getLocalID();
|
||||
this.title = song.getTitle();
|
||||
this.composer = song.getComposer();
|
||||
this.genre = song.getGenre();
|
||||
this.year = song.getYear();
|
||||
}
|
||||
|
||||
public int getLocalID() {
|
||||
return localID;
|
||||
}
|
||||
|
||||
public void setLocalID(int localID) {
|
||||
this.localID = localID;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getComposer() {
|
||||
return composer;
|
||||
}
|
||||
|
||||
public void setComposer(String composer) {
|
||||
this.composer = composer;
|
||||
}
|
||||
|
||||
public String getGenre() {
|
||||
return genre;
|
||||
}
|
||||
|
||||
public void setGenre(String genre) {
|
||||
this.genre = genre;
|
||||
}
|
||||
|
||||
public int getYear() {
|
||||
return year;
|
||||
}
|
||||
|
||||
public void setYear(int year) {
|
||||
this.year = year;
|
||||
}
|
||||
}
|
@ -4,26 +4,48 @@ import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import com.stormtales.notevault.data.repositories.SongRepository;
|
||||
import com.stormtales.notevault.databinding.FragmentGalleryBinding;
|
||||
import com.stormtales.notevault.network.sync.SongSyncModule;
|
||||
import com.stormtales.notevault.network.sync.SongSyncService;
|
||||
|
||||
|
||||
public class GalleryFragment extends Fragment {
|
||||
|
||||
GalleryViewModel galleryViewModel;
|
||||
private FragmentGalleryBinding binding;
|
||||
private ProgressBar progress_sync_created_songs;
|
||||
private Button sync_created_songs_btn;
|
||||
|
||||
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
ViewGroup container, Bundle savedInstanceState) {
|
||||
GalleryViewModel galleryViewModel =
|
||||
new ViewModelProvider(this).get(GalleryViewModel.class);
|
||||
galleryViewModel = new ViewModelProvider(this).get(GalleryViewModel.class);
|
||||
|
||||
binding = FragmentGalleryBinding.inflate(inflater, container, false);
|
||||
View root = binding.getRoot();
|
||||
|
||||
final TextView textView = binding.textGallery;
|
||||
galleryViewModel.getText().observe(getViewLifecycleOwner(), textView::setText);
|
||||
progress_sync_created_songs = binding.progressSyncCreatedSongs;
|
||||
sync_created_songs_btn = binding.syncCreatedBtn;
|
||||
sync_created_songs_btn.setOnClickListener(v -> onSyncCreatedSongs());
|
||||
|
||||
galleryViewModel.setSongSyncModule(new SongSyncModule(getContext(), galleryViewModel));
|
||||
|
||||
galleryViewModel.getIsCreatedSongSyncing().observe(getViewLifecycleOwner(), isCreatedSyncinc -> {
|
||||
if(isCreatedSyncinc) {
|
||||
progress_sync_created_songs.setIndeterminate(true);
|
||||
sync_created_songs_btn.setEnabled(false);
|
||||
} else {
|
||||
progress_sync_created_songs.setIndeterminate(false);
|
||||
sync_created_songs_btn.setEnabled(true);
|
||||
}
|
||||
});
|
||||
return root;
|
||||
}
|
||||
|
||||
@ -32,4 +54,8 @@ public class GalleryFragment extends Fragment {
|
||||
super.onDestroyView();
|
||||
binding = null;
|
||||
}
|
||||
|
||||
public void onSyncCreatedSongs() {
|
||||
galleryViewModel.startCreateSongSyncing();
|
||||
}
|
||||
}
|
@ -3,17 +3,34 @@ package com.stormtales.notevault.ui.gallery;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
import com.stormtales.notevault.data.repositories.SongRepository;
|
||||
import com.stormtales.notevault.network.sync.SongSyncModule;
|
||||
|
||||
public class GalleryViewModel extends ViewModel {
|
||||
|
||||
private final MutableLiveData<String> mText;
|
||||
private final MutableLiveData<Boolean> isCreatedSongSyncing;
|
||||
private SongSyncModule songSyncModule;
|
||||
|
||||
public GalleryViewModel() {
|
||||
mText = new MutableLiveData<>();
|
||||
mText.setValue("This is gallery fragment");
|
||||
|
||||
isCreatedSongSyncing = new MutableLiveData<>();
|
||||
isCreatedSongSyncing.setValue(false);
|
||||
}
|
||||
|
||||
public LiveData<String> getText() {
|
||||
return mText;
|
||||
public MutableLiveData<Boolean> getIsCreatedSongSyncing() {
|
||||
return isCreatedSongSyncing;
|
||||
}
|
||||
|
||||
public void startCreateSongSyncing() {
|
||||
if(this.songSyncModule == null) throw new RuntimeException("SongSyncModule is not initialized");
|
||||
this.isCreatedSongSyncing.setValue(true);
|
||||
|
||||
this.songSyncModule.syncCreatedSongs(batchResponse -> {
|
||||
this.isCreatedSongSyncing.setValue(false);
|
||||
});
|
||||
}
|
||||
|
||||
public void setSongSyncModule(SongSyncModule songSyncModule) {
|
||||
this.songSyncModule = songSyncModule;
|
||||
}
|
||||
}
|
@ -6,18 +6,55 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.gallery.GalleryFragment">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text_gallery"
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:textAlignment="center"
|
||||
android:textSize="20sp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
android:layout_height="match_parent">
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".ui.gallery.GalleryFragment">
|
||||
|
||||
<!-- Label -->
|
||||
<TextView
|
||||
android:id="@+id/sync_created_songs"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Sync Client Side Created 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_marginStart="16dp"
|
||||
android:layout_marginEnd="8dp" />
|
||||
|
||||
<!-- Progress Bar -->
|
||||
<ProgressBar
|
||||
android:id="@+id/progress_sync_created_songs"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="44dp"
|
||||
android:progress="0"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:layout_marginStart="15dp"
|
||||
app:layout_constraintStart_toEndOf="@id/sync_created_songs"
|
||||
app:layout_constraintEnd_toStartOf="@id/sync_created_btn"
|
||||
app:layout_constraintTop_toTopOf="@id/sync_created_songs"
|
||||
app:layout_constraintBottom_toBottomOf="@id/sync_created_songs" />
|
||||
|
||||
<!-- Sync Button -->
|
||||
<Button
|
||||
android:id="@+id/sync_created_btn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Sync"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@id/sync_created_songs"
|
||||
app:layout_constraintBottom_toBottomOf="@id/sync_created_songs"
|
||||
android:layout_marginEnd="16dp" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Reference in New Issue
Block a user