package com.boardgamegeek.service; import android.accounts.Account; import android.content.Context; import android.content.SyncResult; import android.database.Cursor; import android.support.annotation.NonNull; import android.support.v4.util.ArrayMap; import com.boardgamegeek.R; import com.boardgamegeek.io.BggService; import com.boardgamegeek.io.CollectionRequest; import com.boardgamegeek.io.CollectionResponse; import com.boardgamegeek.model.persister.CollectionPersister; import com.boardgamegeek.provider.BggContract.Collection; import com.boardgamegeek.service.model.GameList; import com.boardgamegeek.util.SelectionBuilder; import timber.log.Timber; /** * Syncs collection items that have not yet been updated completely with stats and private info (in batches of 25). */ public class SyncCollectionUnupdated extends SyncTask { private static final int GAME_PER_FETCH = 25; public SyncCollectionUnupdated(Context context, BggService service) { super(context, service); } @Override public int getSyncType() { return SyncService.FLAG_SYNC_COLLECTION_DOWNLOAD; } @Override public void execute(@NonNull Account account, @NonNull SyncResult syncResult) { Timber.i("Syncing unupdated collection list..."); try { int numberOfFetches = 0; CollectionPersister persister = new CollectionPersister.Builder(context) .includePrivateInfo() .includeStats() .build(); ArrayMap<String, String> options = new ArrayMap<>(); options.put(BggService.COLLECTION_QUERY_KEY_SHOW_PRIVATE, "1"); options.put(BggService.COLLECTION_QUERY_KEY_STATS, "1"); GameList previousGameList = new GameList(0); do { if (isCancelled()) break; if (numberOfFetches > 0) if (wasSleepInterrupted(5000)) return; numberOfFetches++; GameList gameIds = queryGames(); if (areGamesListsEqual(gameIds, previousGameList)) { Timber.i("...didn't update any games; breaking out of fetch loop"); break; } previousGameList = gameIds; if (gameIds.getSize() > 0) { String detail = context.getString(R.string.sync_notification_collection_update_games, gameIds.getSize(), gameIds.getDescription()); if (numberOfFetches > 1) { detail = context.getString(R.string.sync_notification_page_suffix, detail, numberOfFetches); } updateProgressNotification(detail); Timber.i("...found %,d games to update [%s]", gameIds.getSize(), gameIds.getDescription()); options.put(BggService.COLLECTION_QUERY_KEY_ID, gameIds.getIds()); options.remove(BggService.COLLECTION_QUERY_KEY_SUBTYPE); int itemCount = requestAndPersist(account.name, persister, options, syncResult); if (itemCount < 0) { Timber.i("...unsuccessful sync; breaking out of fetch loop"); break; } options.put(BggService.COLLECTION_QUERY_KEY_SUBTYPE, BggService.THING_SUBTYPE_BOARDGAME_ACCESSORY); int accessoryCount = requestAndPersist(account.name, persister, options, syncResult); if (accessoryCount < 0) { Timber.i("...unsuccessful sync; breaking out of fetch loop"); break; } if (itemCount + accessoryCount == 0) { Timber.i("...unsuccessful sync; breaking out of fetch loop"); break; } } else { Timber.i("...no more unupdated collection items"); break; } } while (numberOfFetches < 100); } finally { Timber.i("...complete!"); } } public GameList queryGames() { GameList list = new GameList(GAME_PER_FETCH); Cursor cursor = context.getContentResolver().query(Collection.CONTENT_URI, new String[] { Collection.GAME_ID, Collection.GAME_NAME }, SelectionBuilder.whereZeroOrNull("collection." + Collection.UPDATED), null, "collection." + Collection.UPDATED_LIST + " DESC LIMIT " + GAME_PER_FETCH); try { while (cursor != null && cursor.moveToNext()) { list.addGame(cursor.getInt(0), cursor.getString(1)); } } finally { if (cursor != null) cursor.close(); } return list; } private int requestAndPersist(String username, @NonNull CollectionPersister persister, ArrayMap<String, String> options, @NonNull SyncResult syncResult) { Timber.i("..requesting collection items with options %s", options); CollectionResponse response = new CollectionRequest(service, username, options).execute(); if (response.hasError()) { showError(response.getError()); syncResult.stats.numIoExceptions++; return -1; } else if (response.getNumberOfItems() > 0) { int count = persister.save(response.getItems()).getRecordCount(); syncResult.stats.numUpdates += response.getNumberOfItems(); Timber.i("...saved %,d records for %,d collection items", count, response.getNumberOfItems()); return response.getNumberOfItems(); } else { Timber.i("...no collection items found for these games"); return 0; } } private boolean areGamesListsEqual(@NonNull GameList gameList1, @NonNull GameList gameList2) { for (Integer i : gameList1.getIdList()) { if (!gameList2.getIdList().contains(i)) return false; } for (Integer i : gameList2.getIdList()) { if (!gameList1.getIdList().contains(i)) return false; } return true; } @Override public int getNotificationSummaryMessageId() { return R.string.sync_notification_collection_unupdated; } }