package de.ironjan.mensaupb.sync;
import android.accounts.Account;
import android.annotation.TargetApi;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SyncResult;
import android.os.Build;
import android.os.Bundle;
import com.j256.ormlite.android.AndroidConnectionSource;
import com.j256.ormlite.dao.Dao;
import com.j256.ormlite.dao.DaoManager;
import com.j256.ormlite.stmt.DeleteBuilder;
import com.j256.ormlite.stmt.PreparedQuery;
import com.j256.ormlite.stmt.SelectArg;
import com.j256.ormlite.support.ConnectionSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.NestedRuntimeException;
import org.springframework.web.client.RestClientException;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import de.ironjan.mensaupb.menus_ui.WeekdayHelper_;
import de.ironjan.mensaupb.persistence.DatabaseHelper;
import de.ironjan.mensaupb.persistence.DatabaseManager;
import de.ironjan.mensaupb.prefs.InternalKeyValueStore_;
import de.ironjan.mensaupb.stw.Restaurant;
import de.ironjan.mensaupb.stw.filters.FilterChain;
import de.ironjan.mensaupb.stw.rest_api.StwMenu;
import de.ironjan.mensaupb.stw.rest_api.StwRestWrapper;
import de.ironjan.mensaupb.stw.rest_api.StwRestWrapper_;
/**
* SyncAdapter to download and persist menus.
*/
public class MenuSyncAdapter extends AbstractThreadedSyncAdapter {
private static final Object lock = new Object();
private static MenuSyncAdapter instance;
private final Logger LOGGER = LoggerFactory.getLogger(MenuSyncAdapter.class.getSimpleName());
private final ContentResolver mContentResolver;
private final String[] restaurants = Restaurant.getKeys();
private final WeekdayHelper_ mWeekdayHelper;
private final ContentResolver contentResolver;
private final StwRestWrapper stwRestWrapper;
private final FilterChain filterChain = new FilterChain();
private final InternalKeyValueStore_ mInternalKeyValueStore;
@SuppressWarnings("SameParameterValue")
private MenuSyncAdapter(Context context, boolean autoInitialize) {
super(context, autoInitialize);
mContentResolver = context.getContentResolver();
stwRestWrapper = StwRestWrapper_.getInstance_(context);
contentResolver = context.getContentResolver();
mWeekdayHelper = WeekdayHelper_.getInstance_(context);
mInternalKeyValueStore = new InternalKeyValueStore_(context);
}
@SuppressWarnings("SameParameterValue")
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
private MenuSyncAdapter(Context context, boolean autoInitialize, boolean allowParallelSyncs) {
super(context, autoInitialize, allowParallelSyncs);
mContentResolver = context.getContentResolver();
stwRestWrapper = StwRestWrapper_.getInstance_(context);
contentResolver = context.getContentResolver();
mWeekdayHelper = WeekdayHelper_.getInstance_(context);
mInternalKeyValueStore = new InternalKeyValueStore_(context);
}
public static MenuSyncAdapter getInstance(Context context) {
synchronized (lock) {
if (instance == null) {
instance = createSyncAdapterSingleton(context);
}
return instance;
}
}
private static MenuSyncAdapter createSyncAdapterSingleton(Context context) {
Context applicationContext = context.getApplicationContext();
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.HONEYCOMB) {
return new MenuSyncAdapter(applicationContext, true, false);
} else {
return new MenuSyncAdapter(applicationContext, true);
}
}
@Override
public void onPerformSync(Account account, Bundle bundle, String s, ContentProviderClient contentProviderClient, SyncResult syncResult) {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("onPerformeSync({},{},{},{},{})", new Object[]{account, bundle, s, contentProviderClient, syncResult});
}
try {
tryMenuSync();
updateLastSyncTime();
} catch (SQLException | NestedRuntimeException e) {
LOGGER.warn("onPerformeSync({},{},{},{},{}) failed because of exception", new Object[]{account, bundle, s, contentProviderClient, syncResult});
LOGGER.error(e.getMessage(), e);
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("onPerformeSync({},{},{},{},{}) done", new Object[]{account, bundle, s, contentProviderClient, syncResult});
}
}
private void updateLastSyncTime() {
mInternalKeyValueStore.edit().lastSyncTimeStamp().put(System.currentTimeMillis()).apply();
}
private void tryMenuSync() throws java.sql.SQLException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("tryMenuSync()");
}
DatabaseManager databaseManager = new DatabaseManager();
DatabaseHelper helper = (databaseManager.getHelper(getContext()));
ConnectionSource connectionSource =
new AndroidConnectionSource(helper);
Dao<StwMenu, ?> dao = DaoManager.createDao(connectionSource, StwMenu.class);
String[] cachedDaysAsStrings = mWeekdayHelper.getCachedDaysAsStrings();
Date now = new Date();
for (String date : cachedDaysAsStrings) {
for (String restaurant : restaurants) {
syncMenus(dao, restaurant, date, now);
}
}
removeUnneededMenusFromDatabase(dao, now);
databaseManager.releaseHelper(helper);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("tryMenuSync() done");
}
}
@org.androidannotations.annotations.Trace
void syncMenus(Dao<StwMenu, ?> dao, String restaurant, String date, Date now) throws java.sql.SQLException, RestClientException {
StwMenu[] menus = downloadMenus(restaurant, date);
List<StwMenu> menuList = Arrays.asList(menus);
List<StwMenu> filteredList = filterChain.filter(menuList);
persistMenus(dao, filteredList, now);
}
@org.androidannotations.annotations.Trace
StwMenu[] downloadMenus(String restaurant, String date) {
return stwRestWrapper.getMenus(restaurant, date);
}
@org.androidannotations.annotations.Trace
void persistMenus(Dao<StwMenu, ?> dao, Iterable<StwMenu> menus, Date now) throws java.sql.SQLException {
SelectArg nameArg = new SelectArg(),
dateArg = new SelectArg(),
restaurantArg = new SelectArg();
PreparedQuery<StwMenu> preparedQuery = dao.queryBuilder().where().eq(StwMenu.NAME_GERMAN, nameArg)
.and().eq(StwMenu.DATE, dateArg)
.and().eq(StwMenu.RESTAURANT, restaurantArg)
.prepare();
for (StwMenu stwMenu : menus) {
stwMenu.setUpdatedOn(now);
nameArg.setValue(stwMenu.getName_de());
dateArg.setValue(stwMenu.getDate());
restaurantArg.setValue(stwMenu.getRestaurant());
List<StwMenu> local = dao.query(preparedQuery);
if (local.size() > 0) {
stwMenu.set_id(local.get(0).get_id());
dao.update(stwMenu);
} else {
dao.create(stwMenu);
}
contentResolver.notifyChange(MenuContentProvider.MENU_URI, null, false);
}
}
private void removeUnneededMenusFromDatabase(Dao<StwMenu, ?> dao, Date now) throws SQLException {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("removeUnneededMenusFromDatabase(dao,{})", now);
}
int deleted = 0;
try {
DeleteBuilder<StwMenu, ?> deleteBuilder = dao.deleteBuilder();
deleteBuilder.where().not().eq(StwMenu.UPDATED_ON, now);
deleted = deleteBuilder.delete();
} catch (Exception e) {
LOGGER.error("Error", e);
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("removeUnneededMenusFromDatabase(dao,{}) done - deleted {} menus", now, deleted);
}
}
}