1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2025-01-14 19:57:32 +01:00

CalendarReceiver: Convert to ContentObserver

This commit is contained in:
José Rebelo 2024-09-28 22:14:22 +01:00 committed by José Rebelo
parent 3fe3f9698a
commit d4a451e8c8
3 changed files with 77 additions and 37 deletions

View File

@ -21,8 +21,16 @@ package nodomain.freeyourgadget.gadgetbridge.externalevents;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.provider.CalendarContract;
import android.widget.Toast; import android.widget.Toast;
import androidx.core.content.ContextCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -48,15 +56,21 @@ import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarEvent;
import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarManager; import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarManager;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class CalendarReceiver extends BroadcastReceiver { public class CalendarReceiver extends ContentObserver {
private static final Logger LOG = LoggerFactory.getLogger(CalendarReceiver.class); private static final Logger LOG = LoggerFactory.getLogger(CalendarReceiver.class);
private Hashtable<Long, EventSyncState> eventState = new Hashtable<>();
private GBDevice mGBDevice; private static final String ACTION_FORCE_SYNC = "FORCE_CALENDAR_SYNC";
private class EventSyncState { private final Hashtable<Long, EventSyncState> eventState = new Hashtable<>();
private final Context mContext;
private final GBDevice mGBDevice;
private final Handler mSyncHandler;
private final BroadcastReceiver mForceSyncReceiver;
private static class EventSyncState {
private int state; private int state;
private CalendarEvent event; private final CalendarEvent event;
EventSyncState(CalendarEvent event, int state) { EventSyncState(CalendarEvent event, int state) {
this.state = state; this.state = state;
@ -74,10 +88,6 @@ public class CalendarReceiver extends BroadcastReceiver {
public CalendarEvent getEvent() { public CalendarEvent getEvent() {
return event; return event;
} }
public void setEvent(CalendarEvent event) {
this.event = event;
}
} }
private static class EventState { private static class EventState {
@ -87,20 +97,45 @@ public class CalendarReceiver extends BroadcastReceiver {
private static final int NEEDS_DELETE = 3; private static final int NEEDS_DELETE = 3;
} }
public CalendarReceiver(GBDevice gbDevice) { public CalendarReceiver(final Context context, final GBDevice gbDevice) {
LOG.info("Created calendar receiver."); super(new Handler());
LOG.info("Created calendar receiver");
mContext = context;
mGBDevice = gbDevice; mGBDevice = gbDevice;
onReceive(GBApplication.getContext(), new Intent()); mSyncHandler = new Handler();
mForceSyncReceiver = new BroadcastReceiver() {
@Override
public void onReceive(final Context context, final Intent intent) {
LOG.info("Got force sync: {}", intent.getAction());
scheduleSync();
}
};
mContext.getContentResolver().registerContentObserver(CalendarContract.Events.CONTENT_URI, true, this);
// Add a receiver to allow us to quickly force as calendar sync (without having to provide data)
LocalBroadcastManager.getInstance(mContext).registerReceiver(mForceSyncReceiver, new IntentFilter(ACTION_FORCE_SYNC));
} }
public GBDevice getGBDevice(){ public GBDevice getGBDevice() {
return mGBDevice; return mGBDevice;
} }
@Override @Override
public void onReceive(Context context, Intent intent) { public void onChange(boolean selfChange, Uri uri) {
LOG.info("got calendar changed broadcast"); super.onChange(selfChange, uri);
List<CalendarEvent> eventList = (new CalendarManager(context, mGBDevice.getAddress())).getCalendarEventList(); LOG.info("Got calendar change: {}", uri);
scheduleSync();
}
public void scheduleSync() {
LOG.debug("Scheduling calendar sync");
mSyncHandler.removeCallbacksAndMessages(null);
mSyncHandler.postDelayed(this::syncCalendar, 2500L);
}
public void syncCalendar() {
List<CalendarEvent> eventList = (new CalendarManager(mContext, mGBDevice.getAddress())).getCalendarEventList();
LOG.debug("Syncing {} calendar events", eventList.size());
syncCalendar(eventList); syncCalendar(eventList);
} }
@ -119,7 +154,6 @@ public class CalendarReceiver extends BroadcastReceiver {
Long deviceId = DBHelper.getDevice(mGBDevice, session).getId(); Long deviceId = DBHelper.getDevice(mGBDevice, session).getId();
QueryBuilder<CalendarSyncState> qb = session.getCalendarSyncStateDao().queryBuilder(); QueryBuilder<CalendarSyncState> qb = session.getCalendarSyncStateDao().queryBuilder();
for (CalendarEvent e : eventList) { for (CalendarEvent e : eventList) {
long id = e.getId(); long id = e.getId();
eventTable.put(id, e); eventTable.put(id, e);
@ -130,14 +164,13 @@ public class CalendarReceiver extends BroadcastReceiver {
.build().unique(); .build().unique();
if (calendarSyncState == null) { if (calendarSyncState == null) {
eventState.put(id, new EventSyncState(e, EventState.NOT_SYNCED)); eventState.put(id, new EventSyncState(e, EventState.NOT_SYNCED));
LOG.info("event id=" + id + " is yet unknown to device id=" + deviceId); LOG.info("event id={} is yet unknown to device id={}", id, deviceId);
} else if (calendarSyncState.getHash() == e.hashCode()) { } else if (calendarSyncState.getHash() == e.hashCode()) {
eventState.put(id, new EventSyncState(e, EventState.SYNCED)); eventState.put(id, new EventSyncState(e, EventState.SYNCED));
LOG.info("event id=" + id + " is up to date on device id=" + deviceId); LOG.info("event id={} is up to date on device id={}", id, deviceId);
} } else {
else {
eventState.put(id, new EventSyncState(e, EventState.NEEDS_UPDATE)); eventState.put(id, new EventSyncState(e, EventState.NEEDS_UPDATE));
LOG.info("event id=" + id + " is not up to date on device id=" + deviceId); LOG.info("event id={} is not up to date on device id={}", id, deviceId);
} }
} }
} }
@ -147,7 +180,7 @@ public class CalendarReceiver extends BroadcastReceiver {
for (CalendarSyncState CalendarSyncState : CalendarSyncStateList) { for (CalendarSyncState CalendarSyncState : CalendarSyncStateList) {
if (!eventState.containsKey(CalendarSyncState.getCalendarEntryId())) { if (!eventState.containsKey(CalendarSyncState.getCalendarEntryId())) {
eventState.put(CalendarSyncState.getCalendarEntryId(), new EventSyncState(null, EventState.NEEDS_DELETE)); eventState.put(CalendarSyncState.getCalendarEntryId(), new EventSyncState(null, EventState.NEEDS_DELETE));
LOG.info("insert null event for orphaned calendar id=" + CalendarSyncState.getCalendarEntryId() + " for device=" + mGBDevice.getName()); LOG.info("insert null event for orphaned calendar id={} for device={}", CalendarSyncState.getCalendarEntryId(), mGBDevice.getName());
} }
} }
@ -156,6 +189,10 @@ public class CalendarReceiver extends BroadcastReceiver {
qb = session.getCalendarSyncStateDao().queryBuilder(); qb = session.getCalendarSyncStateDao().queryBuilder();
Long i = ids.nextElement(); Long i = ids.nextElement();
EventSyncState es = eventState.get(i); EventSyncState es = eventState.get(i);
if (es == null) {
LOG.error("Failed to get event state for {}", i);
continue;
}
if (eventTable.containsKey(i)) { if (eventTable.containsKey(i)) {
if (es.getState() == EventState.SYNCED) { if (es.getState() == EventState.SYNCED) {
if (!es.getEvent().equals(eventTable.get(i))) { if (!es.getEvent().equals(eventTable.get(i))) {
@ -182,6 +219,10 @@ public class CalendarReceiver extends BroadcastReceiver {
while (ids.hasMoreElements()) { while (ids.hasMoreElements()) {
Long i = ids.nextElement(); Long i = ids.nextElement();
EventSyncState es = eventState.get(i); EventSyncState es = eventState.get(i);
if (es == null) {
LOG.error("Failed to get event state {} for sync", i);
continue;
}
int syncState = es.getState(); int syncState = es.getState();
if (syncState == EventState.NOT_SYNCED || syncState == EventState.NEEDS_UPDATE) { if (syncState == EventState.NOT_SYNCED || syncState == EventState.NEEDS_UPDATE) {
CalendarEvent calendarEvent = es.getEvent(); CalendarEvent calendarEvent = es.getEvent();
@ -195,12 +236,12 @@ public class CalendarReceiver extends BroadcastReceiver {
if (calendarEvent.isAllDay()) { if (calendarEvent.isAllDay()) {
//force the all day events to begin at midnight and last N whole days //force the all day events to begin at midnight and last N whole days
Calendar c = GregorianCalendar.getInstance(); Calendar c = GregorianCalendar.getInstance();
int numDays = (int)TimeUnit.DAYS.convert(calendarEvent.getEnd()-calendarEvent.getBegin(), int numDays = (int) TimeUnit.DAYS.convert(calendarEvent.getEnd() - calendarEvent.getBegin(),
TimeUnit.MILLISECONDS); TimeUnit.MILLISECONDS);
c.setTimeInMillis(calendarEvent.getBegin()); c.setTimeInMillis(calendarEvent.getBegin());
c.set(Calendar.HOUR_OF_DAY, 0); c.set(Calendar.HOUR_OF_DAY, 0);
//workaround for negative timezones //workaround for negative timezones
if(c.getTimeZone().getRawOffset()<0) c.add(Calendar.DAY_OF_MONTH, 1); if (c.getTimeZone().getRawOffset() < 0) c.add(Calendar.DAY_OF_MONTH, 1);
calendarEventSpec.timestamp = (int) (c.getTimeInMillis() / 1000); calendarEventSpec.timestamp = (int) (c.getTimeInMillis() / 1000);
calendarEventSpec.durationInSeconds = 24 * 60 * 60 * numDays; calendarEventSpec.durationInSeconds = 24 * 60 * 60 * numDays;
} }
@ -228,8 +269,14 @@ public class CalendarReceiver extends BroadcastReceiver {
} }
} }
public void dispose() {
mContext.getContentResolver().unregisterContentObserver(this);
LocalBroadcastManager.getInstance(mContext).unregisterReceiver(mForceSyncReceiver);
mSyncHandler.removeCallbacksAndMessages(null);
}
public static void forceSync() { public static void forceSync() {
final Intent intent = new Intent("FORCE_CALENDAR_SYNC"); final Intent intent = new Intent(ACTION_FORCE_SYNC);
intent.setPackage(BuildConfig.APPLICATION_ID); intent.setPackage(BuildConfig.APPLICATION_ID);
GBApplication.getContext().sendBroadcast(intent); GBApplication.getContext().sendBroadcast(intent);
} }

View File

@ -1312,21 +1312,14 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
for (GBDevice deviceWithCalendar : devicesWithCalendar) { for (GBDevice deviceWithCalendar : devicesWithCalendar) {
if (!deviceHasCalendarReceiverRegistered(deviceWithCalendar)) { if (!deviceHasCalendarReceiverRegistered(deviceWithCalendar)) {
if (!(GBApplication.isRunningMarshmallowOrLater() && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_DENIED)) { if (!(GBApplication.isRunningMarshmallowOrLater() && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_DENIED)) {
IntentFilter calendarIntentFilter = new IntentFilter(); CalendarReceiver receiver = new CalendarReceiver(this, deviceWithCalendar);
calendarIntentFilter.addAction("android.intent.action.PROVIDER_CHANGED");
calendarIntentFilter.addDataScheme("content");
calendarIntentFilter.addDataAuthority("com.android.calendar", null);
CalendarReceiver receiver = new CalendarReceiver(deviceWithCalendar);
ContextCompat.registerReceiver(this, receiver, calendarIntentFilter, ContextCompat.RECEIVER_EXPORTED);
mCalendarReceiver.add(receiver); mCalendarReceiver.add(receiver);
// Add a receiver to allow us to quickly force as calendar sync (without having to provide data)
ContextCompat.registerReceiver(this, receiver, new IntentFilter("FORCE_CALENDAR_SYNC"), ContextCompat.RECEIVER_EXPORTED);
} }
} }
} }
} else { } else {
for (CalendarReceiver registeredReceiver: mCalendarReceiver){ for (CalendarReceiver registeredReceiver: mCalendarReceiver) {
unregisterReceiver(registeredReceiver); registeredReceiver.dispose();
} }
mCalendarReceiver.clear(); mCalendarReceiver.clear();
} }

View File

@ -48,7 +48,7 @@ public class CalendarEventTest extends TestBase {
GBDevice dummyGBDevice = createDummyGDevice("00:00:01:00:03"); GBDevice dummyGBDevice = createDummyGDevice("00:00:01:00:03");
dummyGBDevice.setState(GBDevice.State.INITIALIZED); dummyGBDevice.setState(GBDevice.State.INITIALIZED);
// Device device = DBHelper.getDevice(dummyGBDevice, daoSession); // Device device = DBHelper.getDevice(dummyGBDevice, daoSession);
CalendarReceiver testCR = new CalendarReceiver(dummyGBDevice); CalendarReceiver testCR = new CalendarReceiver(getContext(), dummyGBDevice);
testCR.syncCalendar(eventList); testCR.syncCalendar(eventList);