|
|
@ -19,21 +19,28 @@ package eu.faircode.email;
|
|
|
|
Copyright 2018-2022 by Marcel Bokhorst (M66B)
|
|
|
|
Copyright 2018-2022 by Marcel Bokhorst (M66B)
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import android.Manifest;
|
|
|
|
import android.app.AlarmManager;
|
|
|
|
import android.app.AlarmManager;
|
|
|
|
import android.app.Notification;
|
|
|
|
import android.app.Notification;
|
|
|
|
import android.app.NotificationManager;
|
|
|
|
import android.app.NotificationManager;
|
|
|
|
import android.app.PendingIntent;
|
|
|
|
import android.app.PendingIntent;
|
|
|
|
import android.content.BroadcastReceiver;
|
|
|
|
import android.content.BroadcastReceiver;
|
|
|
|
|
|
|
|
import android.content.ContentResolver;
|
|
|
|
|
|
|
|
import android.content.ContentUris;
|
|
|
|
|
|
|
|
import android.content.ContentValues;
|
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.content.IntentFilter;
|
|
|
|
import android.content.IntentFilter;
|
|
|
|
import android.content.SharedPreferences;
|
|
|
|
import android.content.SharedPreferences;
|
|
|
|
|
|
|
|
import android.database.Cursor;
|
|
|
|
import android.net.ConnectivityManager;
|
|
|
|
import android.net.ConnectivityManager;
|
|
|
|
import android.net.Network;
|
|
|
|
import android.net.Network;
|
|
|
|
import android.net.NetworkCapabilities;
|
|
|
|
import android.net.NetworkCapabilities;
|
|
|
|
import android.net.NetworkRequest;
|
|
|
|
import android.net.NetworkRequest;
|
|
|
|
|
|
|
|
import android.net.Uri;
|
|
|
|
import android.os.PowerManager;
|
|
|
|
import android.os.PowerManager;
|
|
|
|
import android.os.SystemClock;
|
|
|
|
import android.os.SystemClock;
|
|
|
|
|
|
|
|
import android.provider.CalendarContract;
|
|
|
|
import android.text.TextUtils;
|
|
|
|
import android.text.TextUtils;
|
|
|
|
|
|
|
|
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
|
import androidx.annotation.NonNull;
|
|
|
@ -68,6 +75,14 @@ import javax.mail.Session;
|
|
|
|
import javax.mail.internet.InternetAddress;
|
|
|
|
import javax.mail.internet.InternetAddress;
|
|
|
|
import javax.mail.internet.MimeMessage;
|
|
|
|
import javax.mail.internet.MimeMessage;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import biweekly.Biweekly;
|
|
|
|
|
|
|
|
import biweekly.ICalendar;
|
|
|
|
|
|
|
|
import biweekly.component.VEvent;
|
|
|
|
|
|
|
|
import biweekly.parameter.ParticipationStatus;
|
|
|
|
|
|
|
|
import biweekly.property.Attendee;
|
|
|
|
|
|
|
|
import biweekly.property.Method;
|
|
|
|
|
|
|
|
import biweekly.property.Uid;
|
|
|
|
|
|
|
|
|
|
|
|
public class ServiceSend extends ServiceBase implements SharedPreferences.OnSharedPreferenceChangeListener {
|
|
|
|
public class ServiceSend extends ServiceBase implements SharedPreferences.OnSharedPreferenceChangeListener {
|
|
|
|
private TupleUnsent lastUnsent = null;
|
|
|
|
private TupleUnsent lastUnsent = null;
|
|
|
|
private Network lastActive = null;
|
|
|
|
private Network lastActive = null;
|
|
|
@ -850,18 +865,82 @@ public class ServiceSend extends ServiceBase implements SharedPreferences.OnShar
|
|
|
|
|
|
|
|
|
|
|
|
// Message could have been deleted
|
|
|
|
// Message could have been deleted
|
|
|
|
EntityMessage orphan = db.message().getMessage(sid);
|
|
|
|
EntityMessage orphan = db.message().getMessage(sid);
|
|
|
|
if (orphan != null)
|
|
|
|
if (orphan != null) {
|
|
|
|
EntityOperation.queue(this, orphan, EntityOperation.EXISTS);
|
|
|
|
EntityOperation.queue(this, orphan, EntityOperation.EXISTS);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
db.setTransactionSuccessful();
|
|
|
|
db.setTransactionSuccessful();
|
|
|
|
} finally {
|
|
|
|
} finally {
|
|
|
|
db.endTransaction();
|
|
|
|
db.endTransaction();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
checkICalendar(sid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ServiceSynchronize.eval(this, "sent");
|
|
|
|
ServiceSynchronize.eval(this, "sent");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private void checkICalendar(long sid) {
|
|
|
|
|
|
|
|
boolean permission = Helper.hasPermission(this, Manifest.permission.WRITE_CALENDAR);
|
|
|
|
|
|
|
|
if (!permission)
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
DB db = DB.getInstance(this);
|
|
|
|
|
|
|
|
List<EntityAttachment> attachments = db.attachment().getAttachments(sid);
|
|
|
|
|
|
|
|
if (attachments == null || attachments.size() == 0)
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (EntityAttachment attachment : attachments)
|
|
|
|
|
|
|
|
if ("text/calendar".equals(attachment.type))
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
File ics = attachment.getFile(this);
|
|
|
|
|
|
|
|
ICalendar icalendar = Biweekly.parse(ics).first();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Method method = icalendar.getMethod();
|
|
|
|
|
|
|
|
if (method == null || !method.isReply())
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
VEvent event = icalendar.getEvents().get(0);
|
|
|
|
|
|
|
|
Uid uid = event.getUid();
|
|
|
|
|
|
|
|
List<Attendee> attendees = event.getAttendees();
|
|
|
|
|
|
|
|
if (uid == null || attendees == null || attendees.size() == 0)
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ParticipationStatus status = attendees.get(0).getParticipationStatus();
|
|
|
|
|
|
|
|
if (!ParticipationStatus.ACCEPTED.equals(status) &&
|
|
|
|
|
|
|
|
!ParticipationStatus.DECLINED.equals(status))
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ContentResolver resolver = getContentResolver();
|
|
|
|
|
|
|
|
try (Cursor cursor = resolver.query(CalendarContract.Events.CONTENT_URI,
|
|
|
|
|
|
|
|
new String[]{CalendarContract.Events._ID},
|
|
|
|
|
|
|
|
CalendarContract.Events.UID_2445 + " = ? ",
|
|
|
|
|
|
|
|
new String[]{uid.getValue()},
|
|
|
|
|
|
|
|
null)) {
|
|
|
|
|
|
|
|
while (cursor.moveToNext()) {
|
|
|
|
|
|
|
|
long eventId = cursor.getLong(0);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// https://developer.android.com/guide/topics/providers/calendar-provider#modify-calendar
|
|
|
|
|
|
|
|
Uri updateUri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventId);
|
|
|
|
|
|
|
|
ContentValues values = new ContentValues();
|
|
|
|
|
|
|
|
if (ParticipationStatus.ACCEPTED.equals(status))
|
|
|
|
|
|
|
|
values.put(CalendarContract.Events.STATUS, CalendarContract.Events.STATUS_CONFIRMED);
|
|
|
|
|
|
|
|
else
|
|
|
|
|
|
|
|
values.put(CalendarContract.Events.STATUS, CalendarContract.Events.STATUS_CANCELED);
|
|
|
|
|
|
|
|
int rows = resolver.update(updateUri, values, null, null);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
EntityMessage message = db.message().getMessage(sid);
|
|
|
|
|
|
|
|
EntityLog.log(this, EntityLog.Type.General, message,
|
|
|
|
|
|
|
|
"Updated event id=" + eventId + " rows=" + rows);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
} catch (Throwable ex) {
|
|
|
|
|
|
|
|
Log.e(ex);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void boot(final Context context) {
|
|
|
|
static void boot(final Context context) {
|
|
|
|
executor.submit(new Runnable() {
|
|
|
|
executor.submit(new Runnable() {
|
|
|
|
@Override
|
|
|
|
@Override
|
|
|
|