Make calendar blacklist configurable per device

This commit is contained in:
José Rebelo 2022-06-16 22:28:17 +01:00 committed by Gitea
parent 7512147c34
commit 152f19575f
18 changed files with 436 additions and 368 deletions

View File

@ -117,7 +117,7 @@ public class GBApplication extends Application {
private static SharedPreferences sharedPrefs;
private static final String PREFS_VERSION = "shared_preferences_version";
//if preferences have to be migrated, increment the following and add the migration logic in migratePrefs below; see http://stackoverflow.com/questions/16397848/how-can-i-migrate-android-preferences-with-a-new-version
private static final int CURRENT_PREFS_VERSION = 16;
private static final int CURRENT_PREFS_VERSION = 17;
private static LimitedQueue mIDSenderLookup = new LimitedQueue(16);
private static Prefs prefs;
@ -224,7 +224,6 @@ public class GBApplication extends Application {
deviceService = createDeviceService();
loadAppsNotifBlackList();
loadAppsPebbleBlackList();
loadCalendarsBlackList();
PeriodicExporter.enablePeriodicExport(context);
@ -557,61 +556,6 @@ public class GBApplication extends Application {
return packageName;
}
private static HashSet<String> calendars_blacklist = null;
public static boolean calendarIsBlacklisted(String calendarUniqueName) {
if (calendars_blacklist == null) {
GB.log("calendarIsBlacklisted: calendars_blacklist is null!", GB.INFO, null);
}
return calendars_blacklist != null && calendars_blacklist.contains(calendarUniqueName);
}
public static void setCalendarsBlackList(Set<String> calendarNames) {
if (calendarNames == null) {
GB.log("Set null apps_notification_blacklist", GB.INFO, null);
calendars_blacklist = new HashSet<>();
} else {
calendars_blacklist = new HashSet<>(calendarNames);
}
GB.log("New calendars_blacklist has " + calendars_blacklist.size() + " entries", GB.INFO, null);
saveCalendarsBlackList();
}
public static void addCalendarToBlacklist(String calendarUniqueName) {
if (calendars_blacklist.add(calendarUniqueName)) {
GB.log("Blacklisted calendar " + calendarUniqueName, GB.INFO, null);
saveCalendarsBlackList();
} else {
GB.log("Calendar " + calendarUniqueName + " already blacklisted!", GB.WARN, null);
}
}
public static void removeFromCalendarBlacklist(String calendarUniqueName) {
calendars_blacklist.remove(calendarUniqueName);
GB.log("Unblacklisted calendar " + calendarUniqueName, GB.INFO, null);
saveCalendarsBlackList();
}
private static void loadCalendarsBlackList() {
GB.log("Loading calendars_blacklist", GB.INFO, null);
calendars_blacklist = (HashSet<String>) sharedPrefs.getStringSet(GBPrefs.CALENDAR_BLACKLIST, null); // lgtm [java/abstract-to-concrete-cast]
if (calendars_blacklist == null) {
calendars_blacklist = new HashSet<>();
}
GB.log("Loaded calendars_blacklist has " + calendars_blacklist.size() + " entries", GB.INFO, null);
}
private static void saveCalendarsBlackList() {
GB.log("Saving calendars_blacklist with " + calendars_blacklist.size() + " entries", GB.INFO, null);
SharedPreferences.Editor editor = sharedPrefs.edit();
if (calendars_blacklist.isEmpty()) {
editor.putStringSet(GBPrefs.CALENDAR_BLACKLIST, null);
} else {
Prefs.putStringSet(editor, GBPrefs.CALENDAR_BLACKLIST, calendars_blacklist);
}
editor.apply();
}
/**
* Deletes both the old Activity database and the new one recreates it with empty tables.
*
@ -1190,6 +1134,32 @@ public class GBApplication extends Application {
}
}
if (oldVersion < 17) {
final HashSet<String> calendarBlacklist = (HashSet<String>) prefs.getStringSet(GBPrefs.CALENDAR_BLACKLIST, null);
try (DBHandler db = acquireDB()) {
final DaoSession daoSession = db.getDaoSession();
final List<Device> activeDevices = DBHelper.getActiveDevices(daoSession);
for (Device dbDevice : activeDevices) {
final SharedPreferences deviceSharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(dbDevice.getIdentifier());
final SharedPreferences.Editor deviceSharedPrefsEdit = deviceSharedPrefs.edit();
deviceSharedPrefsEdit.putBoolean("sync_calendar", prefs.getBoolean("enable_calendar_sync", true));
if (calendarBlacklist != null) {
Prefs.putStringSet(deviceSharedPrefsEdit, GBPrefs.CALENDAR_BLACKLIST, calendarBlacklist);
}
deviceSharedPrefsEdit.apply();
}
} catch (Exception e) {
Log.w(TAG, "error acquiring DB lock");
}
editor.remove(GBPrefs.CALENDAR_BLACKLIST);
}
editor.putString(PREFS_VERSION, Integer.toString(CURRENT_PREFS_VERSION));
editor.apply();
}

View File

@ -44,7 +44,9 @@ import androidx.core.app.ActivityCompat;
import androidx.core.app.NavUtils;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarManager;
public class CalBlacklistActivity extends AbstractGBActivity {
@ -56,12 +58,18 @@ public class CalBlacklistActivity extends AbstractGBActivity {
};
private ArrayList<Calendar> calendarsArrayList;
private GBDevice gbDevice;
private CalendarManager calendarManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_calblacklist);
ListView calListView = (ListView) findViewById(R.id.calListView);
gbDevice = getIntent().getParcelableExtra(GBDevice.EXTRA_DEVICE);
calendarManager = new CalendarManager(this, gbDevice.getAddress());
final Uri uri = CalendarContract.Calendars.CONTENT_URI;
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CALENDAR) != PackageManager.PERMISSION_GRANTED) {
GB.toast(this, "Calendar permission not granted. Nothing to do.", Toast.LENGTH_SHORT, GB.WARN);
@ -83,9 +91,9 @@ public class CalBlacklistActivity extends AbstractGBActivity {
CheckBox selected = (CheckBox) view.findViewById(R.id.item_checkbox);
toggleEntry(view);
if (selected.isChecked()) {
GBApplication.addCalendarToBlacklist(item.getUniqueString());
calendarManager.addCalendarToBlacklist(item.getUniqueString());
} else {
GBApplication.removeFromCalendarBlacklist(item.getUniqueString());
calendarManager.removeFromCalendarBlacklist(item.getUniqueString());
}
}
});
@ -148,8 +156,8 @@ public class CalBlacklistActivity extends AbstractGBActivity {
TextView ownerAccount = (TextView) view.findViewById(R.id.calendar_owner_account);
CheckBox checked = (CheckBox) view.findViewById(R.id.item_checkbox);
if (GBApplication.calendarIsBlacklisted(item.getUniqueString()) && !checked.isChecked() ||
!GBApplication.calendarIsBlacklisted(item.getUniqueString()) && checked.isChecked()) {
if (calendarManager.calendarIsBlacklisted(item.getUniqueString()) && !checked.isChecked() ||
!calendarManager.calendarIsBlacklisted(item.getUniqueString()) && checked.isChecked()) {
toggleEntry(view);
}
color.setBackgroundColor(item.color);

View File

@ -138,15 +138,6 @@ public class SettingsActivity extends AbstractSettingsActivity {
}
});
pref = findPreference("pref_key_blacklist_calendars");
pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
Intent enableIntent = new Intent(SettingsActivity.this, CalBlacklistActivity.class);
startActivity(enableIntent);
return true;
}
});
pref = findPreference("pebble_emu_addr");
pref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override

View File

@ -49,7 +49,9 @@ import java.util.Set;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.R;
import nodomain.freeyourgadget.gadgetbridge.activities.CalBlacklistActivity;
import nodomain.freeyourgadget.gadgetbridge.activities.ConfigureWorldClocks;
import nodomain.freeyourgadget.gadgetbridge.activities.SettingsActivity;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceCoordinator;
import nodomain.freeyourgadget.gadgetbridge.devices.DeviceManager;
import nodomain.freeyourgadget.gadgetbridge.devices.huami.HuamiConst;
@ -688,6 +690,18 @@ public class DeviceSpecificSettingsFragment extends PreferenceFragmentCompat imp
});
}
final Preference calendarBlacklist = findPreference("blacklist_calendars");
if (calendarBlacklist != null) {
calendarBlacklist.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
Intent intent = new Intent(getContext(), CalBlacklistActivity.class);
intent.putExtra(GBDevice.EXTRA_DEVICE, device);
startActivity(intent);
return true;
}
});
}
final Preference cannedMessagesDismissCall = findPreference("canned_messages_dismisscall_send");
if (cannedMessagesDismissCall != null) {
cannedMessagesDismissCall.setOnPreferenceClickListener(new androidx.preference.Preference.OnPreferenceClickListener() {

View File

@ -230,6 +230,7 @@ public class PebbleCoordinator extends AbstractDeviceCoordinator {
R.xml.devicesettings_autoremove_notifications,
R.xml.devicesettings_canned_reply_16,
R.xml.devicesettings_canned_dismisscall_16,
R.xml.devicesettings_sync_calendar,
R.xml.devicesettings_transliteration
};
}

View File

@ -41,7 +41,8 @@ import nodomain.freeyourgadget.gadgetbridge.entities.CalendarSyncStateDao;
import nodomain.freeyourgadget.gadgetbridge.entities.DaoSession;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEvents;
import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarEvent;
import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarManager;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class CalendarReceiver extends BroadcastReceiver {
@ -52,9 +53,9 @@ public class CalendarReceiver extends BroadcastReceiver {
private class EventSyncState {
private int state;
private CalendarEvents.CalendarEvent event;
private CalendarEvent event;
EventSyncState(CalendarEvents.CalendarEvent event, int state) {
EventSyncState(CalendarEvent event, int state) {
this.state = state;
this.event = event;
}
@ -67,11 +68,11 @@ public class CalendarReceiver extends BroadcastReceiver {
this.state = state;
}
public CalendarEvents.CalendarEvent getEvent() {
public CalendarEvent getEvent() {
return event;
}
public void setEvent(CalendarEvents.CalendarEvent event) {
public void setEvent(CalendarEvent event) {
this.event = event;
}
}
@ -92,11 +93,11 @@ public class CalendarReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
LOG.info("got calendar changed broadcast");
List<CalendarEvents.CalendarEvent> eventList = (new CalendarEvents()).getCalendarEventList(GBApplication.getContext());
List<CalendarEvent> eventList = (new CalendarManager(context, mGBDevice.getAddress())).getCalendarEventList();
syncCalendar(eventList);
}
public void syncCalendar(List<CalendarEvents.CalendarEvent> eventList) {
public void syncCalendar(List<CalendarEvent> eventList) {
try (DBHandler dbHandler = GBApplication.acquireDB()) {
DaoSession session = dbHandler.getDaoSession();
syncCalendar(eventList, session);
@ -105,14 +106,14 @@ public class CalendarReceiver extends BroadcastReceiver {
}
}
public void syncCalendar(List<CalendarEvents.CalendarEvent> eventList, DaoSession session) {
public void syncCalendar(List<CalendarEvent> eventList, DaoSession session) {
LOG.info("Syncing with calendar.");
Hashtable<Long, CalendarEvents.CalendarEvent> eventTable = new Hashtable<>();
Hashtable<Long, CalendarEvent> eventTable = new Hashtable<>();
Long deviceId = DBHelper.getDevice(mGBDevice, session).getId();
QueryBuilder<CalendarSyncState> qb = session.getCalendarSyncStateDao().queryBuilder();
for (CalendarEvents.CalendarEvent e : eventList) {
for (CalendarEvent e : eventList) {
long id = e.getId();
eventTable.put(id, e);
if (!eventState.containsKey(e.getId())) {
@ -176,7 +177,7 @@ public class CalendarReceiver extends BroadcastReceiver {
EventSyncState es = eventState.get(i);
int syncState = es.getState();
if (syncState == EventState.NOT_SYNCED || syncState == EventState.NEEDS_UPDATE) {
CalendarEvents.CalendarEvent calendarEvent = es.getEvent();
CalendarEvent calendarEvent = es.getEvent();
CalendarEventSpec calendarEventSpec = new CalendarEventSpec();
calendarEventSpec.id = i;
calendarEventSpec.title = calendarEvent.getTitle();

View File

@ -1,244 +0,0 @@
/* Copyright (C) 2015-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, Daniel Hauck
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.model;
import android.content.ContentUris;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.CalendarContract;
import android.provider.CalendarContract.Instances;
import android.text.format.Time;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Objects;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
public class CalendarEvents {
private static final Logger LOG = LoggerFactory.getLogger(CalendarEvents.class);
// needed for pebble: time, duration, layout, reminders, actions
// layout: type, title, subtitle, body (max 512), tinyIcon, smallIcon, largeIcon
//further: primaryColor, secondaryColor, backgroundColor, headings, paragraphs, lastUpdated
// taken from: https://developer.getpebble.com/guides/timeline/pin-structure/
// needed for MiBand:
// time
private static final String[] EVENT_INSTANCE_PROJECTION = new String[]{
Instances._ID,
Instances.BEGIN,
Instances.END,
Instances.DURATION,
Instances.TITLE,
Instances.DESCRIPTION,
Instances.EVENT_LOCATION,
Instances.CALENDAR_DISPLAY_NAME,
CalendarContract.Calendars.ACCOUNT_NAME,
Instances.CALENDAR_COLOR,
Instances.ALL_DAY
};
private static final int lookahead_days = 7;
private List<CalendarEvent> calendarEventList = new ArrayList<CalendarEvent>();
public List<CalendarEvent> getCalendarEventList(Context mContext) {
fetchSystemEvents(mContext);
return calendarEventList;
}
private boolean fetchSystemEvents(Context mContext) {
Calendar cal = GregorianCalendar.getInstance();
long dtStart = cal.getTimeInMillis();
cal.add(Calendar.DATE, lookahead_days);
long dtEnd = cal.getTimeInMillis();
Uri.Builder eventsUriBuilder = Instances.CONTENT_URI.buildUpon();
ContentUris.appendId(eventsUriBuilder, dtStart);
ContentUris.appendId(eventsUriBuilder, dtEnd);
Uri eventsUri = eventsUriBuilder.build();
try (Cursor evtCursor = mContext.getContentResolver().query(eventsUri, EVENT_INSTANCE_PROJECTION, null, null, Instances.BEGIN + " ASC")) {
if (evtCursor == null || evtCursor.getCount() == 0) {
return false;
}
while (evtCursor.moveToNext()) {
long start = evtCursor.getLong(1);
long end = evtCursor.getLong(2);
if (end == 0) {
LOG.info("no end time, will parse duration string");
Time time = new Time(); //FIXME: deprecated FTW
time.parse(evtCursor.getString(3));
end = start + time.toMillis(false);
}
CalendarEvent calEvent = new CalendarEvent(
start,
end,
evtCursor.getLong(0),
evtCursor.getString(4),
evtCursor.getString(5),
evtCursor.getString(6),
evtCursor.getString(7),
evtCursor.getString(8),
evtCursor.getInt(9),
!evtCursor.getString(10).equals("0")
);
if (!GBApplication.calendarIsBlacklisted(calEvent.getUniqueCalName())) {
calendarEventList.add(calEvent);
} else {
LOG.debug("calendar " + calEvent.getUniqueCalName() + " skipped because it's blacklisted");
}
}
return true;
} catch (Exception e) {
LOG.error("could not query calendar, permission denied?");
return false;
}
}
public static class CalendarEvent {
private long begin;
private long end;
private long id;
private String title;
private String description;
private String location;
private String calName;
private String calAccountName;
private int color;
private boolean allDay;
public CalendarEvent(long begin, long end, long id, String title, String description, String location, String calName, String calAccountName, int color, boolean allDay) {
this.begin = begin;
this.end = end;
this.id = id;
this.title = title;
this.description = description;
this.location = location;
this.calName = calName;
this.calAccountName = calAccountName;
this.color = color;
this.allDay = allDay;
}
public long getBegin() {
return begin;
}
public int getBeginSeconds() {
return (int) (begin / 1000);
}
public long getEnd() {
return end;
}
public long getDuration() {
return end - begin;
}
public int getDurationSeconds() {
return (int) ((getDuration()) / 1000);
}
public short getDurationMinutes() {
return (short) (getDurationSeconds() / 60);
}
public long getId() {
return id;
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
public String getLocation() {
return location;
}
public String getCalName() {
return calName;
}
public String getCalAccountName() {
return calAccountName;
}
public String getUniqueCalName() {
return getCalAccountName() + '/' + getCalName();
}
public int getColor() {
return color;
}
public boolean isAllDay() {
return allDay;
}
@Override
public boolean equals(Object other) {
if (other instanceof CalendarEvent) {
CalendarEvent e = (CalendarEvent) other;
return (this.getId() == e.getId()) &&
Objects.equals(this.getTitle(), e.getTitle()) &&
(this.getBegin() == e.getBegin()) &&
Objects.equals(this.getLocation(), e.getLocation()) &&
Objects.equals(this.getDescription(), e.getDescription()) &&
(this.getEnd() == e.getEnd()) &&
Objects.equals(this.getCalName(), e.getCalName()) &&
Objects.equals(this.getCalAccountName(), e.getCalAccountName()) &&
(this.getColor() == e.getColor()) &&
(this.isAllDay() == e.isAllDay());
} else {
return false;
}
}
@Override
public int hashCode() {
int result = (int) id;
result = 31 * result + Objects.hash(title);
result = 31 * result + Long.valueOf(begin).hashCode();
result = 31 * result + Objects.hash(location);
result = 31 * result + Objects.hash(description);
result = 31 * result + Long.valueOf(end).hashCode();
result = 31 * result + Objects.hash(calName);
result = 31 * result + Objects.hash(calAccountName);
result = 31 * result + Integer.valueOf(color).hashCode();
result = 31 * result + Boolean.valueOf(allDay).hashCode();
return result;
}
}
}

View File

@ -976,7 +976,7 @@ public class DeviceCommunicationService extends Service implements SharedPrefere
}
if (enable && initialized && features.supportsCalendarEvents()) {
if (mCalendarReceiver == null && getPrefs().getBoolean("enable_calendar_sync", true)) {
if (mCalendarReceiver == null) {
if (!(GBApplication.isRunningMarshmallowOrLater() && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALENDAR) == PackageManager.PERMISSION_DENIED)) {
IntentFilter calendarIntentFilter = new IntentFilter();
calendarIntentFilter.addAction("android.intent.action.PROVIDER_CHANGED");

View File

@ -110,7 +110,8 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEvents;
import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarEvent;
import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarManager;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
@ -2509,12 +2510,12 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport {
int availableSlots = prefs.getInt(PREF_RESERVER_ALARMS_CALENDAR, 0);
if (availableSlots > 0) {
CalendarEvents upcomingEvents = new CalendarEvents();
List<CalendarEvents.CalendarEvent> mEvents = upcomingEvents.getCalendarEventList(getContext());
CalendarManager upcomingEvents = new CalendarManager(getContext(), getDevice().getAddress());
List<CalendarEvent> mEvents = upcomingEvents.getCalendarEventList();
int iteration = 0;
for (CalendarEvents.CalendarEvent mEvt : mEvents) {
for (CalendarEvent mEvt : mEvents) {
if (mEvt.isAllDay()) {
continue;
}
@ -2541,13 +2542,13 @@ public abstract class HuamiSupport extends AbstractBTLEDeviceSupport {
final Prefs prefs = new Prefs(GBApplication.getDeviceSpecificSharedPrefs(gbDevice.getAddress()));
int availableSlots = prefs.getInt(PREF_RESERVER_REMINDERS_CALENDAR, 9);
CalendarEvents upcomingEvents = new CalendarEvents();
List<CalendarEvents.CalendarEvent> calendarEvents = upcomingEvents.getCalendarEventList(getContext());
CalendarManager upcomingEvents = new CalendarManager(getContext(), getDevice().getAddress());
List<CalendarEvent> calendarEvents = upcomingEvents.getCalendarEventList();
Calendar calendar = Calendar.getInstance();
int iteration = 0;
for (CalendarEvents.CalendarEvent calendarEvent : calendarEvents) {
for (CalendarEvent calendarEvent : calendarEvents) {
if (calendarEvent.isAllDay()) {
continue;
}

View File

@ -65,7 +65,8 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivitySample;
import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEvents;
import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarEvent;
import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarManager;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
import nodomain.freeyourgadget.gadgetbridge.model.DeviceService;
@ -1233,11 +1234,11 @@ public class MiBandSupport extends AbstractBTLEDeviceSupport {
availableSlots = 3;
}
if (availableSlots > 0) {
CalendarEvents upcomingEvents = new CalendarEvents();
List<CalendarEvents.CalendarEvent> mEvents = upcomingEvents.getCalendarEventList(getContext());
CalendarManager upcomingEvents = new CalendarManager(getContext(), getDevice().getAddress());
List<CalendarEvent> mEvents = upcomingEvents.getCalendarEventList();
int iteration = 0;
for (CalendarEvents.CalendarEvent mEvt : mEvents) {
for (CalendarEvent mEvt : mEvents) {
if (iteration >= availableSlots) {
break;
}

View File

@ -56,7 +56,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.ActivityUser;
import nodomain.freeyourgadget.gadgetbridge.model.Alarm;
import nodomain.freeyourgadget.gadgetbridge.model.BatteryState;
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEventSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEvents;
import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarEvent;
import nodomain.freeyourgadget.gadgetbridge.model.CallSpec;
import nodomain.freeyourgadget.gadgetbridge.model.CannedMessagesSpec;
import nodomain.freeyourgadget.gadgetbridge.model.MusicSpec;
@ -73,6 +73,7 @@ import nodomain.freeyourgadget.gadgetbridge.util.AlarmUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarManager;
import static nodomain.freeyourgadget.gadgetbridge.activities.devicesettings.DeviceSettingsPreferenceConst.PREF_SYNC_CALENDAR;
@ -594,11 +595,11 @@ public class ZeTimeDeviceSupport extends AbstractBTLEDeviceSupport {
return;
}
CalendarEvents upcomingEvents = new CalendarEvents();
List<CalendarEvents.CalendarEvent> calendarEvents = upcomingEvents.getCalendarEventList(getContext());
CalendarManager upcomingEvents = new CalendarManager(getContext(), getDevice().getAddress());
List<CalendarEvent> calendarEvents = upcomingEvents.getCalendarEventList();
int eventCount = 0;
for (CalendarEvents.CalendarEvent calendarEvent : calendarEvents) {
for (CalendarEvent calendarEvent : calendarEvents) {
if (calendarEvent.isAllDay()) {
continue;
}

View File

@ -134,14 +134,6 @@ public class ImportExportSharedPreferences {
}
GBApplication.setAppsPebbleBlackList(apps_pebble_blacklist);
break;
case GBPrefs.CALENDAR_BLACKLIST: //TODO: untested
Set<String> calendars_blacklist = new HashSet<>();
text = text.replace("[", "").replace("]", "");
for (int z = 0; z < text.split(",").length; z++) {
calendars_blacklist.add(text.split(",")[z].trim());
}
GBApplication.setCalendarsBlackList(calendars_blacklist);
break;
}
} else if (!PREFERENCES.equals(name)) {
throw new Exception("Unknown type " + name);

View File

@ -0,0 +1,141 @@
/* Copyright (C) 2017-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, Daniel Hauck
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.util.calendar;
import java.util.Objects;
public class CalendarEvent {
private long begin;
private long end;
private long id;
private String title;
private String description;
private String location;
private String calName;
private String calAccountName;
private int color;
private boolean allDay;
public CalendarEvent(long begin, long end, long id, String title, String description, String location, String calName, String calAccountName, int color, boolean allDay) {
this.begin = begin;
this.end = end;
this.id = id;
this.title = title;
this.description = description;
this.location = location;
this.calName = calName;
this.calAccountName = calAccountName;
this.color = color;
this.allDay = allDay;
}
public long getBegin() {
return begin;
}
public int getBeginSeconds() {
return (int) (begin / 1000);
}
public long getEnd() {
return end;
}
public long getDuration() {
return end - begin;
}
public int getDurationSeconds() {
return (int) ((getDuration()) / 1000);
}
public short getDurationMinutes() {
return (short) (getDurationSeconds() / 60);
}
public long getId() {
return id;
}
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
public String getLocation() {
return location;
}
public String getCalName() {
return calName;
}
public String getCalAccountName() {
return calAccountName;
}
public String getUniqueCalName() {
return getCalAccountName() + '/' + getCalName();
}
public int getColor() {
return color;
}
public boolean isAllDay() {
return allDay;
}
@Override
public boolean equals(Object other) {
if (other instanceof CalendarEvent) {
CalendarEvent e = (CalendarEvent) other;
return (this.getId() == e.getId()) &&
Objects.equals(this.getTitle(), e.getTitle()) &&
(this.getBegin() == e.getBegin()) &&
Objects.equals(this.getLocation(), e.getLocation()) &&
Objects.equals(this.getDescription(), e.getDescription()) &&
(this.getEnd() == e.getEnd()) &&
Objects.equals(this.getCalName(), e.getCalName()) &&
Objects.equals(this.getCalAccountName(), e.getCalAccountName()) &&
(this.getColor() == e.getColor()) &&
(this.isAllDay() == e.isAllDay());
} else {
return false;
}
}
@Override
public int hashCode() {
int result = (int) id;
result = 31 * result + Objects.hash(title);
result = 31 * result + Long.valueOf(begin).hashCode();
result = 31 * result + Objects.hash(location);
result = 31 * result + Objects.hash(description);
result = 31 * result + Long.valueOf(end).hashCode();
result = 31 * result + Objects.hash(calName);
result = 31 * result + Objects.hash(calAccountName);
result = 31 * result + Integer.valueOf(color).hashCode();
result = 31 * result + Boolean.valueOf(allDay).hashCode();
return result;
}
}

View File

@ -0,0 +1,193 @@
/* Copyright (C) 2015-2020 Andreas Shimokawa, Carsten Pfeiffer, Daniele
Gobbetti, Daniel Hauck
This file is part of Gadgetbridge.
Gadgetbridge is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Gadgetbridge is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package nodomain.freeyourgadget.gadgetbridge.util.calendar;
import android.content.ContentUris;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.Uri;
import android.provider.CalendarContract;
import android.provider.CalendarContract.Instances;
import android.text.format.Time;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import nodomain.freeyourgadget.gadgetbridge.GBApplication;
import nodomain.freeyourgadget.gadgetbridge.util.GB;
import nodomain.freeyourgadget.gadgetbridge.util.GBPrefs;
import nodomain.freeyourgadget.gadgetbridge.util.Prefs;
public class CalendarManager {
private static final Logger LOG = LoggerFactory.getLogger(CalendarManager.class);
// needed for pebble: time, duration, layout, reminders, actions
// layout: type, title, subtitle, body (max 512), tinyIcon, smallIcon, largeIcon
//further: primaryColor, secondaryColor, backgroundColor, headings, paragraphs, lastUpdated
// taken from: https://developer.getpebble.com/guides/timeline/pin-structure/
// needed for MiBand:
// time
private static final String[] EVENT_INSTANCE_PROJECTION = new String[]{
Instances._ID,
Instances.BEGIN,
Instances.END,
Instances.DURATION,
Instances.TITLE,
Instances.DESCRIPTION,
Instances.EVENT_LOCATION,
Instances.CALENDAR_DISPLAY_NAME,
CalendarContract.Calendars.ACCOUNT_NAME,
Instances.CALENDAR_COLOR,
Instances.ALL_DAY
};
private static final int lookahead_days = 7;
private final String deviceAddress;
private final Context mContext;
public CalendarManager(final Context context, final String deviceAddress) {
this.mContext = context;
this.deviceAddress = deviceAddress;
loadCalendarsBlackList();
}
public List<CalendarEvent> getCalendarEventList() {
loadCalendarsBlackList();
final List<CalendarEvent> calendarEventList = new ArrayList<CalendarEvent>();
Calendar cal = GregorianCalendar.getInstance();
long dtStart = cal.getTimeInMillis();
cal.add(Calendar.DATE, lookahead_days);
long dtEnd = cal.getTimeInMillis();
Uri.Builder eventsUriBuilder = Instances.CONTENT_URI.buildUpon();
ContentUris.appendId(eventsUriBuilder, dtStart);
ContentUris.appendId(eventsUriBuilder, dtEnd);
Uri eventsUri = eventsUriBuilder.build();
try (Cursor evtCursor = mContext.getContentResolver().query(eventsUri, EVENT_INSTANCE_PROJECTION, null, null, Instances.BEGIN + " ASC")) {
if (evtCursor == null || evtCursor.getCount() == 0) {
return calendarEventList;
}
while (evtCursor.moveToNext()) {
long start = evtCursor.getLong(1);
long end = evtCursor.getLong(2);
if (end == 0) {
LOG.info("no end time, will parse duration string");
Time time = new Time(); //FIXME: deprecated FTW
time.parse(evtCursor.getString(3));
end = start + time.toMillis(false);
}
CalendarEvent calEvent = new CalendarEvent(
start,
end,
evtCursor.getLong(0),
evtCursor.getString(4),
evtCursor.getString(5),
evtCursor.getString(6),
evtCursor.getString(7),
evtCursor.getString(8),
evtCursor.getInt(9),
!evtCursor.getString(10).equals("0")
);
if (!calendarIsBlacklisted(calEvent.getUniqueCalName())) {
calendarEventList.add(calEvent);
} else {
LOG.debug("calendar {} skipped because it's blacklisted", calEvent.getUniqueCalName());
}
}
return calendarEventList;
} catch (final Exception e) {
LOG.error("could not query calendar, permission denied?", e);
return calendarEventList;
}
}
private static HashSet<String> calendars_blacklist = null;
public boolean calendarIsBlacklisted(String calendarUniqueName) {
if (calendars_blacklist == null) {
LOG.warn("calendarIsBlacklisted: calendars_blacklist is null!");
}
return calendars_blacklist != null && calendars_blacklist.contains(calendarUniqueName);
}
public void setCalendarsBlackList(Set<String> calendarNames) {
if (calendarNames == null) {
LOG.info("Set null apps_notification_blacklist");
calendars_blacklist = new HashSet<>();
} else {
calendars_blacklist = new HashSet<>(calendarNames);
}
LOG.info("New calendars_blacklist has {} entries", calendars_blacklist.size());
saveCalendarsBlackList();
}
public void addCalendarToBlacklist(String calendarUniqueName) {
if (calendars_blacklist.add(calendarUniqueName)) {
LOG.info("Blacklisted calendar " + calendarUniqueName);
saveCalendarsBlackList();
} else {
LOG.warn("Calendar {} already blacklisted!", calendarUniqueName);
}
}
public void removeFromCalendarBlacklist(String calendarUniqueName) {
calendars_blacklist.remove(calendarUniqueName);
LOG.info("Unblacklisted calendar " + calendarUniqueName);
saveCalendarsBlackList();
}
private void loadCalendarsBlackList() {
SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(deviceAddress);
LOG.info("Loading calendars_blacklist");
calendars_blacklist = (HashSet<String>) sharedPrefs.getStringSet(GBPrefs.CALENDAR_BLACKLIST, null);
if (calendars_blacklist == null) {
calendars_blacklist = new HashSet<>();
}
LOG.info("Loaded calendars_blacklist has {} entries", calendars_blacklist.size());
}
private void saveCalendarsBlackList() {
final SharedPreferences sharedPrefs = GBApplication.getDeviceSpecificSharedPrefs(deviceAddress);
LOG.info("Saving calendars_blacklist with {} entries", calendars_blacklist.size());
SharedPreferences.Editor editor = sharedPrefs.edit();
if (calendars_blacklist.isEmpty()) {
editor.putStringSet(GBPrefs.CALENDAR_BLACKLIST, null);
} else {
Prefs.putStringSet(editor, GBPrefs.CALENDAR_BLACKLIST, calendars_blacklist);
}
editor.apply();
}
}

View File

@ -205,6 +205,7 @@
<string name="pref_title_weather_summary">Used for the LineageOS weather provider, other Android versions need to use an app like \"Weather notification\". Find more information in the Gadgetbridge wiki.</string>
<string name="pref_applications_settings">Applications list</string>
<string name="pref_blacklist_calendars">Blacklist Calendars</string>
<string name="pref_blacklist_calendars_summary">Blacklisted calendars will not be synced to the device</string>
<string name="pref_header_cannned_messages">Canned messages</string>
<string name="pref_title_canned_replies">Replies</string>
<string name="pref_title_canned_reply_suffix">Common suffix</string>

View File

@ -6,4 +6,10 @@
android:key="sync_calendar"
android:summary="@string/pref_summary_sync_calendar"
android:title="@string/pref_title_sync_caldendar" />
<Preference
android:dependency="sync_calendar"
android:icon="@drawable/ic_block"
android:key="blacklist_calendars"
android:summary="@string/pref_blacklist_calendars_summary"
android:title="@string/pref_blacklist_calendars" />
</androidx.preference.PreferenceScreen>

View File

@ -194,15 +194,6 @@
</PreferenceCategory>
<PreferenceCategory android:title="@string/pref_header_pebble_timeline">
<CheckBoxPreference
android:layout="@layout/preference_checkbox"
android:defaultValue="true"
android:key="enable_calendar_sync"
android:summary="@string/pref_summary_enable_calendar_sync"
android:title="@string/pref_title_enable_calendar_sync" />
<Preference
android:key="pref_key_blacklist_calendars"
android:title="@string/pref_blacklist_calendars" />
<CheckBoxPreference
android:layout="@layout/preference_checkbox"
android:key="send_sunrise_sunset"
android:summary="@string/pref_summary_sunrise_sunset"

View File

@ -8,7 +8,7 @@ import java.util.List;
import nodomain.freeyourgadget.gadgetbridge.entities.CalendarSyncStateDao;
import nodomain.freeyourgadget.gadgetbridge.externalevents.CalendarReceiver;
import nodomain.freeyourgadget.gadgetbridge.impl.GBDevice;
import nodomain.freeyourgadget.gadgetbridge.model.CalendarEvents;
import nodomain.freeyourgadget.gadgetbridge.util.calendar.CalendarEvent;
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
@ -24,12 +24,12 @@ public class CalendarEventTest extends TestBase {
@Test
public void testHashCode() {
CalendarEvents.CalendarEvent c1 =
new CalendarEvents.CalendarEvent(BEGIN, END, ID_1, "something", null, null, CALNAME_1, CALACCOUNTNAME_1, COLOR_1, false);
CalendarEvents.CalendarEvent c2 =
new CalendarEvents.CalendarEvent(BEGIN, END, ID_1, null, "something", null, CALNAME_1, CALACCOUNTNAME_1, COLOR_1, false);
CalendarEvents.CalendarEvent c3 =
new CalendarEvents.CalendarEvent(BEGIN, END, ID_1, null, null, "something", CALNAME_1, CALACCOUNTNAME_1, COLOR_1, false);
CalendarEvent c1 =
new CalendarEvent(BEGIN, END, ID_1, "something", null, null, CALNAME_1, CALACCOUNTNAME_1, COLOR_1, false);
CalendarEvent c2 =
new CalendarEvent(BEGIN, END, ID_1, null, "something", null, CALNAME_1, CALACCOUNTNAME_1, COLOR_1, false);
CalendarEvent c3 =
new CalendarEvent(BEGIN, END, ID_1, null, null, "something", CALNAME_1, CALACCOUNTNAME_1, COLOR_1, false);
assertEquals(c1.hashCode(), c1.hashCode());
assertNotEquals(c1.hashCode(), c2.hashCode());
@ -39,8 +39,8 @@ public class CalendarEventTest extends TestBase {
@Test
public void testSync() {
List<CalendarEvents.CalendarEvent> eventList = new ArrayList<>();
eventList.add(new CalendarEvents.CalendarEvent(BEGIN, END, ID_1, null, "something", null, CALNAME_1, CALACCOUNTNAME_1, COLOR_1, false));
List<CalendarEvent> eventList = new ArrayList<>();
eventList.add(new CalendarEvent(BEGIN, END, ID_1, null, "something", null, CALNAME_1, CALACCOUNTNAME_1, COLOR_1, false));
GBDevice dummyGBDevice = createDummyGDevice("00:00:01:00:03");
dummyGBDevice.setState(GBDevice.State.INITIALIZED);
@ -49,7 +49,7 @@ public class CalendarEventTest extends TestBase {
testCR.syncCalendar(eventList);
eventList.add(new CalendarEvents.CalendarEvent(BEGIN, END, ID_2, null, "something", null, CALNAME_1, CALACCOUNTNAME_1, COLOR_1, false));
eventList.add(new CalendarEvent(BEGIN, END, ID_2, null, "something", null, CALNAME_1, CALACCOUNTNAME_1, COLOR_1, false));
testCR.syncCalendar(eventList);
CalendarSyncStateDao calendarSyncStateDao = daoSession.getCalendarSyncStateDao();