package org.ovirt.mobile.movirt.sync;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.RemoteException;
import android.util.Log;
import android.util.Pair;
import org.androidannotations.annotations.AfterInject;
import org.androidannotations.annotations.Bean;
import org.androidannotations.annotations.EBean;
import org.androidannotations.annotations.RootContext;
import org.ovirt.mobile.movirt.Broadcasts;
import org.ovirt.mobile.movirt.model.Event;
import org.ovirt.mobile.movirt.model.trigger.EventTriggerResolver;
import org.ovirt.mobile.movirt.model.trigger.Trigger;
import org.ovirt.mobile.movirt.provider.ProviderFacade;
import org.ovirt.mobile.movirt.rest.SimpleResponse;
import org.ovirt.mobile.movirt.rest.client.OVirtClient;
import org.ovirt.mobile.movirt.ui.MainActivityFragments;
import org.ovirt.mobile.movirt.ui.MainActivity_;
import org.ovirt.mobile.movirt.util.NotificationHelper;
import org.ovirt.mobile.movirt.util.message.MessageHelper;
import org.ovirt.mobile.movirt.util.preferences.SharedPreferencesHelper;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@EBean(scope = EBean.Scope.Singleton)
public class EventsHandler {
private static final String TAG = EventsHandler.class.getSimpleName();
public static volatile boolean inSync = false;
@Bean
ProviderFacade provider;
@Bean
OVirtClient oVirtClient;
@Bean
EventTriggerResolver eventTriggerResolver;
@Bean
NotificationHelper notificationHelper;
@RootContext
Context context;
ProviderFacade.BatchBuilder batch;
@Bean
SharedPreferencesHelper sharedPreferencesHelper;
@Bean
MessageHelper messageHelper;
private int maxEventsStored = -1;
private boolean deleteEventsBeforeInsert = false;
@AfterInject
void initialize() {
maxEventsStored = sharedPreferencesHelper.getMaxEvents();
}
public void updateEvents(boolean force) {
// it is not exactly thread safe - there is a small chance that two syncs will happen in parallel.
// but it does not cause anything worse than just that the same events will be downloaded twice
if (inSync) {
return;
}
try {
boolean configuredPoll = sharedPreferencesHelper.isPollEventsEnabled();
if (configuredPoll || force) {
batch = provider.batch();
final int lastEventId = deleteEventsBeforeInsert ? 0 : provider.getLastEventId();
oVirtClient.getEventsSince(!deleteEventsBeforeInsert ? lastEventId : 0, new SimpleResponse<List<Event>>() {
@Override
public void before() {
inSync = true;
sendSyncIntent(true);
}
@Override
public void onResponse(List<Event> newEvents) throws RemoteException {
updateEvents(newEvents, lastEventId);
applyBatch();
deleteOldEvents();
}
@Override
public void after() {
inSync = false;
sendSyncIntent(false);
}
});
}
} catch (Exception e) {
messageHelper.showError(e);
}
}
public void deleteEvents() {
if (provider.deleteEvents() != -1) {
// no need to do it again
deleteEventsBeforeInsert = false;
}
}
private void updateEvents(List<Event> newEvents, int lastEventId) {
Log.d(TAG, "Fetched " + newEvents.size() + " new event(s)");
if (deleteEventsBeforeInsert) {
deleteEvents();
}
int newLastEventCandidate = -1;
List<Event> filteredEvents = new ArrayList<>();
for (Event event : newEvents) {
// because the user api (filtered: true) returns all the events all the time
if (event.getId() > lastEventId) {
filteredEvents.add(event);
batch.insert(event);
if (event.getId() > newLastEventCandidate) {
newLastEventCandidate = event.getId();
}
}
}
this.processEventsTriggers(filteredEvents);
}
private void processEventsTriggers(List<Event> events) {
Collection<Trigger<Event>> allEventTriggers = eventTriggerResolver.getAllTriggers();
List<Pair<Event, Trigger<Event>>> eventsAndTriggers = new ArrayList<>();
for (Event event : events) {
final List<Trigger<Event>> triggers = eventTriggerResolver.getTriggers(event, allEventTriggers);
Log.d(TAG, "Processing triggers for Events: " + event.getId());
for (Trigger<Event> trigger : triggers) {
if (trigger.getCondition().evaluate(event)) {
eventsAndTriggers.add(new Pair<>(event, trigger));
}
}
}
displayNotification(eventsAndTriggers);
}
private void displayNotification(List<Pair<Event, Trigger<Event>>> eventsAndTriggers) {
if (eventsAndTriggers.size() == 0) {
return;
}
Intent resultIntent = new Intent(context, MainActivity_.class);
resultIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
resultIntent.setAction(MainActivityFragments.EVENTS.name());
PendingIntent resultPendingIntent =
PendingIntent.getActivity(
context,
0,
resultIntent,
0
);
notificationHelper.showTriggersNotification(eventsAndTriggers, context, resultPendingIntent);
}
private void applyBatch() {
if (batch.isEmpty()) {
Log.d(TAG, "No updates necessary");
} else {
Log.d(TAG, "Applying batch update");
batch.apply();
}
}
private void deleteOldEvents() {
if (maxEventsStored == -1) {
maxEventsStored = sharedPreferencesHelper.getMaxEvents();
}
provider.deleteEventsAndLetOnly(maxEventsStored);
}
public void setMaxEventsStored(int newValue) {
if (newValue > maxEventsStored) {
deleteEventsBeforeInsert = true;
}
maxEventsStored = newValue;
}
private void sendSyncIntent(boolean syncing) {
Intent intent = new Intent(Broadcasts.EVENTS_IN_SYNC);
intent.putExtra(Broadcasts.Extras.SYNCING, syncing);
context.sendBroadcast(intent);
}
}