diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridCoordinator.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridCoordinator.java index 0ccc5c76f..6e85dd6a7 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridCoordinator.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/devices/qhybrid/QHybridCoordinator.java @@ -229,6 +229,11 @@ public class QHybridCoordinator extends AbstractBLEDeviceCoordinator { return true; } + @Override + public boolean supportsCalendarEvents() { + return isHybridHR(); + } + @Override protected void deleteDevice(@NonNull GBDevice gbDevice, @NonNull Device device, @NonNull DaoSession session) throws GBException { @@ -267,6 +272,7 @@ public class QHybridCoordinator extends AbstractBLEDeviceCoordinator { // Settings applicable to all firmware versions generic.add(R.xml.devicesettings_fossilhybridhr_calibration); generic.add(R.xml.devicesettings_fossilhybridhr_navigation); + generic.add(R.xml.devicesettings_sync_calendar); final List health = deviceSpecificSettings.addRootScreen(DeviceSpecificSettingsScreen.HEALTH); health.add(R.xml.devicesettings_fossilhybridhr_workout_detection); health.add(R.xml.devicesettings_inactivity); diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java index 19fbb40b7..cc480d11b 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/QHybridSupport.java @@ -55,6 +55,7 @@ import nodomain.freeyourgadget.gadgetbridge.externalevents.NotificationListener; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.model.Alarm; import nodomain.freeyourgadget.gadgetbridge.model.BatteryState; +import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec; import nodomain.freeyourgadget.gadgetbridge.model.CallSpec; import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec; import nodomain.freeyourgadget.gadgetbridge.model.GenericItem; @@ -851,4 +852,14 @@ public class QHybridSupport extends QHybridBaseSupport { public void onSetNavigationInfo(NavigationInfoSpec navigationInfoSpec) { ((FossilHRWatchAdapter) watchAdapter).onSetNavigationInfo(navigationInfoSpec); } + + @Override + public void onAddCalendarEvent(CalendarEventSpec calendarEventSpec) { + ((FossilHRWatchAdapter) watchAdapter).onSendCalendar(); + } + + @Override + public void onDeleteCalendarEvent(byte type, long id) { + ((FossilHRWatchAdapter) watchAdapter).onSendCalendar(); + } } diff --git a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java index 46125d7ca..019e7dc89 100644 --- a/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java +++ b/app/src/main/java/nodomain/freeyourgadget/gadgetbridge/service/devices/qhybrid/adapter/fossil_hr/FossilHRWatchAdapter.java @@ -78,7 +78,10 @@ import java.nio.charset.StandardCharsets; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.Calendar; +import java.util.Collections; +import java.util.Comparator; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -105,9 +108,9 @@ import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.HybridHRActivitySamp import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.HybridHRSpo2SampleProvider; import nodomain.freeyourgadget.gadgetbridge.devices.qhybrid.NotificationHRConfiguration; import nodomain.freeyourgadget.gadgetbridge.entities.Device; -import nodomain.freeyourgadget.gadgetbridge.entities.User; import nodomain.freeyourgadget.gadgetbridge.entities.HybridHRActivitySample; import nodomain.freeyourgadget.gadgetbridge.entities.HybridHRSpo2Sample; +import nodomain.freeyourgadget.gadgetbridge.entities.User; import nodomain.freeyourgadget.gadgetbridge.externalevents.NotificationListener; import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice; import nodomain.freeyourgadget.gadgetbridge.impl.GBDeviceApp; @@ -181,9 +184,12 @@ import nodomain.freeyourgadget.gadgetbridge.util.Prefs; import nodomain.freeyourgadget.gadgetbridge.util.StringUtils; import nodomain.freeyourgadget.gadgetbridge.util.UriHelper; import nodomain.freeyourgadget.gadgetbridge.util.Version; +import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarEvent; +import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarManager; public class FossilHRWatchAdapter extends FossilWatchAdapter { public static final int MESSAGE_WHAT_VOICE_DATA_RECEIVED = 0; + private static final int MAX_CALENDAR_ITEMS = 10; private byte[] phoneRandomNumber; private byte[] watchRandomNumber; @@ -215,6 +221,8 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter { private Version cleanFirmwareVersion = null; + private final Set lastSync = new HashSet<>(); + ServiceConnection voiceServiceConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { @@ -1609,6 +1617,68 @@ public class FossilHRWatchAdapter extends FossilWatchAdapter { } } + public void onSendCalendar() { + if (!getDeviceSpecificPreferences().getBoolean("sync_calendar", false)) { + LOG.debug("Ignoring calendar sync request, sync is disabled"); + return; + } + + final CalendarManager upcomingEvents = new CalendarManager(getContext(), getDeviceSupport().getDevice().getAddress()); + final List calendarEvents = upcomingEvents.getCalendarEventList(); + + final Set thisSync = new HashSet<>(); + int nEvents = 0; + + for (final CalendarEvent calendarEvent : calendarEvents) { + if (++nEvents > MAX_CALENDAR_ITEMS) { + LOG.warn("Syncing only first {} events of {}", MAX_CALENDAR_ITEMS, calendarEvents.size()); + break; + } + thisSync.add(calendarEvent); + } + + if (thisSync.equals(lastSync)) { + LOG.debug("Already synced this set of events, won't send to device"); + return; + } + + lastSync.clear(); + lastSync.addAll(thisSync); + + List sortedEventList = new ArrayList<>(thisSync); + Collections.sort(sortedEventList, Comparator.comparingLong(CalendarEvent::getBegin)); + + LOG.debug("Syncing {} calendar events", sortedEventList.size()); + + try { + JSONArray items = new JSONArray(); + for(CalendarEvent event : sortedEventList) { + JSONArray reminders = new JSONArray(); + for (long reminder : event.getRemindersAbsoluteTs()) { + reminders.put(reminder / 1000); + } + items.put(new JSONObject() + .put("id", event.getId()) + .put("title", event.getTitle()) + .put("desc", event.getDescription()) + .put("start", event.getBeginSeconds()) + .put("end", event.getEndSeconds()) + .put("reminders", reminders) + ); + } + JSONObject calendarObj = new JSONObject() + .put("res", new JSONObject() + .put("set", new JSONObject() + .put("calendarApp._.config.events", items) + ) + ); + + queueWrite(new JsonPutRequest(calendarObj, this)); + } catch (JSONException e) { + LOG.error("Error sending calendar events: ", e); + } + } + @Override public void factoryReset() { queueWrite(new FactoryResetRequest());