1
0
Fork 0
mirror of https://github.com/M66B/FairEmail.git synced 2024-12-29 19:25:34 +00:00

Added UI service

Unfinished work
This commit is contained in:
M66B 2019-02-27 15:05:15 +00:00
parent a736035bc9
commit 861cf0eb4a
12 changed files with 1876 additions and 1781 deletions

View file

@ -164,6 +164,8 @@
<service android:name=".ServiceSend" />
<service android:name=".ServiceUI" />
<service android:name=".ServiceExternal">
<intent-filter>
<action android:name="${applicationId}.ENABLE" />

View file

@ -292,10 +292,6 @@ public class AdapterFolder extends RecyclerView.Adapter<AdapterFolder.ViewHolder
long aid = args.getLong("account");
long fid = args.getLong("folder");
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
if (!prefs.getBoolean("enabled", true))
throw new IllegalStateException(context.getString(R.string.title_sync_disabled));
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getActiveNetworkInfo();
boolean internet = (ni != null && ni.isConnected());
@ -314,7 +310,7 @@ public class AdapterFolder extends RecyclerView.Adapter<AdapterFolder.ViewHolder
if (account.ondemand) {
if (internet) {
now = true;
ServiceSynchronize.sync(context, fid);
ServiceUI.sync(context, fid);
} else
throw new IllegalArgumentException(context.getString(R.string.title_no_internet));
} else {
@ -341,18 +337,7 @@ public class AdapterFolder extends RecyclerView.Adapter<AdapterFolder.ViewHolder
protected void onException(Bundle args, Throwable ex) {
if (ex instanceof IllegalArgumentException)
Snackbar.make(itemView, ex.getMessage(), Snackbar.LENGTH_LONG).show();
else if (ex instanceof IllegalStateException) {
Snackbar snackbar = Snackbar.make(itemView, ex.getMessage(), Snackbar.LENGTH_LONG);
snackbar.setAction(R.string.title_enable, new View.OnClickListener() {
@Override
public void onClick(View v) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
prefs.edit().putBoolean("enabled", true).apply();
ServiceSynchronize.reload(context, "refresh/disabled");
}
});
snackbar.show();
} else
else
Helper.unexpectedError(context, owner, ex);
}
}.execute(context, owner, args, "folder:sync");

File diff suppressed because it is too large Load diff

View file

