mirror of https://github.com/M66B/FairEmail.git
Replaced forward raw by download/save raw, fixed saving attachments
This commit is contained in:
parent
9490455682
commit
2539f540f3
1
FAQ.md
1
FAQ.md
|
@ -179,6 +179,7 @@ The low priority status bar notification shows the number of pending operations,
|
|||
* *flag*: add/remove star in remote folder
|
||||
* *keyword*: add/remove IMAP flag in remote folder
|
||||
* *headers*: download message headers
|
||||
* *raw*: download raw message
|
||||
* *body*: download message text
|
||||
* *attachment*: download attachment
|
||||
* *sync*: synchronize local and remove folder
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -60,7 +60,6 @@ import org.openintents.openpgp.util.OpenPgpApi;
|
|||
import org.openintents.openpgp.util.OpenPgpServiceConnection;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
@ -120,13 +119,15 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB
|
|||
static final int REQUEST_UNIFIED = 1;
|
||||
static final int REQUEST_THREAD = 2;
|
||||
|
||||
static final int REQUEST_ATTACHMENT = 1;
|
||||
static final int REQUEST_ATTACHMENTS = 2;
|
||||
static final int REQUEST_DECRYPT = 3;
|
||||
static final int REQUEST_RAW = 1;
|
||||
static final int REQUEST_ATTACHMENT = 2;
|
||||
static final int REQUEST_ATTACHMENTS = 3;
|
||||
static final int REQUEST_DECRYPT = 4;
|
||||
|
||||
static final String ACTION_VIEW_MESSAGES = BuildConfig.APPLICATION_ID + ".VIEW_MESSAGES";
|
||||
static final String ACTION_VIEW_THREAD = BuildConfig.APPLICATION_ID + ".VIEW_THREAD";
|
||||
static final String ACTION_VIEW_FULL = BuildConfig.APPLICATION_ID + ".VIEW_FULL";
|
||||
static final String ACTION_STORE_RAW = BuildConfig.APPLICATION_ID + ".STORE_RAW";
|
||||
static final String ACTION_EDIT_FOLDER = BuildConfig.APPLICATION_ID + ".EDIT_FOLDER";
|
||||
static final String ACTION_EDIT_ANSWER = BuildConfig.APPLICATION_ID + ".EDIT_ANSWER";
|
||||
static final String ACTION_STORE_ATTACHMENT = BuildConfig.APPLICATION_ID + ".STORE_ATTACHMENT";
|
||||
|
@ -470,6 +471,7 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB
|
|||
iff.addAction(ACTION_VIEW_MESSAGES);
|
||||
iff.addAction(ACTION_VIEW_THREAD);
|
||||
iff.addAction(ACTION_VIEW_FULL);
|
||||
iff.addAction(ACTION_STORE_RAW);
|
||||
iff.addAction(ACTION_EDIT_FOLDER);
|
||||
iff.addAction(ACTION_EDIT_ANSWER);
|
||||
iff.addAction(ACTION_STORE_ATTACHMENT);
|
||||
|
@ -1001,6 +1003,8 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB
|
|||
onViewThread(intent);
|
||||
else if (ACTION_VIEW_FULL.equals(action))
|
||||
onViewFull(intent);
|
||||
else if (ACTION_STORE_RAW.equals(action))
|
||||
onStoreRaw(intent);
|
||||
else if (ACTION_EDIT_FOLDER.equals(action))
|
||||
onEditFolder(intent);
|
||||
else if (ACTION_EDIT_ANSWER.equals(action))
|
||||
|
@ -1069,6 +1073,18 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB
|
|||
fragmentTransaction.commit();
|
||||
}
|
||||
|
||||
private void onStoreRaw(Intent intent) {
|
||||
message = intent.getLongExtra("id", -1);
|
||||
Intent create = new Intent(Intent.ACTION_CREATE_DOCUMENT);
|
||||
create.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
create.setType("*/*");
|
||||
create.putExtra(Intent.EXTRA_TITLE, "email.eml");
|
||||
if (create.resolveActivity(getPackageManager()) == null)
|
||||
Snackbar.make(getVisibleView(), R.string.title_no_saf, Snackbar.LENGTH_LONG).show();
|
||||
else
|
||||
startActivityForResult(Helper.getChooser(this, create), REQUEST_RAW);
|
||||
}
|
||||
|
||||
private void onEditFolder(Intent intent) {
|
||||
FragmentFolder fragment = new FragmentFolder();
|
||||
fragment.setArguments(intent.getExtras());
|
||||
|
@ -1291,7 +1307,10 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB
|
|||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
if (resultCode == Activity.RESULT_OK)
|
||||
if (requestCode == REQUEST_ATTACHMENT) {
|
||||
if (requestCode == REQUEST_RAW) {
|
||||
if (data != null)
|
||||
saveRaw(data);
|
||||
} else if (requestCode == REQUEST_ATTACHMENT) {
|
||||
if (data != null)
|
||||
saveAttachment(data);
|
||||
} else if (requestCode == REQUEST_ATTACHMENTS) {
|
||||
|
@ -1304,6 +1323,76 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB
|
|||
}
|
||||
}
|
||||
|
||||
private void saveRaw(Intent data) {
|
||||
Bundle args = new Bundle();
|
||||
args.putLong("id", message);
|
||||
args.putParcelable("uri", data.getData());
|
||||
|
||||
new SimpleTask<Void>() {
|
||||
@Override
|
||||
protected Void onExecute(Context context, Bundle args) throws Throwable {
|
||||
long id = args.getLong("id");
|
||||
Uri uri = args.getParcelable("uri");
|
||||
|
||||
if ("file".equals(uri.getScheme())) {
|
||||
Log.w("Save raw uri=" + uri);
|
||||
throw new IllegalArgumentException(context.getString(R.string.title_no_stream));
|
||||
}
|
||||
|
||||
File file = EntityMessage.getRawFile(context, id);
|
||||
Log.i("Raw file=" + file);
|
||||
|
||||
ParcelFileDescriptor pfd = null;
|
||||
OutputStream os = null;
|
||||
InputStream is = null;
|
||||
try {
|
||||
pfd = context.getContentResolver().openFileDescriptor(uri, "w");
|
||||
os = new FileOutputStream(pfd.getFileDescriptor());
|
||||
is = new BufferedInputStream(new FileInputStream(file));
|
||||
|
||||
byte[] buffer = new byte[ATTACHMENT_BUFFER_SIZE];
|
||||
int read;
|
||||
while ((read = is.read(buffer)) != -1)
|
||||
os.write(buffer, 0, read);
|
||||
} finally {
|
||||
try {
|
||||
if (pfd != null)
|
||||
pfd.close();
|
||||
} catch (Throwable ex) {
|
||||
Log.w(ex);
|
||||
}
|
||||
try {
|
||||
if (os != null)
|
||||
os.close();
|
||||
} catch (Throwable ex) {
|
||||
Log.w(ex);
|
||||
}
|
||||
try {
|
||||
if (is != null)
|
||||
is.close();
|
||||
} catch (Throwable ex) {
|
||||
Log.w(ex);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onExecuted(Bundle args, Void data) {
|
||||
Toast.makeText(ActivityView.this, R.string.title_raw_saved, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onException(Bundle args, Throwable ex) {
|
||||
if (ex instanceof IllegalArgumentException)
|
||||
Snackbar.make(getVisibleView(), ex.getMessage(), Snackbar.LENGTH_LONG).show();
|
||||
else
|
||||
Helper.unexpectedError(ActivityView.this, ActivityView.this, ex);
|
||||
}
|
||||
}.execute(this, args, "raw:save");
|
||||
}
|
||||
|
||||
private void saveAttachment(Intent data) {
|
||||
Bundle args = new Bundle();
|
||||
args.putLong("id", attachment);
|
||||
|
@ -1327,7 +1416,7 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB
|
|||
InputStream is = null;
|
||||
try {
|
||||
pfd = context.getContentResolver().openFileDescriptor(uri, "w");
|
||||
os = new BufferedOutputStream(new FileOutputStream(pfd.getFileDescriptor()));
|
||||
os = new FileOutputStream(pfd.getFileDescriptor());
|
||||
is = new BufferedInputStream(new FileInputStream(file));
|
||||
|
||||
byte[] buffer = new byte[ATTACHMENT_BUFFER_SIZE];
|
||||
|
@ -1399,7 +1488,7 @@ public class ActivityView extends ActivityBilling implements FragmentManager.OnB
|
|||
InputStream is = null;
|
||||
try {
|
||||
pfd = context.getContentResolver().openFileDescriptor(document.getUri(), "w");
|
||||
os = new BufferedOutputStream(new FileOutputStream(pfd.getFileDescriptor()));
|
||||
os = new FileOutputStream(pfd.getFileDescriptor());
|
||||
is = new BufferedInputStream(new FileInputStream(file));
|
||||
|
||||
byte[] buffer = new byte[ATTACHMENT_BUFFER_SIZE];
|
||||
|
|
|
@ -1272,7 +1272,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
|
|||
.show();
|
||||
}
|
||||
|
||||
private void onMenuForward(final ActionData data, final boolean raw) {
|
||||
private void onMenuForward(final ActionData data) {
|
||||
Bundle args = new Bundle();
|
||||
args.putLong("id", data.message.id);
|
||||
|
||||
|
@ -1291,8 +1291,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
|
|||
protected void onExecuted(Bundle args, Boolean available) {
|
||||
final Intent forward = new Intent(context, ActivityCompose.class)
|
||||
.putExtra("action", "forward")
|
||||
.putExtra("reference", data.message.id)
|
||||
.putExtra("raw", raw);
|
||||
.putExtra("reference", data.message.id);
|
||||
if (available)
|
||||
context.startActivity(forward);
|
||||
else
|
||||
|
@ -1515,6 +1514,48 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
|
|||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
private void onMenuRaw(ActionData data) {
|
||||
if (data.message.raw == null) {
|
||||
Bundle args = new Bundle();
|
||||
args.putLong("id", data.message.id);
|
||||
|
||||
new SimpleTask<Void>() {
|
||||
@Override
|
||||
protected Void onExecute(Context context, Bundle args) {
|
||||
Long id = args.getLong("id");
|
||||
|
||||
DB db = DB.getInstance(context);
|
||||
try {
|
||||
db.beginTransaction();
|
||||
|
||||
EntityMessage message = db.message().getMessage(id);
|
||||
if (message == null)
|
||||
return null;
|
||||
|
||||
EntityOperation.queue(context, db, message, EntityOperation.RAW);
|
||||
|
||||
db.message().setMessageRaw(message.id, false);
|
||||
|
||||
db.setTransactionSuccessful();
|
||||
} finally {
|
||||
db.endTransaction();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onException(Bundle args, Throwable ex) {
|
||||
Helper.unexpectedError(context, owner, ex);
|
||||
}
|
||||
}.execute(context, owner, args, "message:raw");
|
||||
} else {
|
||||
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
|
||||
lbm.sendBroadcast(
|
||||
new Intent(ActivityView.ACTION_STORE_RAW)
|
||||
.putExtra("id", data.message.id));
|
||||
}
|
||||
}
|
||||
|
||||
private void onMenuManageKeywords(ActionData data) {
|
||||
if (!Helper.isPro(context)) {
|
||||
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(context);
|
||||
|
@ -1663,7 +1704,6 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
|
|||
popupMenu.getMenu().findItem(R.id.menu_junk).setVisible(data.hasJunk);
|
||||
|
||||
popupMenu.getMenu().findItem(R.id.menu_forward).setEnabled(data.message.content);
|
||||
popupMenu.getMenu().findItem(R.id.menu_forward_raw).setVisible(data.message.content && data.message.headers != null);
|
||||
|
||||
popupMenu.getMenu().findItem(R.id.menu_reply_all).setEnabled(data.message.content);
|
||||
popupMenu.getMenu().findItem(R.id.menu_answer).setEnabled(data.message.content);
|
||||
|
@ -1677,6 +1717,12 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
|
|||
popupMenu.getMenu().findItem(R.id.menu_show_headers).setChecked(show_headers);
|
||||
popupMenu.getMenu().findItem(R.id.menu_show_headers).setEnabled(data.message.uid != null);
|
||||
|
||||
popupMenu.getMenu().findItem(R.id.menu_raw).setVisible(show_headers);
|
||||
popupMenu.getMenu().findItem(R.id.menu_raw).setEnabled(
|
||||
data.message.uid != null && (data.message.raw == null || data.message.raw));
|
||||
popupMenu.getMenu().findItem(R.id.menu_raw).setTitle(
|
||||
data.message.raw == null || !data.message.raw ? R.string.title_raw_download : R.string.title_raw_save);
|
||||
|
||||
popupMenu.getMenu().findItem(R.id.menu_manage_keywords).setEnabled(data.message.uid != null);
|
||||
|
||||
popupMenu.getMenu().findItem(R.id.menu_decrypt).setEnabled(
|
||||
|
@ -1690,10 +1736,7 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
|
|||
onMenuJunk(data);
|
||||
return true;
|
||||
case R.id.menu_forward:
|
||||
onMenuForward(data, false);
|
||||
return true;
|
||||
case R.id.menu_forward_raw:
|
||||
onMenuForward(data, true);
|
||||
onMenuForward(data);
|
||||
return true;
|
||||
case R.id.menu_reply_all:
|
||||
onActionReply(data, true);
|
||||
|
@ -1710,6 +1753,9 @@ public class AdapterMessage extends RecyclerView.Adapter<AdapterMessage.ViewHold
|
|||
case R.id.menu_show_headers:
|
||||
onMenuShowHeaders(data);
|
||||
return true;
|
||||
case R.id.menu_raw:
|
||||
onMenuRaw(data);
|
||||
return true;
|
||||
case R.id.menu_manage_keywords:
|
||||
onMenuManageKeywords(data);
|
||||
return true;
|
||||
|
|
|
@ -49,7 +49,7 @@ import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory;
|
|||
// https://developer.android.com/topic/libraries/architecture/room.html
|
||||
|
||||
@Database(
|
||||
version = 33,
|
||||
version = 34,
|
||||
entities = {
|
||||
EntityIdentity.class,
|
||||
EntityAccount.class,
|
||||
|
@ -412,6 +412,13 @@ public abstract class DB extends RoomDatabase {
|
|||
db.execSQL("ALTER TABLE `identity` ADD COLUMN `realm` TEXT");
|
||||
}
|
||||
})
|
||||
.addMigrations(new Migration(33, 34) {
|
||||
@Override
|
||||
public void migrate(SupportSQLiteDatabase db) {
|
||||
Log.i("DB migration from version " + startVersion + " to " + endVersion);
|
||||
db.execSQL("ALTER TABLE `message` ADD COLUMN `raw` INTEGER");
|
||||
}
|
||||
})
|
||||
.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -305,6 +305,9 @@ public interface DaoMessage {
|
|||
@Query("UPDATE message SET headers = :headers WHERE id = :id")
|
||||
int setMessageHeaders(long id, String headers);
|
||||
|
||||
@Query("UPDATE message SET raw = :raw WHERE id = :id")
|
||||
int setMessageRaw(long id, Boolean raw);
|
||||
|
||||
@Query("UPDATE message SET stored = :stored WHERE id = :id")
|
||||
int setMessageStored(long id, long stored);
|
||||
|
||||
|
|
|
@ -115,6 +115,7 @@ public class EntityMessage implements Serializable {
|
|||
public Address[] bcc;
|
||||
public Address[] reply;
|
||||
public String headers;
|
||||
public Boolean raw;
|
||||
public String subject;
|
||||
public Integer size;
|
||||
@NonNull
|
||||
|
@ -204,6 +205,13 @@ public class EntityMessage implements Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
static File getRawFile(Context context, Long id) {
|
||||
File dir = new File(context.getFilesDir(), "raw");
|
||||
if (!dir.exists())
|
||||
dir.mkdir();
|
||||
return new File(dir, Long.toString(id));
|
||||
}
|
||||
|
||||
private class ContactInfo {
|
||||
Uri lookupUri;
|
||||
String displayName;
|
||||
|
@ -318,6 +326,7 @@ public class EntityMessage implements Serializable {
|
|||
MessageHelper.equal(this.bcc, other.bcc) &&
|
||||
MessageHelper.equal(this.reply, other.reply) &&
|
||||
(this.headers == null ? other.headers == null : this.headers.equals(other.headers)) &&
|
||||
(this.raw == null ? other.raw == null : this.raw.equals(other.raw)) &&
|
||||
(this.subject == null ? other.subject == null : this.subject.equals(other.subject)) &&
|
||||
(this.size == null ? other.size == null : this.size.equals(other.size)) &&
|
||||
this.content == other.content &&
|
||||
|
@ -364,6 +373,7 @@ public class EntityMessage implements Serializable {
|
|||
MessageHelper.equal(this.bcc, other.bcc) &&
|
||||
MessageHelper.equal(this.reply, other.reply) &&
|
||||
(this.headers == null ? other.headers == null : this.headers.equals(other.headers)) &&
|
||||
(this.raw == null ? other.raw == null : this.raw.equals(other.raw)) &&
|
||||
(this.subject == null ? other.subject == null : this.subject.equals(other.subject)) &&
|
||||
(this.size == null ? other.size == null : this.size.equals(other.size)) &&
|
||||
this.content == other.content &&
|
||||
|
|
|
@ -73,6 +73,7 @@ public class EntityOperation {
|
|||
static final String FLAG = "flag";
|
||||
static final String KEYWORD = "keyword";
|
||||
static final String HEADERS = "headers";
|
||||
static final String RAW = "raw";
|
||||
static final String BODY = "body";
|
||||
static final String ATTACHMENT = "attachment";
|
||||
static final String SYNC = "sync";
|
||||
|
|
|
@ -1364,7 +1364,6 @@ public class FragmentCompose extends FragmentBase {
|
|||
String action = args.getString("action");
|
||||
long id = args.getLong("id", -1);
|
||||
long reference = args.getLong("reference", -1);
|
||||
boolean raw = args.getBoolean("raw", false);
|
||||
long answer = args.getLong("answer", -1);
|
||||
|
||||
Log.i("Load draft action=" + action + " id=" + id + " reference=" + reference);
|
||||
|
@ -1598,30 +1597,6 @@ public class FragmentCompose extends FragmentBase {
|
|||
File target = EntityAttachment.getFile(context, attachment.id);
|
||||
Helper.copy(source, target);
|
||||
}
|
||||
|
||||
if (raw) {
|
||||
EntityAttachment headers = new EntityAttachment();
|
||||
headers.message = result.draft.id;
|
||||
headers.sequence = ++sequence;
|
||||
headers.name = "headers.txt";
|
||||
headers.type = "text/plan";
|
||||
headers.available = true;
|
||||
headers.id = db.attachment().insertAttachment(headers);
|
||||
|
||||
headers.write(context, ref.headers);
|
||||
|
||||
EntityAttachment content = new EntityAttachment();
|
||||
content.message = result.draft.id;
|
||||
content.sequence = ++sequence;
|
||||
content.name = "content.html";
|
||||
content.type = "text/html";
|
||||
content.available = true;
|
||||
content.id = db.attachment().insertAttachment(content);
|
||||
|
||||
File csource = EntityMessage.getFile(context, ref.id);
|
||||
File ctarget = EntityAttachment.getFile(context, content.id);
|
||||
Helper.copy(csource, ctarget);
|
||||
}
|
||||
}
|
||||
|
||||
EntityOperation.queue(context, db, result.draft, EntityOperation.ADD);
|
||||
|
|
|
@ -137,6 +137,20 @@ public class JobDaily extends JobService {
|
|||
Log.w("Error deleting " + file);
|
||||
}
|
||||
|
||||
// Cleanup attachment files
|
||||
Log.i("Cleanup raw files");
|
||||
File[] raw = new File(context.getFilesDir(), "raw").listFiles();
|
||||
if (raw != null)
|
||||
for (File file : raw)
|
||||
if (file.isFile()) {
|
||||
long id = Long.parseLong(file.getName());
|
||||
if (db.message().countMessage(id) == 0) {
|
||||
Log.i("Cleanup raw id=" + id);
|
||||
if (!file.delete())
|
||||
Log.w("Error deleting " + file);
|
||||
}
|
||||
}
|
||||
|
||||
Log.i("Cleanup log");
|
||||
long before = now - KEEP_LOG_DURATION;
|
||||
int logs = db.log().deleteLogs(before);
|
||||
|
|
|
@ -1517,6 +1517,9 @@ public class ServiceSynchronize extends LifecycleService {
|
|||
else if (EntityOperation.HEADERS.equals(op.name))
|
||||
doHeaders(folder, ifolder, message, db);
|
||||
|
||||
else if (EntityOperation.RAW.equals(op.name))
|
||||
doRaw(folder, ifolder, message, db);
|
||||
|
||||
else if (EntityOperation.BODY.equals(op.name))
|
||||
doBody(folder, ifolder, message, db);
|
||||
|
||||
|
@ -1923,18 +1926,17 @@ public class ServiceSynchronize extends LifecycleService {
|
|||
}
|
||||
|
||||
db.message().setMessageHeaders(message.id, sb.toString());
|
||||
}
|
||||
|
||||
private void doRaw(EntityFolder folder, IMAPFolder ifolder, EntityMessage message, DB db) throws MessagingException, IOException {
|
||||
Message imessage = ifolder.getMessageByUID(message.uid);
|
||||
if (imessage == null)
|
||||
throw new MessageRemovedException();
|
||||
|
||||
if (imessage instanceof MimeMessage) {
|
||||
MimeMessage mmessage = (MimeMessage) imessage;
|
||||
EntityAttachment attachment = new EntityAttachment();
|
||||
attachment.message = message.id;
|
||||
attachment.sequence = db.attachment().getAttachmentSequence(message.id) + 1;
|
||||
attachment.name = "email.eml";
|
||||
attachment.type = "message/rfc822";
|
||||
attachment.available = false;
|
||||
attachment.id = db.attachment().insertAttachment(attachment);
|
||||
|
||||
File file = EntityAttachment.getFile(this, attachment.id);
|
||||
File file = EntityMessage.getRawFile(this, message.id);
|
||||
|
||||
OutputStream os = null;
|
||||
try {
|
||||
|
@ -1945,7 +1947,7 @@ public class ServiceSynchronize extends LifecycleService {
|
|||
os.close();
|
||||
}
|
||||
|
||||
db.attachment().setDownloaded(attachment.id, file.length());
|
||||
db.message().setMessageRaw(message.id, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -8,10 +8,6 @@
|
|||
android:id="@+id/menu_forward"
|
||||
android:title="@string/title_forward" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_forward_raw"
|
||||
android:title="@string/title_forward_raw" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_reply_all"
|
||||
android:title="@string/title_reply_all" />
|
||||
|
@ -33,6 +29,10 @@
|
|||
android:checkable="true"
|
||||
android:title="@string/title_show_headers" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_raw"
|
||||
android:title="@string/title_raw_download" />
|
||||
|
||||
<item
|
||||
android:id="@+id/menu_manage_keywords"
|
||||
android:title="@string/title_manage_keywords" />
|
||||
|
|
|
@ -269,10 +269,11 @@
|
|||
<string name="title_flag">Add star</string>
|
||||
<string name="title_unflag">Remove star</string>
|
||||
<string name="title_forward">Forward</string>
|
||||
<string name="title_forward_raw">Forward raw</string>
|
||||
<string name="title_reply_all">Reply to all</string>
|
||||
<string name="title_share">Share</string>
|
||||
<string name="title_show_headers">Show headers</string>
|
||||
<string name="title_raw_download">Download raw message</string>
|
||||
<string name="title_raw_save">Save raw message</string>
|
||||
<string name="title_manage_keywords">Manage keywords</string>
|
||||
<string name="title_add_keyword">Add keyword</string>
|
||||
<string name="title_download_all">Download all</string>
|
||||
|
@ -296,6 +297,7 @@
|
|||
<string name="title_no_stream">An outdated app sent a file path instead of a file stream</string>
|
||||
<string name="title_no_contacts">Contact picker not available</string>
|
||||
<string name="title_no_internet">No internet connection</string>
|
||||
<string name="title_raw_saved">Raw message saved</string>
|
||||
<string name="title_attachment_saved">Attachment saved</string>
|
||||
<string name="title_attachments_saved">Attachments saved</string>
|
||||
<string name="title_attachment_unavailable">Some attachments are not downloaded and will not be added, continue?</string>
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<paths xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<paths>
|
||||
<files-path
|
||||
name="attachments"
|
||||
path="attachments" />
|
||||
<files-path
|
||||
name="raw"
|
||||
path="raw" />
|
||||
</paths>
|
||||
|
|
Loading…
Reference in New Issue