app/src/main/java/ch/epfl/sdp/appart/FavoriteActivity.java
package ch.epfl.sdp.appart;
import android.os.Bundle;
import android.util.Log;
import android.util.Pair;
import android.view.Menu;
import android.view.MenuItem;
import androidx.appcompat.widget.Toolbar;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import javax.inject.Inject;
import ch.epfl.sdp.appart.database.DatabaseService;
import ch.epfl.sdp.appart.database.local.LocalDatabaseService;
import ch.epfl.sdp.appart.favorites.FavoriteViewModel;
import ch.epfl.sdp.appart.scrolling.card.Card;
import ch.epfl.sdp.appart.scrolling.card.CardAdapter;
import ch.epfl.sdp.appart.user.User;
import ch.epfl.sdp.appart.utils.DatabaseSync;
import dagger.hilt.android.AndroidEntryPoint;
@AndroidEntryPoint
public class FavoriteActivity extends ToolbarActivity {
@Inject
DatabaseService database;
@Inject
LocalDatabaseService localdb;
private RecyclerView recyclerView;
private FavoriteViewModel mViewModel;
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
MenuItem item = menu.findItem(R.id.action_favorite);
item.setVisible(false);
invalidateOptionsMenu();
return true;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_favorite);
Toolbar toolbar = findViewById(R.id.Favorites_toolbar);
setSupportActionBar(toolbar);
mViewModel = new ViewModelProvider(this).get(FavoriteViewModel.class);
recyclerView = findViewById(R.id.recycler_favorites);
recyclerView.setAdapter(new CardAdapter(this, database,
new ArrayList<>()));
recyclerView.setHasFixedSize(true); //use for performance if card
// dims does
// not change
mViewModel.getFavorites().observe(this, this::updateList);
removeStaleAds().thenAccept(arg -> {
CompletableFuture<Void> initRes = mViewModel.initHome();
initRes.exceptionally(e -> {
Log.d("FAVORITES", "Failed to init");
return null;
});
initRes.thenAccept(res -> updateFavsLocally());
}).exceptionally(e -> {
e.printStackTrace();
return null;
});
}
/**
* Update the list of cards.
*
* @param ls a list of card.
*/
private void updateList(Pair<List<Card>, Boolean> ls) {
recyclerView.setAdapter(new CardAdapter(this, database, ls.first,
false, ls.second));
}
/**
* Gets all favorite ads and their images from the database and tries to
* save the new data
* locally.
*/
private void updateFavsLocally() {
//So cleanFavorites is not appropriate.
if (DatabaseSync.areWeOnline(this)) {
writeAdsToDisk();
}
}
private void writeAdsToDisk() {
/*
We need this clean if we have the ability to remove or suppress ads.
Adding a clean favorites as a preparation for the possibility to
remove favorites. However, this should not clean the currentUser
data, which it currently does.
Update : Now, this is fixed. We actually have this clean for
safety. In case one part of the synchronization does not work.
*/
localdb.cleanFavoritesWithoutCurrentUser();
List<Card> favs = mViewModel.getFavorites().getValue().first;
for (int i = 0; i < favs.size(); i++) {
Card card = favs.get(i);
DatabaseSync.writeAd(database, card, this, localdb);
}
}
private CompletableFuture<Void> removeStaleAds() {
if (!DatabaseSync.areWeOnline(this)) {
return CompletableFuture.completedFuture(null);
}
CompletableFuture<Void> futureRemove = new CompletableFuture<>();
//This should never be null as this will be called only if we are online
User currentUser = loginService.getCurrentUser();
database.getUser(currentUser.getUserId())
.thenCompose(currentDBUser -> database.getCards().thenApply(cards -> new Pair(currentDBUser,
cards)))
.thenAccept(dbUserAndCards -> {
User currentDBUser = (User) dbUserAndCards.first;
List<Card> cards = (List<Card>) dbUserAndCards.second;
Set<String> userFavIds = currentDBUser.getFavoritesIds();
List<String> adIds =
cards.parallelStream().map(card -> card.getAdId()).collect(Collectors.toList());
CompletableFuture<List<Card>> futureLocalCards =
localdb.getCards();
futureLocalCards.thenAccept(localCards -> {
for (String adId : userFavIds) {
if (!adIds.contains(adId)) {
//Update user
currentDBUser.removeFavorite(adId);
//Update local db
for (Card localCard : localCards) {
if (localCard.getAdId().equals(adId)) {
localdb.removeCard(localCard.getId());
}
}
}
}
database.updateUser(currentDBUser).thenAccept(arg -> futureRemove.complete(null)).exceptionally(e -> {
e.printStackTrace();
futureRemove.completeExceptionally(e);
return null;
});
}).exceptionally(e -> {
futureRemove.completeExceptionally(e);
e.printStackTrace();
return null;
});
}).exceptionally(e -> {
e.printStackTrace();
futureRemove.completeExceptionally(e);
return null;
});
return futureRemove;
}
}