1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-10-06 13:20:30 +02:00

Sync birthdays with calendar events

This commit is contained in:
José Rebelo 2024-09-27 18:36:52 +01:00 committed by José Rebelo
parent f612f685bf
commit 8ae8898a89
4 changed files with 119 additions and 3 deletions

View File

@ -25,6 +25,7 @@ import java.text.FieldPosition;
import java.text.ParseException;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
@ -128,6 +129,18 @@ public class DateTimeUtils {
return calendar.getTime();
}
public static Date dayStart(final LocalDate date) {
final Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.YEAR, date.getYear());
calendar.set(Calendar.MONTH, date.getMonthValue() - 1);
calendar.set(Calendar.DAY_OF_MONTH, date.getDayOfMonth());
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
return calendar.getTime();
}
public static Date dayEnd(final Date date) {
final Calendar calendar = Calendar.getInstance();
calendar.setTime(date);

View File

@ -23,19 +23,27 @@ import android.database.Cursor;
import android.net.Uri;
import android.provider.CalendarContract;
import android.provider.CalendarContract.Instances;
import android.provider.ContactsContract;
import android.text.format.Time;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Comparator;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.util.DateTimeUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
@ -82,7 +90,18 @@ public class CalendarManager {
public List<CalendarEvent> getCalendarEventList() {
loadCalendarsBlackList();
final List<CalendarEvent> calendarEventList = new ArrayList<CalendarEvent>();
final SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(deviceAddress);
final List<CalendarEvent> calendarEventList = getCalendarEvents();
if (sharedPrefs.getBoolean("sync_birthdays", false)) {
calendarEventList.addAll(getBirthdays());
calendarEventList.sort(Comparator.comparingInt(CalendarEvent::getBeginSeconds));
}
return calendarEventList;
}
public List<CalendarEvent> getCalendarEvents() {
final List<CalendarEvent> calendarEventList = new ArrayList<>();
Calendar cal = GregorianCalendar.getInstance();
long dtStart = cal.getTimeInMillis();
@ -160,6 +179,78 @@ public class CalendarManager {
}
}
public List<CalendarEvent> getBirthdays() {
final String[] projection = new String[]{
ContactsContract.CommonDataKinds.Event.CONTACT_ID,
ContactsContract.CommonDataKinds.Event.START_DATE,
ContactsContract.CommonDataKinds.Event.DISPLAY_NAME
};
final String selection = ContactsContract.Data.MIMETYPE + " = ? AND " +
ContactsContract.CommonDataKinds.Event.TYPE + " = ?";
final String[] selectionArgs = new String[]{
ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE,
String.valueOf(ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY)
};
final List<CalendarEvent> birthdays = new LinkedList<>();
final LocalDate maxDate = LocalDate.now().plusDays(30);
try (Cursor birthdayCursor = mContext.getContentResolver().query(ContactsContract.Data.CONTENT_URI, projection, selection, selectionArgs, ContactsContract.CommonDataKinds.Event.START_DATE + " ASC")) {
if (birthdayCursor == null || birthdayCursor.getCount() == 0) {
return birthdays;
}
while (birthdayCursor.moveToNext()) {
final String contactId = birthdayCursor.getString(birthdayCursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Event.CONTACT_ID));
final String birthdayStr = birthdayCursor.getString(birthdayCursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Event.START_DATE));
final String displayName = birthdayCursor.getString(birthdayCursor.getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Event.DISPLAY_NAME));
final LocalDate birthday = parseBirthday(birthdayStr);
if (birthday == null || birthday.isAfter(maxDate)) {
continue;
}
birthdays.add(new CalendarEvent(
DateTimeUtils.dayStart(birthday).getTime(),
DateTimeUtils.dayStart(birthday).getTime() + 86400000L - 1L,
contactId.hashCode(),
mContext.getString(R.string.contact_birthday, displayName),
null,
null,
mContext.getString(R.string.birthdays),
mContext.getString(R.string.pref_contacts_title),
0,
true,
null
));
}
} catch (final Exception e) {
LOG.error("could not query birthdays, permission denied?", e);
}
return birthdays;
}
private LocalDate parseBirthday(final String birthdayStr) {
final LocalDate birthday;
final LocalDate now = LocalDate.now();
try {
if (birthdayStr.startsWith("--")) {
// MM-DD
final String monthDay = birthdayStr.substring(2);
birthday = LocalDate.parse(now.getYear() + "-" + monthDay, DateTimeFormatter.ofPattern("yyyy-MM-dd"));
} else {
birthday = LocalDate.parse(birthdayStr, DateTimeFormatter.ISO_LOCAL_DATE).withYear(now.getYear());
}
} catch (final DateTimeParseException e) {
LOG.error("Failed to parse birthday {}", birthdayStr, e);
return null;
}
if (birthday.isAfter(now)) {
return birthday;
}
return birthday.plusYears(1);
}
private static HashSet<String> calendars_blacklist = null;
public boolean calendarIsBlacklisted(String calendarUniqueName) {
@ -182,7 +273,7 @@ public class CalendarManager {
public void addCalendarToBlacklist(String calendarUniqueName) {
if (calendars_blacklist.add(calendarUniqueName)) {
LOG.info("Blacklisted calendar " + calendarUniqueName);
LOG.info("Blacklisted calendar {}", calendarUniqueName);
saveCalendarsBlackList();
} else {
LOG.warn("Calendar {} already blacklisted!", calendarUniqueName);
@ -191,7 +282,7 @@ public class CalendarManager {
public void removeFromCalendarBlacklist(String calendarUniqueName) {
calendars_blacklist.remove(calendarUniqueName);
LOG.info("Unblacklisted calendar " + calendarUniqueName);
LOG.info("Unblacklisted calendar {}", calendarUniqueName);
saveCalendarsBlackList();
}

View File

@ -422,6 +422,8 @@
<string name="pref_summary_device_intents">Allow Bangle.js watch apps to send Android Intents, and allow other apps on Android (like Tasker) to send data to Bangle.js with the com.banglejs.uart.tx Intent. Needs permission to display over other apps to work in the background.</string>
<string name="pref_summary_sync_calendar">Enables calendar alerts, even when disconnected</string>
<string name="pref_title_sync_caldendar">Sync calendar events</string>
<string name="pref_summary_sync_birthdays">Sync contact birthdays alongside calendar events</string>
<string name="pref_title_sync_birthdays">Sync birthdays</string>
<string name="pref_summary_relax_firmware_checks">Relax firmware checks</string>
<string name="pref_title_relax_firmware_checks">Enable this if you want to flash a firmware not intended for your device (at your own risk)</string>
<string name="pref_title_vibration_strength">Vibration strength</string>
@ -3317,4 +3319,6 @@
<string name="label_distance_trip_mph">Trip: %.1f mi</string>
<string name="label_distance_total_mph">Total: %.1f mi</string>
<string name="error_no_cycling_sensor_found">no cycling sensor found</string>
<string name="contact_birthday">%1s\'s birthday</string>
<string name="birthdays">Birthdays</string>
</resources>

View File

@ -7,6 +7,14 @@
android:layout="@layout/preference_checkbox"
android:summary="@string/pref_summary_sync_calendar"
android:title="@string/pref_title_sync_caldendar" />
<SwitchPreferenceCompat
android:defaultValue="false"
android:dependency="sync_calendar"
android:icon="@drawable/ic_person"
android:key="sync_birthdays"
android:layout="@layout/preference_checkbox"
android:summary="@string/pref_summary_sync_birthdays"
android:title="@string/pref_title_sync_birthdays" />
<Preference
android:dependency="sync_calendar"
android:icon="@drawable/ic_block"