@ -175,7 +175,7 @@ public class EntityMessage implements Serializable {
static void snooze(Context context, long id, Long wakeup) {
Intent snoozed = new Intent(context, ServiceSynchronize.class);
snoozed.setAction("snooze:" + id);
PendingIntent pi = PendingIntent.getService(context, ServiceSynchronize.PI_SNOOZED, snoozed, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent pi = PendingIntent.getService(context, ServiceUI.PI_SNOOZED, snoozed, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (wakeup == null) {

View file

@ -2078,10 +2078,6 @@ public class FragmentCompose extends FragmentBase {
if (!attachment.available)
throw new IllegalArgumentException(context.getString(R.string.title_attachments_missing));
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
if (!prefs.getBoolean("enabled", true))
throw new IllegalStateException(context.getString(R.string.title_sync_disabled));
// Delete draft (cannot move to outbox)
EntityOperation.queue(context, db, draft, EntityOperation.DELETE);
@ -2160,18 +2156,7 @@ public class FragmentCompose extends FragmentBase {
finish();
else if (ex instanceof IllegalArgumentException || ex instanceof AddressException)
Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show();
else if (ex instanceof IllegalStateException) {
Snackbar snackbar = Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG);
snackbar.setAction(R.string.title_enable, new View.OnClickListener() {
@Override
public void onClick(View v) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
prefs.edit().putBoolean("enabled", true).apply();
ServiceSynchronize.reload(getContext(), "compose/disabled");
}
});
snackbar.show();
} else
else
Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex);
}
};

View file

@ -492,10 +492,6 @@ public class FragmentMessages extends FragmentBase {
protected Boolean onExecute(Context context, Bundle args) {
long fid = args.getLong("folder");
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
if (!prefs.getBoolean("enabled", true))
throw new IllegalStateException(context.getString(R.string.title_sync_disabled));
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo ni = cm.getActiveNetworkInfo();
boolean internet = (ni != null && ni.isConnected());
@ -527,7 +523,7 @@ public class FragmentMessages extends FragmentBase {
if (account.ondemand) {
if (internet) {
now = true;
ServiceSynchronize.sync(context, folder.id);
ServiceUI.sync(context, folder.id);
} else
nointernet = true;
} else {
@ -561,18 +557,7 @@ public class FragmentMessages extends FragmentBase {
if (ex instanceof IllegalArgumentException)
Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG).show();
else if (ex instanceof IllegalStateException) {
Snackbar snackbar = Snackbar.make(view, ex.getMessage(), Snackbar.LENGTH_LONG);
snackbar.setAction(R.string.title_enable, new View.OnClickListener() {
@Override
public void onClick(View v) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext());
prefs.edit().putBoolean("enabled", true).apply();
ServiceSynchronize.reload(getContext(), "refresh/disabled");
}
});
snackbar.show();
} else
else
Helper.unexpectedError(getContext(), getViewLifecycleOwner(), ex);
}
}.execute(FragmentMessages.this, args, "messages:refresh");

View file

@ -997,38 +997,4 @@ public class Helper {
static String sanitizeFilename(String name) {
return (name == null ? null : name.replaceAll("[^a-zA-Z0-9\\.\\-]", "_"));
}
static NotificationCompat.Builder getNotificationError(Context context, String title, Throwable ex) {
return getNotificationError(context, "error", title, ex, true);
}
static NotificationCompat.Builder getNotificationError(Context context, String channel, String title, Throwable ex, boolean debug) {
// Build pending intent
Intent intent = new Intent(context, ActivitySetup.class);
if (debug)
intent.setAction("error");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pi = PendingIntent.getActivity(
context, ActivitySetup.REQUEST_ERROR, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Build notification
NotificationCompat.Builder builder = new NotificationCompat.Builder(context, channel);
builder
.setSmallIcon(R.drawable.baseline_warning_white_24)
.setContentTitle(context.getString(R.string.title_notification_failed, title))
.setContentText(Helper.formatThrowable(ex))
.setContentIntent(pi)
.setAutoCancel(false)
.setShowWhen(true)
.setPriority(Notification.PRIORITY_MAX)
.setOnlyAlertOnce(true)
.setCategory(Notification.CATEGORY_ERROR)
.setVisibility(NotificationCompat.VISIBILITY_SECRET);
builder.setStyle(new NotificationCompat.BigTextStyle()
.bigText(Helper.formatThrowable(ex, false, "\n")));
return builder;
}
}

View file

@ -374,11 +374,11 @@ public class ServiceSend extends LifecycleService {
long now = new Date().getTime();
long delayed = now - message.last_attempt;
if (delayed > IDENTITY_ERROR_AFTER * 60 * 1000L) {
if (delayed > IDENTITY_ERROR_AFTER * 60 * 1000L || ex instanceof SendFailedException) {
Log.i("Reporting send error after=" + delayed);
NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify("send", message.identity.intValue(),
Helper.getNotificationError(this, ident.name, ex).build());
Core.getNotificationError(this, ident.name, ex).build());
}
throw ex;

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,294 @@
package eu.faircode.email;
import android.app.IntentService;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.preference.PreferenceManager;
import com.sun.mail.imap.IMAPFolder;
import java.util.Properties;
import javax.mail.Folder;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Store;
import androidx.annotation.Nullable;
public class ServiceUI extends IntentService {
static final int PI_WHY = 1;
static final int PI_SUMMARY = 2;
static final int PI_CLEAR = 3;
static final int PI_SEEN = 4;
static final int PI_ARCHIVE = 5;
static final int PI_TRASH = 6;
static final int PI_IGNORED = 7;
static final int PI_SNOOZED = 8;
public ServiceUI() {
this(ServiceUI.class.getName());
}
public ServiceUI(String name) {
super(name);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
if (intent == null)
return;
// TODO: wakelock?
String action = intent.getAction();
if (action == null)
return;
String[] parts = action.split(":");
long id = (parts.length > 1 ? Long.parseLong(parts[1]) : -1);
switch (parts[0]) {
case "why":
onWhy();
break;
case "summary":
onSummary();
break;
case "clear":
onClear();
break;
case "seen":
onSeen(id);
break;
case "archive":
onArchive(id);
break;
case "trash":
onTrash(id);
break;
case "ignore":
onIgnore(id);
break;
case "snooze":
onSnooze(id);
break;
case "synchronize":
// AlarmManager.RTC_WAKEUP
onSyncOndemand(id);
break;
default:
Log.w("Unknown action: " + parts[0]);
}
}
private void onWhy() {
Intent why = new Intent(Intent.ACTION_VIEW);
why.setData(Uri.parse(Helper.FAQ_URI + "#user-content-faq2"));
why.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PackageManager pm = getPackageManager();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if (prefs.getBoolean("why", false) || why.resolveActivity(pm) == null) {
Intent view = new Intent(this, ActivityView.class);
view.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(view);
} else {
prefs.edit().putBoolean("why", true).apply();
startActivity(why);
}
}
private void onSummary() {
DB.getInstance(this).message().ignoreAll();
Intent view = new Intent(this, ActivityView.class);
view.setAction("unified");
view.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(view);
}
private void onClear() {
DB.getInstance(this).message().ignoreAll();
}
private void onSeen(long id) {
DB db = DB.getInstance(this);
try {
db.beginTransaction();
EntityMessage message = db.message().getMessage(id);
if (message != null)
EntityOperation.queue(this, db, message, EntityOperation.SEEN, true);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
private void onArchive(long id) {
DB db = DB.getInstance(this);
try {
db.beginTransaction();
EntityMessage message = db.message().getMessage(id);
if (message != null) {
EntityFolder archive = db.folder().getFolderByType(message.account, EntityFolder.ARCHIVE);
if (archive == null)
archive = db.folder().getFolderByType(message.account, EntityFolder.TRASH);
if (archive != null)
EntityOperation.queue(this, db, message, EntityOperation.MOVE, archive.id);
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
private void onTrash(long id) {
DB db = DB.getInstance(this);
try {
db.beginTransaction();
EntityMessage message = db.message().getMessage(id);
if (message != null) {
EntityFolder trash = db.folder().getFolderByType(message.account, EntityFolder.TRASH);
if (trash != null)
EntityOperation.queue(this, db, message, EntityOperation.MOVE, trash.id);
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
private void onIgnore(long id) {
DB db = DB.getInstance(this);
try {
db.beginTransaction();
EntityMessage message = db.message().getMessage(id);
if (message != null)
db.message().setMessageUiIgnored(message.id, true);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
private void onSnooze(long id) {
DB db = DB.getInstance(this);
try {
db.beginTransaction();
EntityMessage message = db.message().getMessage(id);
if (message != null) {
db.message().setMessageSnoozed(message.id, null);
EntityFolder folder = db.folder().getFolder(message.folder);
if (EntityFolder.OUTBOX.equals(folder.type)) {
Log.i("Delayed send id=" + message.id);
EntityOperation.queue(
this, db, message, EntityOperation.SEND);
} else {
EntityOperation.queue(
this, db, message, EntityOperation.SEEN, false);
db.message().setMessageUiIgnored(message.id, false);
}
}
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
private void onSyncOndemand(long fid) {
Log.i("Synchronize on demand folder=" + fid);
DB db = DB.getInstance(this);
EntityFolder folder = null;
EntityAccount account = null;
Store istore = null;
try {
folder = db.folder().getFolder(fid);
account = db.account().getAccount(folder.account);
// Create session
Properties props = MessageHelper.getSessionProperties(account.auth_type, account.realm, account.insecure);
final Session isession = Session.getInstance(props, null);
isession.setDebug(true);
// Connect account
Log.i(account.name + " connecting");
db.account().setAccountState(account.id, "connecting");
istore = isession.getStore(account.getProtocol());
Helper.connect(this, istore, account);
db.account().setAccountState(account.id, "connected");
Log.i(account.name + " connected");
// Synchronize folders
Core.synchronizeFolders(this, account, istore, new Core.State());
// Connect folder
Log.i(folder.name + " connecting");
db.folder().setFolderState(folder.id, "connecting");
Folder ifolder = istore.getFolder(folder.name);
ifolder.open(Folder.READ_WRITE);
db.folder().setFolderState(folder.id, "connected");
db.folder().setFolderError(folder.id, null);
Log.i(folder.name + " connected");
// Process operations
Core.processOperations(this, account, folder, isession, istore, ifolder, new Core.State());
// Synchronize messages
Core.synchronizeMessages(this, account, folder, (IMAPFolder) ifolder, folder.getSyncArgs(), new Core.State());
} catch (Throwable ex) {
Log.w(ex);
Core.reportError(this, account, folder, ex);
db.account().setAccountError(account.id, Helper.formatThrowable(ex));
db.folder().setFolderError(folder.id, Helper.formatThrowable(ex, false));
} finally {
if (istore != null) {
Log.i(account.name + " closing");
db.account().setAccountState(account.id, "closing");
db.folder().setFolderState(folder.id, "closing");
try {
istore.close();
} catch (MessagingException ex) {
Log.e(ex);
}
db.account().setAccountState(account.id, null);
db.folder().setFolderState(folder.id, null);
Log.i(account.name + " closed");
}
}
}
public static void sync(Context context, long folder) {
context.startService(
new Intent(context, ServiceUI.class)
.setAction("synchronize:" + folder));
}
}

View file

@ -306,7 +306,7 @@ public class ViewModelBrowse extends ViewModel {
Log.i("Boundary sync uid=" + uid);
EntityMessage message = db.message().getMessageByUid(folder.id, uid);
if (message == null) {
message = ServiceSynchronize.synchronizeMessage(state.context,
message = Core.synchronizeMessage(state.context,
folder, state.ifolder, (IMAPMessage) isub[j],
true,
new ArrayList<EntityRule>());

View file

@ -256,7 +256,6 @@
<string name="title_no_uidplus">This provider does not support UIDPLUS</string>
<string name="title_no_utf8">This provider does not support UTF-8</string>
<string name="title_no_sync">Synchronization errors since %1$s</string>
<string name="title_sync_disabled">Synchronization is disabled</string>
<string name="title_sync_delayed">Synchronization will be performed on the next account connection</string>
<string name="title_drafts_required">A drafts folder is required to send messages</string>
<string name="title_account_delete">Delete this account permanently?</string>