Compare commits

...

1475 Commits
1.504 ... 1.641

Author SHA1 Message Date
M66B
dcbf5730ef 1.641 release 2019-08-09 19:53:45 +02:00
M66B
3c6e8685eb Crowdin sync 2019-08-09 19:53:26 +02:00
M66B
b970144285 Small layout improvement 2019-08-09 19:42:07 +02:00
M66B
95154e2c09 Secure XDA link 2019-08-09 19:32:14 +02:00
M66B
47da32aa97 Close boundary on destroy 2019-08-09 19:29:17 +02:00
M66B
e09bd95cc6 Show relative snoozed time 2019-08-09 18:44:14 +02:00
M66B
e94f07bba3 XDA link 2019-08-09 18:26:47 +02:00
M66B
083fdb2b3b Honor name/email setting in normal view 2019-08-09 18:21:51 +02:00
M66B
601be58d56 Suppress some more common exceptions 2019-08-09 17:48:10 +02:00
M66B
a7a2322913 Check if swipe left/right folder exists 2019-08-09 17:34:37 +02:00
M66B
3ccdc904c4 Small layout improvement 2019-08-09 16:54:41 +02:00
M66B
0a583b7a01 Prevent crash 2019-08-09 16:50:04 +02:00
M66B
8073cf425e Prevent crash 2019-08-09 16:41:36 +02:00
M66B
2f6c0d37d6 Updated texts 2019-08-09 16:39:31 +02:00
M66B
257776f68a Added FAQ 2019-08-09 16:22:48 +02:00
M66B
626d301eec Updated texts 2019-08-09 15:39:47 +02:00
M66B
a4918e0e94 Keep attachment files for a while 2019-08-06 11:12:25 +02:00
M66B
c7597ec2a8 Improved raw message file cleanup 2019-08-06 11:02:09 +02:00
M66B
7a49a7dd89 Updated widget after activating pro features 2019-08-06 10:52:32 +02:00
M66B
9b4493d356 1.638 release 2019-08-06 10:04:52 +02:00
M66B
23a3c72a69 Refactoring 2019-08-06 10:02:37 +02:00
M66B
321daabd0e Improved logging 2019-08-06 09:59:49 +02:00
M66B
361e9f68b3 Small behavior improvement 2019-08-06 09:55:20 +02:00
M66B
eb405aaded Small improvement 2019-08-06 08:01:53 +02:00
M66B
ddae17a8d2 1.638 release 2019-08-06 07:50:08 +02:00
M66B
4e0bd7a925 Crowdin sync 2019-08-06 07:49:57 +02:00
M66B
1465fcfdd9 Updated setup instructions 2019-08-06 07:46:50 +02:00
M66B
796a80078b Small layout fix 2019-08-06 07:33:37 +02:00
M66B
43915d7a0a Crowdin sync 2019-08-05 20:00:02 +02:00
M66B
c8ca1a29aa 1.637 release 2019-08-05 19:49:19 +02:00
M66B
ac49c712b3 Crowdin sync 2019-08-05 19:48:23 +02:00
M66B
b39ef5ba48 Revert "Optimize new message notifications (2)"
This reverts commit c2f2afe0f5.
2019-08-05 19:44:42 +02:00
M66B
37097ecfbb Improved setup layout 2019-08-05 19:37:01 +02:00
M66B
520ec2d714 Updated setup instructions 2019-08-05 19:02:45 +02:00
M66B
34b738f0fa 1.636 release 2019-08-05 17:51:51 +02:00
M66B
6af420cf4e Crowdin sync 2019-08-05 17:51:41 +02:00
M66B
6800c38893 Added logging 2019-08-05 17:48:07 +02:00
M66B
c2f2afe0f5 Optimize new message notifications (2) 2019-08-05 16:49:17 +02:00
M66B
ac71c03f21 Optimize new message notifications (1) 2019-08-05 16:47:48 +02:00
M66B
e642c378f2 Check possible notification actions 2019-08-05 15:48:04 +02:00
M66B
d6b9ceb388 Fix "ascii" charset 2019-08-05 15:41:15 +02:00
M66B
a20524199b Crowdin sync 2019-08-05 14:47:52 +02:00
M66B
4afc7f659b Crowdin sync, localized setup instructions 2019-08-05 14:43:56 +02:00
M66B
228e6b4dc9 Small improvement 2019-08-05 10:52:29 +02:00
M66B
ef56231855 1.635 release 2019-08-05 09:51:06 +02:00
M66B
c3b06035b4 Crowdin sync 2019-08-05 09:47:53 +02:00
M66B
d8dbe8b7c7 Optimized delete 2019-08-05 09:47:14 +02:00
M66B
c2834f3352 Updated sent message text for sent orphans 2019-08-05 08:38:45 +02:00
M66B
68ce09107b Allow whole day conditions 2019-08-05 08:10:16 +02:00
M66B
9e3ea2818f 1.634 release 2019-08-05 07:43:13 +02:00
M66B
0a0dbac11c Consider bad command as unrecoverable 2019-08-05 07:41:33 +02:00
M66B
f3c070b76c Prevent crash 2019-08-05 07:28:01 +02:00
M66B
8892c58a79 Crowdin sync 2019-08-05 07:14:57 +02:00
M66B
5fb69d269d Updated FAQ 2019-08-04 22:23:55 +02:00
M66B
267a845ac9 Added option to snooze until end of schedule 2019-08-04 22:06:22 +02:00
M66B
41bb207724 Fixed initial rule focus 2019-08-04 20:53:51 +02:00
M66B
508fd8fbba Updated FAQ 2019-08-04 20:35:39 +02:00
M66B
060e1e3dfa Use fixed anchor for swipe to ask 2019-08-04 20:28:42 +02:00
M66B
8767aed899 Prevent crash 2019-08-04 20:00:20 +02:00
M66B
3185166d32 Added time condition to rule 2019-08-04 19:40:20 +02:00
M66B
c54a4bfc25 Updated FAQ 2019-08-04 16:11:22 +02:00
M66B
3645a8c684 1.633 release 2019-08-04 16:00:39 +02:00
M66B
2274409b62 Keep volatile record of notifications 2019-08-04 15:21:21 +02:00
M66B
bebc65e8de Small improvement 2019-08-04 14:33:38 +02:00
M66B
b16c07918c Updated FAQ 2019-08-04 14:30:07 +02:00
M66B
bfc79ff1c4 Updated FAQ 2019-08-04 14:28:06 +02:00
M66B
ad31094cc9 1.632 release 2019-08-04 13:44:57 +02:00
M66B
6522b711dd Crowdin sync 2019-08-04 13:44:22 +02:00
M66B
12f05b9edd Show all sender addresses 2019-08-04 13:43:37 +02:00
M66B
f60d15a02b Updated FAQ 2019-08-04 11:57:08 +02:00
M66B
72b58a5167 1.631 release 2019-08-04 11:13:39 +02:00
M66B
b68aa65826 Updated ignored exceptions 2019-08-04 11:08:36 +02:00
M66B
a2b484d4a5 Case insensitive re: replace 2019-08-04 10:48:03 +02:00
M66B
e2c46f978e 1.630 release 2019-08-04 09:57:28 +02:00
M66B
3b14f58d32 Set keep alive of web.de to 9 minutes 2019-08-04 09:55:29 +02:00
M66B
73e4f2c4f9 Prevent crash 2019-08-04 09:37:46 +02:00
M66B
eeda538546 Suppress exception 2019-08-04 09:36:31 +02:00
M66B
c473db6239 Refactoring 2019-08-04 09:22:53 +02:00
M66B
fcd8bc6700 Subscribe to created folder if showing subscribed folders only 2019-08-04 09:15:34 +02:00
M66B
6b34b64d10 Crowdin sync 2019-08-04 09:13:23 +02:00
M66B
2d76845fe0 Generalize setup help 2019-08-04 09:10:52 +02:00
M66B
dfed6cd8b0 Reschedule on turning scheduling on 2019-08-04 08:49:00 +02:00
M66B
08d4d9e851 Revert "Show when for summary notification"
This reverts commit 16942e2487.
2019-08-04 08:39:38 +02:00
M66B
3cb8ffd0cd Added external enable/disable account 2019-08-03 22:00:22 +02:00
M66B
1053ba3ac1 Check if operation before message update 2019-08-03 20:44:30 +02:00
M66B
a49f20db83 Fixed search in title 2019-08-03 18:32:44 +02:00
M66B
7c039c28cb 1.629 release 2019-08-03 17:56:45 +02:00
M66B
c887268d63 Crowdin sync 2019-08-03 17:56:41 +02:00
M66B
1ad850ae89 Updated FAQ 2019-08-03 17:50:06 +02:00
M66B
2eb51d2189 Cache purchases 2019-08-03 17:37:13 +02:00
M66B
0ae44923e9 Fixed losing selection on adding items 2019-08-03 17:08:01 +02:00
M66B
f59adf17f5 Improved watchdog 2019-08-03 16:07:00 +02:00
M66B
952d93bf17 Keep cleanup 2019-08-03 15:41:05 +02:00
M66B
07d0b30816 Update from with extra 2019-08-03 14:55:10 +02:00
M66B
ec5709bdc8 Improved re/fwd once, default enabled 2019-08-03 14:39:26 +02:00
M66B
a4d212fa6d Small fix 2019-08-03 14:26:36 +02:00
M66B
16942e2487 Show when for summary notification 2019-08-03 14:09:40 +02:00
M66B
6b7b8e50b8 Reduce logging 2019-08-03 13:56:16 +02:00
M66B
f20ebe1183 Use cache folder for photos 2019-08-03 13:53:49 +02:00
M66B
68d03fac18 Fixed manual poll in some circumstances 2019-08-03 13:39:41 +02:00
M66B
d2d20dd6ee Fixed "Prefix too short" 2019-08-03 13:31:18 +02:00
M66B
8b3edfe1fe 1.628 release 2019-08-01 12:36:40 +02:00
M66B
69fadd295d Restore alarms on manual cleanup 2019-08-01 11:59:42 +02:00
M66B
8a5b5ec577 Added FAQ 2019-08-01 11:55:57 +02:00
M66B
d8701a69a3 Increase maximum number of messages in unified inbox widget to 100 2019-08-01 10:07:31 +02:00
M66B
c72e752a43 Show executing cleanup 2019-08-01 10:07:10 +02:00
M66B
bcb165db59 Check if folder open before uid fetch 2019-08-01 10:04:15 +02:00
M66B
f74c4c460f Release names 2019-08-01 09:50:01 +02:00
M66B
3930428969 Retry on compute fails 2019-08-01 09:37:09 +02:00
M66B
316ed410a4 1.627 release 2019-08-01 09:25:11 +02:00
M66B
a0d37c9919 Cleanup 2019-08-01 09:24:21 +02:00
M66B
e386a405ae Serialize notification processing 2019-08-01 09:19:22 +02:00
M66B
6f47a2e22c Simplified boot/init 2019-08-01 09:08:28 +02:00
M66B
2fda4ebd35 Prevent crash 2019-08-01 08:38:15 +02:00
M66B
d89b6f86bc Limit number of records for widget 2019-08-01 08:33:56 +02:00
M66B
b5613e46a6 1.626 release 2019-07-31 18:08:52 +02:00
M66B
59b66ecd23 Crowdin sync 2019-07-31 18:08:10 +02:00
M66B
320e4e882a Updated FAQ 2019-07-31 18:08:00 +02:00
M66B
f18402ef14 Updated F-Droid meta data 2019-07-31 15:27:05 +02:00
M66B
4c7345abb4 Updated work manager 2019-07-31 15:14:48 +02:00
M66B
b0110efea2 1.625 release 2019-07-31 10:12:55 +02:00
M66B
a1f0418d03 Check for two state owner in simple task 2019-07-31 10:10:59 +02:00
M66B
59e3555bf7 Crowdin sync 2019-07-31 10:09:15 +02:00
M66B
9c3c999ae2 Added send breadcrumbs 2019-07-31 09:55:45 +02:00
M66B
f83585a753 Added folder/message to operation breadcrumb 2019-07-31 09:52:25 +02:00
M66B
d98298160d Added start/end date/time to calendar replies 2019-07-31 09:46:45 +02:00
M66B
0fa78c3e66 Use temporary calendar files 2019-07-31 09:40:54 +02:00
M66B
f0456e1a41 Use temporary file for resizing images 2019-07-31 09:32:10 +02:00
M66B
b9411b82d6 Fixed warning 2019-07-31 08:51:18 +02:00
M66B
fe2a6c5e25 Small layout improvements 2019-07-31 08:46:00 +02:00
M66B
85fa83faf8 Small fix 2019-07-31 08:45:53 +02:00
M66B
0606dac914 Start foreground on start command too
This shouldn't be necessary ...
2019-07-31 08:18:05 +02:00
M66B
5f71c31357 Updated text 2019-07-31 08:07:19 +02:00
M66B
496c1215e9 Fixed updating starred messages / unified inbox widget 2019-07-30 22:50:09 +02:00
M66B
62587b966d Small change 2019-07-30 22:23:53 +02:00
M66B
6934feda1a Delay delivering task data 2019-07-30 22:06:02 +02:00
M66B
aded278004 Revert "Adeed logging"
This reverts commit 52443b3a7a.
2019-07-30 20:57:22 +02:00
M66B
98593432c4 Use mail event executor 2019-07-30 20:53:37 +02:00
M66B
52443b3a7a Adeed logging 2019-07-30 20:38:51 +02:00
M66B
6957a15c0a Fixed showing drafts with unavailable attachments 2019-07-30 20:07:41 +02:00
M66B
d60ff43ed4 Prevent crash 2019-07-30 19:32:51 +02:00
M66B
279c1eeacf Refactored folder selector 2019-07-30 19:22:28 +02:00
M66B
9883a82cd7 Updated text 2019-07-30 19:07:58 +02:00
M66B
a9a5ca6077 Match provider by encryption method too 2019-07-30 18:58:29 +02:00
M66B
f2233cf9e9 Simplification 2019-07-30 18:53:18 +02:00
M66B
b1f485c575 Delay setting SMTP EHLO local host address 2019-07-30 18:52:45 +02:00
M66B
f8397ea8b7 Always fallback to connecting to IP addresses 2019-07-30 17:33:20 +02:00
M66B
f6a0e908e7 Small layout improvements 2019-07-30 17:27:48 +02:00
M66B
0ce5f6bf57 Small behavior improvement 2019-07-30 17:19:51 +02:00
M66B
3b51d9ce64 Make sure simple task delivers 2019-07-30 17:02:21 +02:00
M66B
0affd9e1f3 Fixed long attachment names 2019-07-30 14:46:02 +02:00
M66B
d9dd78e3a7 Unchecked attachment file delete 2019-07-30 14:45:40 +02:00
M66B
928119b4e9 Updated texts 2019-07-30 11:57:46 +02:00
M66B
849bfc4c56 1.624 release 2019-07-30 11:43:49 +02:00
M66B
133a1974d5 Crowdin sync 2019-07-30 11:43:25 +02:00
M66B
01a205e7fa Added unified inbox widget configuration to show unseen/flagged messages only 2019-07-30 11:38:12 +02:00
M66B
5bbb1b081a Refactoring 2019-07-30 11:31:32 +02:00
M66B
1f5e98ca16 Fixed duplicate legend page titles on rotate 2019-07-30 10:28:01 +02:00
M66B
d99f19f2b6 Prevent crash 2019-07-30 09:41:46 +02:00
M66B
f544f485a8 1.623 release 2019-07-30 09:13:37 +02:00
M66B
0dcbc24ca5 Crowdin sync 2019-07-30 09:13:14 +02:00
M66B
2c4c91f7cf Limit file name length 2019-07-30 09:03:54 +02:00
M66B
954c8db225 Start foreground on create
Sometimes there is a significant delay between on create and on start command
2019-07-30 08:46:38 +02:00
M66B
eaf3ea4a7c Small improvement 2019-07-30 08:00:30 +02:00
M66B
773b5660ab Updated FAQ 2019-07-30 07:58:09 +02:00
M66B
2e626fd5e9 Disable long press when selecting folders 2019-07-30 07:50:57 +02:00
M66B
5cb43e07ee Promote non selectable root folder containing non user childs to system folder 2019-07-29 23:21:49 +02:00
M66B
e6da47140d Cleanup 2019-07-29 22:44:28 +02:00
M66B
a8b0c4481d Remove some more tracking parameters 2019-07-29 22:28:33 +02:00
M66B
f1b904b75b Suppress folder icon for non selectable folders 2019-07-29 22:04:39 +02:00
M66B
1ed78da2a0 Support for renaming folders 2019-07-29 21:53:32 +02:00
M66B
c4ebff4c09 Updated Zoho bug report 2019-07-29 21:49:21 +02:00
M66B
d768ea3db9 Rebuild link only when query parameters haven been removed 2019-07-29 21:06:48 +02:00
M66B
22cb48be11 Updated FAQ 2019-07-29 20:37:13 +02:00
M66B
1b024566fd 1.622 release 2019-07-29 20:30:07 +02:00
M66B
8c47a34624 Crowdin sync 2019-07-29 20:29:47 +02:00
M66B
0d62c69f7c Updated FAQ 2019-07-29 20:29:29 +02:00
M66B
c92878d6b9 Prevent crash 2019-07-29 20:24:48 +02:00
M66B
dd9d50ff13 Prevent crash 2019-07-29 20:17:57 +02:00
M66B
4363218b7b Keep current primary account on import 2019-07-29 20:10:30 +02:00
M66B
99412f294f Fixed mapping folders on importing rules 2019-07-29 20:05:23 +02:00
M66B
af028c3daa Updated description 2019-07-29 19:20:54 +02:00
M66B
f1acfd1ac8 Added FAQ 2019-07-29 19:12:49 +02:00
M66B
b7d2406cad Fallback to connecting to IP addresses when insecure is enabled 2019-07-29 18:23:25 +02:00
M66B
5a38cf523a Select identity profile on host and port 2019-07-29 18:22:39 +02:00
M66B
2d80959e37 Allow editing account profile 2019-07-29 18:15:21 +02:00
M66B
2956b39cfe Updated F-Droid meta data 2019-07-29 17:08:42 +02:00
M66B
bfac316768 Refactoring 2019-07-29 16:44:11 +02:00
M66B
48a7a714ad Refactoring 2019-07-29 16:42:17 +02:00
M66B
f6f43ef575 Retry to connect using interface addresses 2019-07-29 16:23:25 +02:00
M66B
3687a632f6 Feedback on export/import 2019-07-29 13:53:10 +02:00
M66B
381e0b2052 1.621 release 2019-07-29 13:40:06 +02:00
M66B
767b8ea00e Prevent crash 2019-07-29 12:42:01 +02:00
M66B
53cdce5fa9 1.620 release 2019-07-29 12:21:21 +02:00
M66B
0688d3ec63 Crowdin sync 2019-07-29 12:20:48 +02:00
M66B
deab3432b5 Sanitize read/write errors 2019-07-29 12:18:34 +02:00
M66B
78e9ed5dd9 Show waiting for suitable connection in synchronizing notification 2019-07-29 12:02:25 +02:00
M66B
ed35dddceb Refactoring 2019-07-29 11:37:50 +02:00
M66B
a18767e9e8 Updated description 2019-07-29 09:51:26 +02:00
M66B
9656401794 Added FAQ 2019-07-29 08:59:24 +02:00
M66B
531fcfeb2f 1.619 release 2019-07-29 07:55:22 +02:00
M66B
50697e66f6 Crowdin sync 2019-07-29 07:53:02 +02:00
M66B
7db054342d Consider operations with file not found as unrecoverable 2019-07-29 07:47:11 +02:00
M66B
4dec43ae27 Updated FAQ 2019-07-29 07:38:31 +02:00
M66B
45a9f8cc5f Faster retry on database compute exception 2019-07-29 07:28:23 +02:00
M66B
a947db12a9 Added link to F-Droid metadata documentation 2019-07-29 07:24:16 +02:00
M66B
c15302c453 Added F-Droid screenshots 2019-07-29 07:21:48 +02:00
M66B
d488d8a8f5 Added F-Droid metadata images 2019-07-29 07:18:47 +02:00
M66B
5d9c57db3c Added hint about color precedence to account 2019-07-29 07:11:10 +02:00
M66B
c60953a9e0 Updated text 2019-07-29 07:10:29 +02:00
M66B
ba0d63e5d0 Retry connection exceptions on IPv4 only 2019-07-29 07:09:45 +02:00
M66B
c036c686a6 Added hint about color precedence 2019-07-28 22:07:31 +02:00
M66B
ae5cbb3ea5 Added service holder 2019-07-28 21:45:29 +02:00
M66B
2c63a12e0a Added F-Droid meta data 2019-07-28 20:46:20 +02:00
M66B
e263ee43f6 Fixed falling back to IPv4 2019-07-28 20:04:07 +02:00
M66B
ea0b69f46b 1.618 release 2019-07-28 18:14:30 +02:00
M66B
118bf99d34 Crowdin sync 2019-07-28 18:14:13 +02:00
M66B
410170d6d6 Retry on compute exception 2019-07-28 18:13:13 +02:00
M66B
3e88be112d Made encryption a free feature 2019-07-28 18:05:04 +02:00
M66B
c3d41e6a9a 1.617 release 2019-07-28 17:37:09 +02:00
M66B
a317e804c1 Crowdin sync 2019-07-28 17:35:52 +02:00
M66B
304794c677 Changed text 2019-07-28 17:35:46 +02:00
M66B
8a8ca574cd Bring back stop delay 2019-07-28 17:05:43 +02:00
M66B
9be0e5c788 Snackbar when data saver is enabled 2019-07-28 16:50:43 +02:00
M66B
c55cafa5d0 Made unified inbox widget a pro feature 2019-07-28 16:26:09 +02:00
M66B
9f1f446210 Prevent ROOM tracker from looping 2019-07-28 15:52:46 +02:00
M66B
abbd89eece 1.616 release 2019-07-28 14:31:13 +02:00
M66B
55a4fe2549 Crowdin sync 2019-07-28 14:28:44 +02:00
M66B
b01c75679b Added spinners to quick setup 2019-07-28 14:11:42 +02:00
M66B
d6ba051e85 Defer cleaning up files for emergency situations 2019-07-28 14:03:03 +02:00
M66B
45379870b1 Salvage draft in case of errors
This is know to happen due to "Exception while computing database live data"
2019-07-28 14:02:01 +02:00
M66B
7b3d0a106b Fixed proguard warnings 2019-07-28 13:19:07 +02:00
M66B
1beea3e88d Fixed inline ROOM release build 2019-07-28 13:11:19 +02:00
M66B
1fcfee1d3f Revert "Removed ROOM inline compilation"
This reverts commit 519e617dc7.
2019-07-28 12:44:45 +02:00
M66B
ac11efb0a0 Revert "Enable ROOM multi instance invalidation"
This reverts commit 95a5b10e6c.
2019-07-28 12:43:56 +02:00
M66B
027e7d8698 Disable select all when searching
Searching is incremental, so select all is relative and therefore confusing
2019-07-28 11:44:07 +02:00
M66B
04dbed7e8d Show number of messages selected 2019-07-28 10:14:12 +02:00
M66B
55f5faf794 Load IDs from model 2019-07-28 10:09:00 +02:00
M66B
98a99417ad Updated description
Closes #159
2019-07-28 09:25:29 +02:00
M66B
657ce04251 Updated texts 2019-07-28 07:50:20 +02:00
M66B
abfa22c348 Small layout improvement 2019-07-27 21:51:38 +02:00
M66B
661f86fe67 1.615 release 2019-07-27 21:16:54 +02:00
M66B
39293c4143 Crowdin sync 2019-07-27 21:16:33 +02:00
M66B
b9253671a1 Refactoring 2019-07-27 21:10:55 +02:00
M66B
834e84273b Added select all for search results 2019-07-27 20:45:49 +02:00
M66B
03f7f4271b Folder closed is not unexpected when browsing messages 2019-07-27 19:14:18 +02:00
M66B
95a5b10e6c Enable ROOM multi instance invalidation 2019-07-27 18:55:32 +02:00
M66B
519e617dc7 Removed ROOM inline compilation 2019-07-27 18:48:47 +02:00
M66B
86c0638fa0 Small behavior improvement 2019-07-27 18:45:36 +02:00
M66B
17aa81ccaf Updated FAQ 2019-07-27 18:18:56 +02:00
M66B
3e58d22dc6 Automatically retry on IPv4 2019-07-27 17:59:46 +02:00
M66B
f661a7c09b Removed prefer IPv4 settings, refactoring 2019-07-27 17:07:22 +02:00
M66B
4a72f7ff90 Scan all addresses 2019-07-27 16:39:45 +02:00
M66B
d7d00556b9 Updated Zoho conversation 2019-07-27 14:37:14 +02:00
M66B
f7ed05b40c Moved patches to separate folder 2019-07-27 13:54:27 +02:00
M66B
41dfa81fb1 Prevent crash due to "Exception while computing database" 2019-07-27 13:53:30 +02:00
M66B
9ab39ea3ea Compile ROOM inline 2019-07-27 13:48:07 +02:00
M66B
36bfa673e6 Always download sent message text 2019-07-27 11:46:15 +02:00
M66B
b2b48be05c Cleanup 2019-07-27 11:21:06 +02:00
M66B
1d3cd1d44f Sync instead of store sent 2019-07-27 11:11:24 +02:00
M66B
d02e246189 Force resync 2019-07-27 10:55:27 +02:00
M66B
5f5d219b71 Updated FAQ 2019-07-27 09:13:57 +02:00
M66B
8fc3813961 Append signature/referenced message only when sending 2019-07-27 08:47:33 +02:00
M66B
78c83b1f35 Refactoring 2019-07-27 08:13:11 +02:00
M66B
0191240a27 Deliver/pop on started instead of resumed
https://developer.android.com/guide/components/activities/activity-lifecycle#onstart
2019-07-27 08:04:47 +02:00
M66B
9d234aa9a4 Convert HTML width/height digits only 2019-07-27 07:32:26 +02:00
M66B
44a8381290 Skip synchronizing erroneously return old messages 2019-07-27 07:24:59 +02:00
M66B
df706f36ec Updated Zoho bug report with response 2019-07-27 06:53:41 +02:00
M66B
13882be6d0 Parse img width/height separately 2019-07-26 21:33:32 +02:00
M66B
c102711570 Fixed auto previous animation 2019-07-26 21:30:49 +02:00
M66B
0b53989826 Recognize img width/height auto 2019-07-26 21:21:12 +02:00
M66B
afbfd20b9b 1.614 release 2019-07-26 21:06:57 +02:00
M66B
40b8086ba6 Crowdin sync 2019-07-26 21:05:45 +02:00
M66B
3e80a86726 Use html image width/height 2019-07-26 20:45:53 +02:00
M66B
828d86e86f Fixed inline image view 2019-07-26 19:17:24 +02:00
M66B
2b8ec3d342 Added setting to prefer IPv4 addresses 2019-07-26 17:57:40 +02:00
M66B
7618845a39 Cleanup 2019-07-26 17:48:36 +02:00
M66B
0421ecb5c7 Simplification 2019-07-26 17:38:28 +02:00
M66B
c8ed3a4a33 Display conversations in unified inbox widget 2019-07-26 16:20:23 +02:00
M66B
55627735bd Revert "Show in action bar when composing plain text message"
This reverts commit 04f2c3f367.
2019-07-26 15:48:04 +02:00
M66B
805b5601fd Fixed setting colored flags when multiple selecting 2019-07-26 15:42:44 +02:00
M66B
04f2c3f367 Show in action bar when composing plain text message 2019-07-26 15:39:39 +02:00
M66B
7fd52db273 Updated FAQ 2019-07-26 14:32:39 +02:00
M66B
c6a56f8040 1.613 release 2019-07-26 14:05:11 +02:00
M66B
39e4eebdb8 Crowdin sync 2019-07-26 14:04:28 +02:00
M66B
490cb6cc80 Added service breadcrumbs 2019-07-26 14:02:31 +02:00
M66B
4c433674e3 1.612 release 2019-07-26 12:39:11 +02:00
M66B
a5dc0a3081 Crowdin sync 2019-07-26 12:38:39 +02:00
M66B
1e30425bc3 Added account name to unified widget 2019-07-26 12:32:35 +02:00
M66B
f5ca9277c3 Added option to go to previous conversation on closing conversation 2019-07-26 10:23:34 +02:00
M66B
4d3f19c5b3 Always run VACUUM on boot/update 2019-07-26 09:07:41 +02:00
M66B
de963c56b7 Removed not nessary input/output buffering 2019-07-26 08:57:11 +02:00
M66B
c4e29a9539 Improved auto config 2019-07-26 08:09:18 +02:00
M66B
6d7e034ad4 Updated FAQ 2019-07-26 07:22:16 +02:00
M66B
3e080b9706 1.611 release 2019-07-25 20:14:26 +02:00
M66B
4d2755e17c Increased read/write timeout to 60 seconds 2019-07-25 20:13:45 +02:00
M66B
d7377f0f0f Simplified stop sequence 2019-07-25 20:11:54 +02:00
M66B
8ba3d39cff Small layout improvement 2019-07-25 18:56:42 +02:00
M66B
680149b63d Simplification 2019-07-25 17:32:42 +02:00
M66B
748d959a7d 1.610 release 2019-07-25 17:23:49 +02:00
M66B
92332a7c78 Fixed/simplified unified widget update 2019-07-25 17:22:00 +02:00
M66B
58943f1291 Small layout improvement 2019-07-25 16:29:54 +02:00
M66B
26f5e0d764 Optimized unified widget update 2019-07-25 16:23:50 +02:00
M66B
10cd919172 1.609 release 2019-07-25 15:59:20 +02:00
M66B
1c13bc29a9 Crowdin sync 2019-07-25 15:56:21 +02:00
M66B
d43f7a170f Updated widget title 2019-07-25 15:49:29 +02:00
M66B
120a837c32 Use relative time for unified widget 2019-07-25 15:47:54 +02:00
M66B
ceee4166de Refactoring 2019-07-25 15:38:14 +02:00
M66B
e046e0f7cf Added unified inbox widget 2019-07-25 15:35:11 +02:00
M66B
e0d35792b2 Mark conversation (un)seen on double tap 2019-07-25 15:25:06 +02:00
M66B
ccb6c259a4 Use executor to update widget 2019-07-25 12:54:30 +02:00
M66B
906d98a14f Added logging 2019-07-25 10:43:07 +02:00
M66B
ac4e904d29 Hide message on moving to junk 2019-07-25 10:14:32 +02:00
M66B
9cbda608dc Cancel snooze on moving to archive/trash 2019-07-25 10:13:05 +02:00
M66B
efb2107fd1 Show snoozed count for outbox 2019-07-25 09:44:50 +02:00
M66B
687fc722e5 Stop send service when no more operations 2019-07-25 09:23:15 +02:00
M66B
7fcf1da77a 1.608 release 2019-07-25 08:36:40 +02:00
M66B
7c41153bad Crowdin sync 2019-07-25 08:36:26 +02:00
M66B
db0b6fcd45 Optimization 2019-07-25 08:36:07 +02:00
M66B
7969833e2c Added hint 2019-07-25 08:27:44 +02:00
M66B
59e4c797ea Small layout improvement 2019-07-25 08:27:31 +02:00
M66B
e0a446a8a9 Check threads for multiple selection actions 2019-07-25 08:17:54 +02:00
M66B
f2c454a744 Convert to lower case before requesting encryption 2019-07-25 07:41:37 +02:00
M66B
0042c5e691 1.607 2019-07-25 07:24:07 +02:00
M66B
cf785de592 Crowdin sync 2019-07-25 07:22:19 +02:00
M66B
40cba0f42b Consider not sticky folders as read only 2019-07-25 07:17:27 +02:00
M66B
d8b398cf27 Fix 2019-07-24 22:04:58 +02:00
M66B
7e94319498 Small improvement 2019-07-24 22:02:17 +02:00
M66B
44416cfa20 Refactoring 2019-07-24 22:01:55 +02:00
M66B
861c6da4b9 Store and check uid validity 2019-07-24 22:01:41 +02:00
M66B
ab50d9db77 Refactoring 2019-07-24 21:15:10 +02:00
M66B
968c6800d3 1.606 release 2019-07-24 20:23:15 +02:00
M66B
7fcae32ab2 Crowdin sync 2019-07-24 20:22:46 +02:00
M66B
cbd1f119dd Small layout improvement 2019-07-24 20:20:12 +02:00
M66B
e65c5c44ec Prefer to show non drafts when searching globally 2019-07-24 20:17:55 +02:00
M66B
24cdaf213a Simplify paged messages queries 2019-07-24 19:56:00 +02:00
M66B
3535908520 Small layout fix 2019-07-24 19:32:44 +02:00
M66B
c40beae0fa Fixed simple task count 2019-07-24 19:07:07 +02:00
M66B
a0c2e41adb Added special searches 2019-07-24 18:46:39 +02:00
M66B
0be109a816 Added workaround for Zoho IMAP server bug 2019-07-24 17:50:45 +02:00
M66B
6040b420b8 Added profiles for Zoho 2019-07-24 16:49:11 +02:00
M66B
f77b257312 Skip synchronizing starred messages by default 2019-07-24 16:33:00 +02:00
M66B
3be40ef641 Store attachment count for better performance 2019-07-24 14:49:55 +02:00
M66B
752aee0a3b Small improvement 2019-07-24 14:43:45 +02:00
M66B
989bdb1cb9 Updated description 2019-07-24 13:20:34 +02:00
M66B
1ac7f26dae Search locally for keywords too 2019-07-24 12:00:47 +02:00
M66B
13896fc72a Small improvement 2019-07-24 10:43:32 +02:00
M66B
b306bd1eff Updated FAQ 2019-07-24 10:29:58 +02:00
M66B
e02c891db9 1.605 release 2019-07-24 10:08:32 +02:00
M66B
b6047488d0 Always allow deleting operations 2019-07-24 10:06:56 +02:00
M66B
1f2074a26f Crowdin sync 2019-07-24 09:24:49 +02:00
M66B
00400d7ef6 Read contacts in background 2019-07-24 09:07:57 +02:00
M66B
6104ecfadb Small improvements 2019-07-24 08:42:37 +02:00
M66B
f8665c8526 Updated FAQ 2019-07-24 08:33:18 +02:00
M66B
9d01825146 Update last authentication on user interaction 2019-07-24 07:38:34 +02:00
M66B
cab37caac9 Refactoring 2019-07-23 20:27:05 +02:00
M66B
ca6827c266 Small fix 2019-07-23 19:50:53 +02:00
M66B
0a212e93af Small layout improvements 2019-07-23 19:34:38 +02:00
M66B
8210434891 Updated attribution 2019-07-23 19:01:22 +02:00
M66B
d2f673dafc Small improvement 2019-07-23 18:54:50 +02:00
M66B
3704960fd4 Erase account warning on disabling sync 2019-07-23 17:57:37 +02:00
M66B
e5c14ff25d 1.604 release 2019-07-23 17:34:09 +02:00
M66B
a633bd8689 Crowdin sync 2019-07-23 17:33:23 +02:00
M66B
c197562c3d Fixed quick setup 2019-07-23 17:25:04 +02:00
M66B
ccfffe71e0 Small layout improvement 2019-07-23 17:05:30 +02:00
M66B
54d54f5d9f Show attribution inline 2019-07-23 16:59:12 +02:00
M66B
83c5ec091b Small layout improvement 2019-07-23 16:28:48 +02:00
M66B
b5f0729de3 Added mini port scanner 2019-07-23 16:08:28 +02:00
M66B
102be15f79 Typo 2019-07-23 16:00:23 +02:00
M66B
f7361fc495 Updated Bugsnag plugin 2019-07-23 15:15:25 +02:00
M66B
0d55e5880b Refactoring 2019-07-23 15:15:18 +02:00
M66B
196354cf8c Added attribution menu 2019-07-23 14:30:57 +02:00
M66B
cbb1cd48c1 Small layout improvements 2019-07-23 14:20:07 +02:00
M66B
6df557f62b Integrated setup help and privacy information 2019-07-23 14:11:22 +02:00
M66B
54b7c6a3a8 Changed notification categories 2019-07-23 11:38:36 +02:00
M66B
fd5f7b3418 Drop requirement for multiple unified system folders 2019-07-23 10:28:41 +02:00
M66B
4808f118bd Distinguish send and sent 2019-07-23 09:21:46 +02:00
M66B
bf608284b4 Cleanup 2019-07-23 09:13:22 +02:00
M66B
a24d612704 Leak canary 2019-07-23 08:57:32 +02:00
M66B
5ce960ea61 1.603 release 2019-07-23 08:28:56 +02:00
M66B
edfaa010c0 Limit VACUUM to once a day 2019-07-23 08:28:32 +02:00
M66B
4c4f4cb979 1.602 release 2019-07-23 08:12:21 +02:00
M66B
bb33abc52a Crowdin sync 2019-07-23 08:05:02 +02:00
M66B
f22c8ce801 Prevent crash 2019-07-23 08:03:11 +02:00
M66B
c35e4a8b31 Added comments 2019-07-23 07:51:39 +02:00
M66B
8489ca28d5 Updated FAQ 2019-07-23 07:31:17 +02:00
M66B
145dc94e04 Updated bugsnag 2019-07-22 19:06:09 +02:00
M66B
a70641c9cd Added flags to issue reports 2019-07-22 17:19:18 +02:00
M66B
e139fe0b5c Revert "Revert "Added profile for freenet.de""
This reverts commit 2839760d37.
2019-07-22 17:09:00 +02:00
M66B
c97ea7556f Run VACUUM on boot/update 2019-07-22 16:28:48 +02:00
M66B
0fe0b4857e 1.601 release 2019-07-22 15:27:57 +02:00
M66B
c748836da7 Crowdin sync 2019-07-22 15:27:38 +02:00
M66B
2839760d37 Revert "Added profile for freenet.de"
This reverts commit bebf1e0e04.
2019-07-22 15:23:25 +02:00
M66B
bebf1e0e04 Added profile for freenet.de 2019-07-22 15:17:58 +02:00
M66B
958982481d Allow refreshing unified system folders 2019-07-22 14:42:03 +02:00
M66B
ed92a999f0 Show number of unseen messages in unified system folders 2019-07-22 14:34:06 +02:00
M66B
bd3cc5018d Suppress folder type icon in unified system folders 2019-07-22 14:13:09 +02:00
M66B
0b9d717eb4 Updated FAQ 2019-07-22 14:02:05 +02:00
M66B
a1bead9301 Unified system folder fixes/improvements 2019-07-22 13:44:25 +02:00
M66B
816ab11ef9 Fix updating nav account/folder state 2019-07-22 13:25:03 +02:00
M66B
7f9b4fc2b5 Skip sync on oneshot operation 2019-07-22 13:18:52 +02:00
M66B
24ac579b1c Updated FAQ 2019-07-22 12:39:47 +02:00
M66B
45ddabc9f0 Added option to always show images 2019-07-22 12:39:22 +02:00
M66B
b316486119 Refactoring 2019-07-22 12:04:13 +02:00
M66B
226964e9fc Added option to disabled generated icons 2019-07-22 10:40:03 +02:00
M66B
bc6555210f Show folder last sync in nav item 2019-07-22 10:09:50 +02:00
M66B
049f3c342d Refactoring 2019-07-22 09:52:08 +02:00
M66B
704b5c30b0 Show last sync time in account nav item 2019-07-22 09:48:48 +02:00
M66B
39c600822f Refactoring 2019-07-22 09:30:58 +02:00
M66B
4ce99a70d0 Extended debug info 2019-07-22 09:18:03 +02:00
M66B
932d0f6570 Updated FAQ 2019-07-21 22:14:17 +02:00
M66B
0635dfdc5d Cleared release name 2019-07-21 21:32:29 +02:00
M66B
a492881e4a 1.600 release 2019-07-21 21:27:30 +02:00
M66B
4fb5c528ca Show check on debug only 2019-07-21 21:19:21 +02:00
M66B
ba19d816c5 Small improvement 2019-07-21 20:09:55 +02:00
M66B
c805749114 Updated Sqlite support library 2019-07-21 20:05:22 +02:00
M66B
fd7c286752 Restore notifications on boot/update 2019-07-21 19:29:46 +02:00
M66B
65c3d4f3ad 1.599 release 2019-07-21 17:59:21 +02:00
M66B
3730027913 Added FAQ 2019-07-21 17:58:55 +02:00
M66B
564ee54baa Crowdin sync 2019-07-21 17:41:29 +02:00
M66B
5d3fb91deb Added purchase check 2019-07-21 17:38:24 +02:00
M66B
cb6a239716 Updated work manager 2019-07-21 14:47:52 +02:00
M66B
0d65ab0d25 Updated FAQ 2019-07-21 14:40:20 +02:00
M66B
938446435b 1.598 release 2019-07-21 14:24:30 +02:00
M66B
7d8cfeb9ab Crowdin sync 2019-07-21 14:24:11 +02:00
M66B
c3e5f8ba0f Show quick setup help button more prominently 2019-07-21 13:40:48 +02:00
M66B
97e9bed623 Crowdin sync 2019-07-21 13:33:22 +02:00
M66B
6ca9d9d3b3 Permanently delete messages instead of conversations 2019-07-21 13:28:47 +02:00
M66B
4977f66c1d Updated setup instructions 2019-07-21 13:18:18 +02:00
M66B
38f827c023 Small improvement 2019-07-21 12:43:36 +02:00
M66B
b9f71a875a Refactoring 2019-07-21 12:20:27 +02:00
M66B
145b6d7efa Copy attachments on reply/forward only 2019-07-21 12:17:11 +02:00
M66B
fe0714d10e Refactoring 2019-07-21 12:11:46 +02:00
M66B
bad6f79ff7 Skip adding attachments on receipts 2019-07-21 11:56:59 +02:00
M66B
3d5d994852 Added edit as new 2019-07-21 11:54:36 +02:00
M66B
adad5602f0 Used untrusted group instead relation 2019-07-21 10:31:34 +02:00
M66B
29246a0f2b Small improvement 2019-07-21 09:50:35 +02:00
M66B
3037791282 Drop requirement for synchronizing unified folder 2019-07-21 09:50:17 +02:00
M66B
8241d19d09 Updated setup instructions 2019-07-20 23:11:33 +02:00
M66B
39087ae79c Removed workaround 2019-07-20 22:59:48 +02:00
M66B
cade6c9dc3 1.597 release 2019-07-20 22:35:32 +02:00
M66B
5e54dae5fa Crowdin sync 2019-07-20 22:35:01 +02:00
M66B
76399d131e Small fix 2019-07-20 22:25:59 +02:00
M66B
9c13eee4b5 Collapsible unified system folders 2019-07-20 22:25:50 +02:00
M66B
12c47070fd Revert "Added option to enable unified system folders"
This reverts commit ded0259acc.
2019-07-20 21:47:07 +02:00
M66B
fa6e5e6d3f Fixed/simplified unified message selection 2019-07-20 21:44:56 +02:00
M66B
ded0259acc Added option to enable unified system folders 2019-07-19 09:21:54 +02:00
M66B
bc73d2f83a 1.596 release 2019-07-19 08:48:33 +02:00
M66B
5b5ec254fb Crowdin sync 2019-07-19 08:48:27 +02:00
M66B
174bad50f4 Unified system folders 2019-07-19 08:48:14 +02:00
M66B
1daa8052c1 1.595 release 2019-07-19 08:38:17 +02:00
M66B
a942cf1b79 Added Lithuanian 2019-07-19 08:37:49 +02:00
M66B
08573a1a6f Updated legend 2019-07-19 08:37:28 +02:00
M66B
7677c89af8 Crowdin sync 2019-07-19 08:32:51 +02:00
M66B
272e691478 Added unified folder types 2019-07-19 08:27:44 +02:00
M66B
1f36e69e04 Simplified cleanup 2019-07-19 07:14:47 +02:00
M66B
7a97739689 Fixed resetting manual sync all 2019-07-19 07:11:05 +02:00
M66B
fa432a76d7 Refactoring 2019-07-19 07:08:30 +02:00
M66B
e757446d5e Updated colors 2019-07-19 06:47:59 +02:00
M66B
7229ca3f83 Small layout improvement 2019-07-18 20:23:17 +02:00
M66B
b7a562994d Prevent crash 2019-07-18 20:13:33 +02:00
M66B
9bb8437096 Improved can auth check 2019-07-18 19:40:47 +02:00
M66B
ad7b497405 1.594 release 2019-07-18 19:01:15 +02:00
M66B
952f6132df Updated AndroidX libraries 2019-07-18 18:57:43 +02:00
M66B
00ef5e056a Document sign key handling 2019-07-18 18:06:33 +02:00
M66B
99abbb9a1d Updated color 2019-07-18 17:45:00 +02:00
M66B
d4f6653047 Automatically remember sign key 2019-07-18 17:38:51 +02:00
M66B
463c59650c Fold mime 2019-07-18 16:42:20 +02:00
M66B
e65317bbe7 Improved mime word decoding 2019-07-18 14:47:08 +02:00
M66B
ec59befb82 Fixed swipe refresh on fast scroll 2019-07-18 14:04:14 +02:00
M66B
456756efd3 1.593 release 2019-07-18 13:16:06 +02:00
M66B
9d53cc6c75 Crowdin sync 2019-07-18 13:15:39 +02:00
M66B
c474d399e8 Make sure swiped item will be moved back 2019-07-18 13:08:40 +02:00
M66B
3da3d14a92 Set query and fetch executors to low priority again 2019-07-18 13:01:01 +02:00
M66B
c14af37a71 Updated FAQ 2019-07-18 12:02:06 +02:00
M66B
a11963938f Make sure that pre moved messages are visible 2019-07-17 15:19:09 +02:00
M66B
ebfa20b952 Allow selecting inbox as swipe target 2019-07-17 14:43:17 +02:00
M66B
748f532a94 Refactoring 2019-07-17 14:28:05 +02:00
M66B
f0922e9152 Added swipe to seen 2019-07-17 14:23:23 +02:00
M66B
87b9dbd834 Take inline images into account for having images 2019-07-17 13:06:47 +02:00
M66B
3a96e3d13e Added relinker, fixed warning 2019-07-17 12:28:57 +02:00
M66B
450ccde55e Small improvement 2019-07-17 11:29:44 +02:00
M66B
83ca643da8 1.592 release 2019-07-17 11:16:42 +02:00
M66B
0e3d79db1d Crowdin sync 2019-07-17 11:15:45 +02:00
M66B
67b8756b78 Added hint 2019-07-17 11:07:05 +02:00
M66B
96c3ab83a0 Send inline content as related 2019-07-17 09:38:40 +02:00
M66B
2c531aa76d Updated FAQ 2019-07-17 08:33:49 +02:00
M66B
a662203213 Cleanup 2019-07-16 21:43:02 +02:00
M66B
e5b24d3cc6 1.591 release 2019-07-16 19:43:07 +02:00
M66B
21cf9fd9a3 Crowdin sync 2019-07-16 19:43:00 +02:00
M66B
1b31abc4fc Small improvement 2019-07-16 19:28:42 +02:00
M66B
69e375b542 Small fix 2019-07-16 18:47:35 +02:00
M66B
a8215cd7bd Default encrypt on replying to encrypted message 2019-07-16 18:46:25 +02:00
M66B
369a476513 Default Autocrypt prefer mutual mode 2019-07-16 17:58:43 +02:00
M66B
b8203fe0f0 Added option to check sender's MX records 2019-07-16 14:56:46 +02:00
M66B
4fb3f3c638 Added logging 2019-07-16 14:24:32 +02:00
M66B
368d344f0b Only once 2019-07-16 13:32:02 +02:00
M66B
3bfc29d4c9 Updated FAQ 2019-07-16 12:36:35 +02:00
M66B
62059347f6 Prevent crash 2019-07-16 12:04:32 +02:00
M66B
ceed8f0b03 Clear signature html on empty text 2019-07-16 11:59:14 +02:00
M66B
684cb896c0 Updated FAQ 2019-07-16 11:12:10 +02:00
M66B
2e4d0c5917 Use DNS watch as default DNS server 2019-07-16 11:02:51 +02:00
M66B
8de929ff54 Small improvements 2019-07-16 10:54:07 +02:00
M66B
af391e3308 Updated billing library 2019-07-16 10:23:41 +02:00
M66B
057cf01a1f Updated FAQ 2019-07-16 09:42:43 +02:00
M66B
0e159b9f1c Added more polling intervals 2019-07-16 09:28:44 +02:00
M66B
9c3d886c59 1.590 Brontogladiopter 2019-07-16 08:27:15 +02:00
M66B
5817a280f6 Count executed rules 2019-07-16 08:25:30 +02:00
M66B
6bd3817fc2 Crowdin sync 2019-07-16 08:25:13 +02:00
M66B
9c17a9758d Added option to disable double back 2019-07-15 22:15:38 +02:00
M66B
075d3dc6e9 Fixed some more time formatting 2019-07-15 21:28:55 +02:00
M66B
ea14fa769a Fixed notification grouping/sound for older Android versions 2019-07-15 20:39:24 +02:00
M66B
f044559475 Updated FAQ 2019-07-15 20:04:26 +02:00
M66B
fe41220b42 Quick setup alt fix 2019-07-15 19:05:55 +02:00
M66B
667e25b7dd Prevent crash 2019-07-15 18:45:17 +02:00
M66B
ae029a1b11 Fixed alt drafts quick setup 2019-07-15 17:41:11 +02:00
M66B
8267434832 Added logging 2019-07-15 17:19:25 +02:00
M66B
dd60779b29 Updated FAQ 2019-07-15 17:03:42 +02:00
M66B
6499a14c7b Get DNS server for older Android versions 2019-07-15 16:53:47 +02:00
M66B
4a5235b4bf Use alternate drafts for quick setup 2019-07-15 16:23:51 +02:00
M66B
7c437d2a2d Get network DNS server 2019-07-15 16:15:37 +02:00
M66B
c142c44055 Always prefer built-in profiles 2019-07-15 15:11:01 +02:00
Marcel Bokhorst
4b585c3567 Merge pull request #158 from Primokorn/patch-1
Update providers.xml
2019-07-15 15:00:02 +02:00
Primokorn
9361e2131b Update providers.xml
Adding Orange.fr, the n°1 ISP provider.
2019-07-15 14:50:59 +02:00
M66B
269bd11d87 Prevent crash 2019-07-15 14:12:41 +02:00
M66B
930663ff76 Sync time of sent messages 2019-07-15 13:48:27 +02:00
M66B
40ad0bf068 Small layout improvement 2019-07-15 13:43:44 +02:00
M66B
31fc9f73a6 Added a profile for 24-mail 2019-07-15 13:39:44 +02:00
M66B
cf810659d6 1.589 Cathedral 2019-07-15 12:55:55 +02:00
M66B
0b10c17b0b Respect folder download setting on inline download 2019-07-15 12:54:53 +02:00
M66B
317adf4f02 Crowdin sync 2019-07-15 12:47:25 +02:00
M66B
052ea3964b Fix host name URIs 2019-07-15 12:41:51 +02:00
M66B
041e293ac1 Fallback to sent time if no received time 2019-07-15 12:41:31 +02:00
M66B
d593c230ce Small improvement 2019-07-15 12:27:58 +02:00
M66B
d4f330e1af Revert "Less small messages"
This reverts commit ff738caa37.
2019-07-15 12:17:29 +02:00
M66B
c248dce45b Density aware images 2019-07-15 12:17:01 +02:00
M66B
c9b46d6ecb Added debug info 2019-07-15 09:18:03 +02:00
M66B
631fc07b8d Manual sync can still download messages 2019-07-15 08:33:54 +02:00
M66B
ff738caa37 Less small messages 2019-07-14 22:47:23 +02:00
M66B
7b07850a91 Moved subscription management to folder long press menu 2019-07-14 22:44:03 +02:00
M66B
6d69005b52 Enable report issue for third party builds 2019-07-14 22:11:49 +02:00
M66B
d2f25ad652 Download small messages inline 2019-07-14 21:55:50 +02:00
M66B
52c16a52ed Added option to lookup MX records on sending 2019-07-14 20:46:12 +02:00
M66B
2359276202 Better DNS lookup 2019-07-14 13:32:55 +02:00
M66B
c9d9061919 Changed title 2019-07-14 12:34:30 +02:00
M66B
ff4d6f307f Enabled images for known contacts by default 2019-07-14 12:33:00 +02:00
M66B
fb67b073dc Feedback on reset options 2019-07-14 12:32:32 +02:00
M66B
b66a34a785 Allow marking contacts as untrusted 2019-07-14 11:33:32 +02:00
M66B
f3fff6b6e1 Fixed link 2019-07-14 07:31:37 +02:00
M66B
426b3e553a 1.588 Allosaurus 2019-07-14 07:06:25 +02:00
M66B
42f48f4745 Crowdin sync 2019-07-14 07:06:04 +02:00
M66B
05705b7641 Use accent color for header names 2019-07-13 21:14:49 +02:00
M66B
68d204d5b3 Added color resources 2019-07-13 21:12:09 +02:00
M66B
8108de074e Added FAQ 2019-07-13 20:49:24 +02:00
M66B
a49c64d87c Added release name 2019-07-13 20:14:19 +02:00
M66B
f8badc4d06 Reset selection on action end 2019-07-13 19:30:37 +02:00
M66B
74925669c3 Revert "Added chips"
This reverts commit 2c80c25b8a.
2019-07-13 19:11:40 +02:00
M66B
2c80c25b8a Added chips 2019-07-13 19:11:24 +02:00
M66B
30abe9e607 Updated text 2019-07-13 18:47:37 +02:00
M66B
0eba14de0c Moved toasts above bottom action bar 2019-07-13 18:03:22 +02:00
M66B
7ad57f431a Added color title 2019-07-13 13:53:45 +02:00
M66B
091067073a Added FAQ 2019-07-13 13:38:28 +02:00
M66B
4d5484c936 Reset welcome 2019-07-13 13:29:14 +02:00
M66B
746d5ec921 Consistent color references 2019-07-13 13:25:12 +02:00
M66B
abf06559c7 Refactoring 2019-07-13 12:48:44 +02:00
M66B
7a1ccd4db0 Skip answered operation in send service 2019-07-13 12:47:22 +02:00
M66B
96be1c9bf6 Hide pro texts when applicable 2019-07-13 11:42:42 +02:00
M66B
27b89de1f5 Updated text 2019-07-13 11:23:21 +02:00
M66B
0a1aab9ee3 Default signature for new identities only 2019-07-13 10:28:29 +02:00
M66B
9f0e94107b Revert "Small layout improvement"
This reverts commit c55b94a8cf.
2019-07-13 10:26:47 +02:00
M66B
c55b94a8cf Small layout improvement 2019-07-13 10:24:33 +02:00
M66B
3c19cad2e3 1.587 release 2019-07-13 10:04:49 +02:00
M66B
c1a453bc90 Crowdin sync 2019-07-13 10:04:29 +02:00
M66B
0aa8b01976 Addede account/identity hints 2019-07-13 09:50:55 +02:00
M66B
5748d298fc Small layout improvement 2019-07-13 09:39:21 +02:00
M66B
4e32004ca9 Set new rule name to subject 2019-07-13 09:29:29 +02:00
M66B
79e40ade73 Small layout improvement 2019-07-13 09:09:37 +02:00
M66B
d488809a69 Custom toast 2019-07-13 09:09:30 +02:00
M66B
dcc43a1f1c Revert "Updated material"
This reverts commit 0b8655f9a1.
2019-07-13 08:34:12 +02:00
M66B
93ea791bad Some promotion 2019-07-12 21:20:06 +02:00
M66B
0b8655f9a1 Updated material 2019-07-12 20:08:29 +02:00
M66B
ec87b383e8 Check if biometrics sensor available 2019-07-12 19:48:48 +02:00
M66B
0998f408b0 Prevent crash 2019-07-12 18:50:18 +02:00
M66B
902d05c53b Updated links 2019-07-12 18:29:55 +02:00
M66B
59bd41281a Fixed search widget style 2019-07-12 17:50:50 +02:00
Marcel Bokhorst
4b5137e0d9 Merge pull request #157 from licaon-kter/patch-9
Update links in Readme
2019-07-12 17:28:12 +02:00
Licaon_Kter
5e8a0bb311 Update links in Readme 2019-07-12 15:26:32 +00:00
M66B
37cf5fe9c7 Updated text 2019-07-12 17:05:28 +02:00
M66B
77eb810766 Renamed GitHub 2019-07-12 16:57:56 +02:00
M66B
a37037be3e Restart on enabling/disabling biometrics authentication 2019-07-12 14:38:00 +02:00
M66B
081faac153 1.586 release 2019-07-12 14:20:01 +02:00
M66B
b3c925944d Prevent crash 2019-07-12 14:18:56 +02:00
M66B
fee604659f Crowdin sync 2019-07-12 14:14:11 +02:00
M66B
8e6d0adac3 Prevent crash 2019-07-12 14:10:31 +02:00
M66B
24f28b05b5 Added search suggestions 2019-07-12 14:05:53 +02:00
M66B
71706dc098 Hide hint when irrelevant 2019-07-12 11:21:16 +02:00
M66B
f9bdb3c8a2 Use default light 2019-07-12 11:20:29 +02:00
M66B
9138720150 Workaround notification sound before Android 8 2019-07-12 11:11:01 +02:00
M66B
a8d18b8bf7 Added comment 2019-07-12 10:05:30 +02:00
M66B
9e9c3ccff1 Updated work manager 2019-07-12 10:05:14 +02:00
M66B
d9d027195e Updated FAQ 2019-07-12 08:55:36 +02:00
M66B
d4d93f63f0 Prevent crash 2019-07-11 20:19:52 +02:00
M66B
f6d730405d Small fix 2019-07-11 20:16:58 +02:00
M66B
4ca77df6b9 Updated FAQ 2019-07-11 19:17:15 +02:00
M66B
21b66b165a Finish on screen off when using biometrics authentication 2019-07-11 17:54:52 +02:00
M66B
ba699002b0 Small layout improvement 2019-07-11 17:40:25 +02:00
M66B
f9729cdca4 Added setting for biometric inactivity period 2019-07-11 17:06:49 +02:00
M66B
be2f1a2aa7 Updated FAQ 2019-07-11 13:53:01 +02:00
M66B
226f729236 1.585 release 2019-07-11 12:11:50 +02:00
M66B
6926933094 Defer download on initialize 2019-07-11 12:03:43 +02:00
M66B
d28635d13a 1.584 release 2019-07-11 10:42:38 +02:00
M66B
91ec7305d2 Updated FAQ 2019-07-11 10:41:06 +02:00
M66B
599e2564a0 Crowdin sync 2019-07-11 10:37:46 +02:00
M66B
31923694be Simplified notifications when using biometric authentication 2019-07-11 10:33:51 +02:00
M66B
c3b7275a48 Prevent double notification sound when using biometrics 2019-07-11 10:14:51 +02:00
M66B
1e7949a982 1.583 release 2019-07-11 09:06:22 +02:00
M66B
d162aee196 Small layout fix 2019-07-11 09:06:14 +02:00
M66B
777a0bb897 Crowdin sync 2019-07-11 09:03:27 +02:00
M66B
d66db8e7c9 1.582 release 2019-07-11 08:53:46 +02:00
M66B
64b40af9ce Crowdin sync 2019-07-11 08:46:48 +02:00
M66B
c842392f67 Handle screen off in sync service 2019-07-11 08:42:20 +02:00
M66B
6f268c1b56 Refactoring 2019-07-11 08:13:49 +02:00
M66B
b35a63dff9 Updated FAQ 2019-07-11 08:11:29 +02:00
M66B
5e372c4afa Simplified screen off detection 2019-07-11 08:10:33 +02:00
M66B
8a63e3e269 Fixed main theme 2019-07-11 08:04:11 +02:00
M66B
2c7529ab1c Small improvements 2019-07-11 07:52:06 +02:00
M66B
c758723855 Exclude from recents when using biometric authentication 2019-07-11 07:50:42 +02:00
M66B
7d60483274 Updated text 2019-07-11 07:34:56 +02:00
M66B
2e697cfa4d Prevent crash 2019-07-11 07:34:02 +02:00
M66B
15a33ba71a Added FAQ 2019-07-10 20:28:40 +02:00
M66B
df584f637e 1.581 release 2019-07-10 19:59:15 +02:00
M66B
2ea7718c32 Crowdin sync 2019-07-10 19:58:54 +02:00
M66B
e17f313956 Authentication improvements/fixes 2019-07-10 19:58:02 +02:00
M66B
3bb6956bfe 1.580 release 2019-07-10 19:08:29 +02:00
M66B
25088e1771 Small layout improvement 2019-07-10 19:07:05 +02:00
M66B
cc639ab44f Crowdin sync 2019-07-10 19:06:12 +02:00
M66B
4baa05a11e Refactoring 2019-07-10 19:01:14 +02:00
M66B
cb5fd98909 Suppress notification content when using biometric authentication 2019-07-10 18:41:39 +02:00
M66B
955c997736 Notify per group 2019-07-10 18:33:25 +02:00
M66B
0b29f443f9 Simplification 2019-07-10 18:31:03 +02:00
M66B
59c2eedb5f Prevent crash 2019-07-10 18:17:28 +02:00
M66B
7e5d4bc715 Revert "Added show quotes button"
This reverts commit d9052eaaae.
2019-07-10 18:00:48 +02:00
M66B
d4742e157f Revert "Made showing quotes indepedent of threading"
This reverts commit 77e486bf7d.
2019-07-10 17:59:04 +02:00
M66B
0611cd66d1 Added biometric authentication 2019-07-10 17:58:26 +02:00
M66B
02433e0bc1 Updated FAQ 2019-07-10 14:06:41 +02:00
M66B
2efda11920 Updated description 2019-07-10 13:57:51 +02:00
M66B
8ef86ee219 Added FAQ 2019-07-10 12:44:55 +02:00
M66B
77e486bf7d Made showing quotes indepedent of threading 2019-07-10 11:38:53 +02:00
M66B
9b53442dcc 1.579 release 2019-07-10 11:29:40 +02:00
M66B
1dc90c332f Crowdin sync 2019-07-10 11:29:24 +02:00
M66B
6a7969b6ce Updated FAQ 2019-07-10 11:26:20 +02:00
M66B
51537d9d28 Added option to enable collapsing quoted text 2019-07-10 11:25:23 +02:00
M66B
e51b7a1fbf Log activity intent 2019-07-10 11:10:32 +02:00
M66B
4c78cc823b Revert "Define manage space activity"
This reverts commit 485474b415.
2019-07-10 11:08:40 +02:00
M66B
ce73273ae7 Log activity intent 2019-07-10 11:03:06 +02:00
M66B
485474b415 Define manage space activity 2019-07-10 11:02:12 +02:00
M66B
3cbc81cb08 Improved cleanup 2019-07-10 10:57:51 +02:00
M66B
091b64f055 Updated FAQ 2019-07-10 10:49:15 +02:00
M66B
d9052eaaae Added show quotes button 2019-07-10 10:20:24 +02:00
M66B
3512920bbc 1.578 release 2019-07-10 09:50:37 +02:00
M66B
786862e195 Crowdin sync 2019-07-10 09:50:12 +02:00
M66B
4bfef11c2e Cleanup on boot/update 2019-07-10 09:46:29 +02:00
M66B
2067d89666 Added explicit cleanup 2019-07-10 09:35:20 +02:00
M66B
35f254eade "ActivityRecord not found" is not our fault 2019-07-10 09:18:30 +02:00
M66B
7d040e2658 Updated description 2019-07-10 09:08:12 +02:00
M66B
d1f2dac002 Updated FAQ 2019-07-10 08:47:55 +02:00
M66B
235fc2e82f Updated FAQ 2019-07-10 08:33:07 +02:00
M66B
b8dc9faf64 1.577 release 2019-07-10 07:55:24 +02:00
M66B
291436e4fe Crowdin sync 2019-07-10 07:54:58 +02:00
M66B
665827e37e Updated gradle 2019-07-10 07:53:57 +02:00
M66B
f47f039f66 Simplification 2019-07-09 22:44:02 +02:00
M66B
4bc057d9bb Simplification 2019-07-09 21:51:38 +02:00
M66B
813e74f5b6 Use ranges to fetch old uids 2019-07-09 21:07:56 +02:00
M66B
7619052ada Updated FAQ 2019-07-09 19:20:01 +02:00
M66B
f31e7e91ed Made log easy to access 2019-07-09 18:31:36 +02:00
M66B
5d8b14f25d Updated FAQ 2019-07-09 18:31:24 +02:00
M66B
c504225786 Log server ID 2019-07-09 17:59:58 +02:00
M66B
95a29a6225 High contrast preview 2019-07-09 15:19:20 +02:00
M66B
484ee8eeab Log notices 2019-07-09 13:40:14 +02:00
M66B
32515e01df Changed update notification icon 2019-07-09 13:17:28 +02:00
M66B
b880bfef96 Updated FAQ 2019-07-09 12:55:10 +02:00
M66B
7f867220b0 Moved authentication setting to behavior 2019-07-09 12:23:35 +02:00
M66B
ed0a57f707 1.576 release 2019-07-09 11:33:46 +02:00
M66B
22a6df4942 Crowdin sync 2019-07-09 11:33:29 +02:00
M66B
cff8541d44 Layout fix 2019-07-09 11:19:14 +02:00
M66B
4074263d58 Error color 2019-07-09 11:13:02 +02:00
M66B
6032fad099 Switch to warning flag for authentication failures 2019-07-09 11:02:43 +02:00
M66B
3d814f5ff9 1.575 release 2019-07-09 08:35:34 +02:00
M66B
39a0849872 Crowdin sync 2019-07-09 08:35:19 +02:00
M66B
11dc9b40af Added option to show text high contrast 2019-07-09 08:09:20 +02:00
M66B
9fada9c0e4 Always ask for disabling battery optimizations 2019-07-09 07:49:15 +02:00
M66B
61ebab80f0 Simplify escaping patterns 2019-07-09 07:48:57 +02:00
M66B
45c6e5678c Revert "Prevent white space"
This reverts commit 69729b0566.
2019-07-08 21:46:57 +02:00
M66B
69729b0566 Prevent white space 2019-07-08 21:42:35 +02:00
M66B
4d38a63a0b Increased default download size from 64 to 256 KB
There are too many questions about this
2019-07-08 21:36:31 +02:00
M66B
864b9ebcea Revert "Made div with br a special case"
This reverts commit 61408dfd02.
2019-07-08 21:31:00 +02:00
M66B
61408dfd02 Made div with br a special case 2019-07-08 21:15:19 +02:00
M66B
a6f2101352 Block elements with breaks are not empty 2019-07-08 20:29:00 +02:00
M66B
db3b9ac42b Updated description 2019-07-08 20:22:25 +02:00
M66B
08ab6e99a1 Prevent too many line breaks 2019-07-08 20:14:33 +02:00
M66B
619a6685ea Disable sound of update channel 2019-07-08 20:00:19 +02:00
M66B
4d756593f5 Added help button high battery usage 2019-07-08 19:58:02 +02:00
M66B
f62e1bce82 Removed auto keep alive adjust 2019-07-08 19:23:07 +02:00
M66B
381046d800 1.574 release 2019-07-08 17:00:19 +02:00
M66B
3280410c0c Crowdin sync 2019-07-08 16:59:57 +02:00
M66B
db1ea29fbf Auto adjust keep alive interval 2019-07-08 16:58:25 +02:00
M66B
d7ca03cfbb Reduced logging 2019-07-08 16:47:16 +02:00
M66B
717a1f336b Prevent showing error message 2019-07-08 16:06:51 +02:00
M66B
4df14940a1 Added hint 2019-07-08 15:44:59 +02:00
M66B
b815d622fe Simple layout for unselectable folders 2019-07-08 15:28:59 +02:00
M66B
88b5146245 Added option to always show remote content in original messages 2019-07-08 14:51:36 +02:00
M66B
77bd52ace6 Crowdin sync 2019-07-08 14:37:18 +02:00
M66B
7d4810ceae Block all network when showing original messages without showing images 2019-07-08 13:58:31 +02:00
M66B
f39d973b1f Fixed decoding of subject 2019-07-08 13:39:25 +02:00
M66B
8fb7b47f10 Revised attachment layout 2019-07-08 12:37:24 +02:00
M66B
0bee705e4d Improved message decoding 2019-07-08 11:01:40 +02:00
M66B
b31df5d1a4 1.573 release 2019-07-08 10:26:14 +02:00
M66B
a2d9556393 Crowdin sync 2019-07-08 10:25:55 +02:00
M66B
91a4f527ba Set GMX keep alive interval to 9 minutes 2019-07-08 10:21:27 +02:00
M66B
f0039c234a Fixed error after send after 2019-07-08 09:21:53 +02:00
M66B
f31dc74c3d Added auto trash 2019-07-08 09:19:16 +02:00
M66B
f1393e5793 Suppress "Unable to create layer" 2019-07-08 08:45:09 +02:00
M66B
5662a25a3c Show inline images in web view when enabled 2019-07-07 23:19:42 +02:00
M66B
74016592ac Prefer first text/html part 2019-07-07 22:53:52 +02:00
M66B
377812ece9 HTML bodies do not have a filename 2019-07-07 11:58:56 +02:00
M66B
e878a9fe2a Revert "Enable auto delete for spam"
This reverts commit 40d55cfd00.
2019-07-07 11:47:39 +02:00
M66B
ba4fc168de Added remark about ipinfo.io 2019-07-07 11:13:11 +02:00
M66B
75a26bf68d Reload on changing sync folders 2019-07-07 10:51:18 +02:00
M66B
3a34a30cbc Made extra privacy features explicit 2019-07-07 10:50:06 +02:00
M66B
bd7e879eff 1.572 release 2019-07-07 09:55:28 +02:00
M66B
1e5560dd1c Updated text, crowdin sync 2019-07-07 09:54:49 +02:00
M66B
9e5ccb28b9 Improved read only folder support 2019-07-07 09:38:43 +02:00
M66B
beba3b3815 Small layout fix 2019-07-07 09:11:43 +02:00
M66B
697f72a30f Reset account/identity error on inline sync disable 2019-07-07 08:42:46 +02:00
M66B
9f6c1f3ea9 Support for non selectable folders 2019-07-07 08:23:46 +02:00
M66B
e8ced5da45 Small layout fix 2019-07-07 07:43:50 +02:00
M66B
f33047f332 Updated constraint layout library 2019-07-06 20:39:08 +02:00
M66B
38a53f2f49 Small layout improvement 2019-07-06 20:30:17 +02:00
M66B
40d55cfd00 Enable auto delete for spam 2019-07-06 20:26:33 +02:00
M66B
c127ff6fa4 1.571 release 2019-07-06 19:10:35 +02:00
M66B
0451dda73c Crowdin sync 2019-07-06 19:10:18 +02:00
M66B
f2ddefc150 Small improvement 2019-07-06 19:07:23 +02:00
M66B
3ff91764d8 Added button to simplify decryption 2019-07-06 18:59:25 +02:00
M66B
1cf32ef90e Prevent crash 2019-07-06 18:33:21 +02:00
M66B
6e02eb9b07 Purge old, browsed/searched messages 2019-07-06 18:18:26 +02:00
M66B
338523ac0c Added setting to delete old, unseen messages 2019-07-06 18:13:38 +02:00
M66B
6a6be9f9c3 1.570 release 2019-07-06 17:15:25 +02:00
M66B
963d36cb4b Improved account/identity help 2019-07-06 16:56:24 +02:00
M66B
0fbff8cfdf Fix attachment disposition 2019-07-06 14:18:14 +02:00
M66B
18f0e29437 Resize replied images 2019-07-06 13:46:38 +02:00
M66B
28fd55a639 Show link host name 2019-07-06 13:17:51 +02:00
M66B
9341b166ba Read message file as stream 2019-07-06 13:02:32 +02:00
M66B
7630a6edcb Use dialog fragment to view images 2019-07-06 12:52:00 +02:00
M66B
edde5e8414 Refactoring 2019-07-06 12:16:42 +02:00
M66B
309001f156 Fixed viewing external images 2019-07-06 12:14:34 +02:00
M66B
a7f455f159 Updated FAQ 2019-07-06 11:54:21 +02:00
M66B
d139c72bba Send without encryption when no key confirmed 2019-07-06 11:42:02 +02:00
M66B
da839c8857 Ignore bad notification posted 2019-07-06 11:33:45 +02:00
M66B
586db9d9ba 1.568 release 2019-07-06 10:54:39 +02:00
M66B
fb31a4ff7c Crowdin sync 2019-07-06 10:54:20 +02:00
M66B
2102131d99 Added live data patch 2019-07-06 10:51:16 +02:00
M66B
24ea54f034 Updated AndroidX libraries 2019-07-06 10:46:15 +02:00
M66B
7c856c7a9e Crowdin sync 2019-07-06 10:28:04 +02:00
M66B
46dd099e24 Update text 2019-07-06 10:24:55 +02:00
M66B
dd3d92d61f Persist notifying messages 2019-07-06 10:16:42 +02:00
M66B
828d936325 Swipe move similar messages 2019-07-06 07:53:12 +02:00
M66B
11a581dff8 simplification 2019-07-06 07:50:08 +02:00
M66B
d4f1d92f9e Prevent crash 2019-07-05 21:00:56 +02:00
M66B
2a03d75639 Prevent crash 2019-07-05 20:54:07 +02:00
M66B
4797737b79 Small layout improvement 2019-07-05 20:42:34 +02:00
M66B
4a25cbe269 Global enabled / schedule 2019-07-05 20:36:41 +02:00
M66B
f906271b0c Prevent crash 2019-07-05 20:28:41 +02:00
M66B
178c885183 Workaround Javamail encoding bug 2019-07-05 20:12:12 +02:00
M66B
f01235ba01 Fixed export/import 2019-07-05 19:41:16 +02:00
M66B
6f78a96682 Updated description 2019-07-02 11:51:06 +02:00
M66B
25a19ad0c1 Added option to show inline images by default 2019-07-02 10:58:11 +02:00
M66B
b4a8bc1568 Layout fix 2019-07-02 10:33:21 +02:00
M66B
151c479269 For my own convenience 2019-07-02 10:32:06 +02:00
M66B
20bdcf65b0 Added setup hint 2019-07-02 10:27:36 +02:00
M66B
4a4d003787 1.568 release 2019-07-02 09:10:57 +02:00
M66B
7a8a34343f Skip confirm exit on external search 2019-07-02 09:05:29 +02:00
M66B
3b718aad63 Fixed auto move 2019-07-02 08:57:20 +02:00
M66B
34b2a0583e IAB improvements 2019-07-02 08:50:16 +02:00
M66B
ddbe0dcae3 Simplification 2019-07-02 07:53:58 +02:00
M66B
8ee2e1ec83 Fixed default charset 2019-07-02 07:35:04 +02:00
M66B
05f8d03e96 Added spinner to full message view 2019-07-01 23:02:31 +02:00
M66B
0b012fc774 1.567 release 2019-07-01 21:34:46 +02:00
M66B
435b23a444 Reordered folder context menu 2019-07-01 21:32:41 +02:00
M66B
ce39684810 Crowdin sync 2019-07-01 21:26:57 +02:00
M66B
1ef7a69670 Fixes 2019-07-01 21:22:57 +02:00
M66B
351e851083 Replace export/import snackbars by toasts 2019-07-01 20:45:06 +02:00
M66B
2699b34bcf Simplification 2019-07-01 20:34:02 +02:00
M66B
4888ee8482 Prevent crash 2019-07-01 20:19:22 +02:00
M66B
5b33d64a51 Simplification, cleanup 2019-07-01 19:59:09 +02:00
M66B
8f1d5cda93 Fixes 2019-07-01 19:54:37 +02:00
M66B
86822f4e79 Removed dependency on dialog builder lifecycle
Finally!
2019-07-01 19:46:18 +02:00
M66B
cebd727209 Fixed zooming of level drawables 2019-07-01 19:40:07 +02:00
M66B
a087e79c5e Use dialog fragment to show original message 2019-07-01 19:15:45 +02:00
M66B
d9c111beb7 Updated FAQ 2019-07-01 19:09:17 +02:00
M66B
ba242ac0b6 Use dialog fragment to select account 2019-07-01 16:46:40 +02:00
M66B
53a71bd632 Simplification, dialog fragment for quick setup 2019-07-01 16:27:22 +02:00
M66B
44fa9c4ce0 A matter of focus 2019-07-01 15:55:15 +02:00
M66B
e8aa4d3f7d Refactoring 2019-07-01 15:52:34 +02:00
M66B
9325eb98ba Use dialog fragments to check/delete rules 2019-07-01 15:25:31 +02:00
M66B
ce159b1776 Use dialog fragment to delete answer 2019-07-01 15:01:37 +02:00
M66B
737eef3cbb Use dialog fragment to edit signature html 2019-07-01 14:56:13 +02:00
M66B
4b1da12f81 Simplification 2019-07-01 14:43:52 +02:00
M66B
5354e4eadd Use dialog fragments for account/identitie 2019-07-01 14:29:35 +02:00
M66B
22cedd0a84 Small improvements 2019-07-01 14:14:09 +02:00
M66B
9f5a829206 Use dialog fragment for unexpected errors 2019-07-01 14:06:15 +02:00
M66B
b855358169 Use dialog fragments for compose actions 2019-07-01 10:59:38 +02:00
M66B
72db63e979 Safely reference parent fragment from adapters 2019-07-01 09:30:07 +02:00
M66B
0d5b1e2bac Use dialog fragment to delete error operations 2019-06-30 23:15:58 +02:00
M66B
784f4bb442 Simplification 2019-06-30 23:12:36 +02:00
M66B
19c8fbce7a Use dialog fragments for answers and contacts 2019-06-30 23:05:00 +02:00
M66B
be56a865cb Use dialog fragments to select theme, disable doze, boundary errors 2019-06-30 22:52:33 +02:00
M66B
976daac18a Small fix 2019-06-30 22:27:49 +02:00
M66B
da13999b2e Use dialog fragments to manage keywords 2019-06-30 22:14:52 +02:00
M66B
879d348c3a Use dialog fragment to print 2019-06-30 21:43:48 +02:00
M66B
f3e573f13f Use dialog fragment to report spam / delete permanently 2019-06-30 21:20:16 +02:00
M66B
d98d8e9ad5 Use dialog fragment to open links 2019-06-30 20:56:31 +02:00
M66B
4f5fa6791e Use dialog fragment for folder actions 2019-06-30 20:47:30 +02:00
M66B
8cec626e72 Use dialog fragment to ask fo reporting 2019-06-30 20:17:32 +02:00
M66B
77502a5a7b Refactoring 2019-06-30 20:17:02 +02:00
M66B
0d33077b19 Use dialog fragment to select folder/duration 2019-06-30 19:43:01 +02:00
M66B
0f7f0e1ad3 Use dialog fragment to select colors 2019-06-30 16:55:15 +02:00
M66B
d2b0cba6a6 Use dialog fragment to confirm move/delete 2019-06-30 14:29:31 +02:00
M66B
8a0fafa620 Use dialog fragment to ask sure 2019-06-30 13:13:02 +02:00
M66B
5e65931e37 Use dialog fragment 2019-06-30 12:22:19 +02:00
M66B
ec6e63d190 Use dialog fragment 2019-06-30 11:35:03 +02:00
M66B
29ec1ef5bc Dismiss color picker on pause
Signed-off-by: M66B <M66B@users.noreply.github.com>
2019-06-30 10:25:50 +02:00
M66B
ea1f85e712 Small layout improvement 2019-06-30 10:08:14 +02:00
M66B
a6963da515 Show image description when not showing images 2019-06-30 09:59:03 +02:00
M66B
655f0e3d3b Suppress more white space 2019-06-30 09:51:43 +02:00
M66B
1072edaa78 Refactoring 2019-06-30 09:41:18 +02:00
M66B
003d7c720e Updated FAQ 2019-06-30 09:19:11 +02:00
M66B
a1434ca3cf Fixed autocrypt keydata 2019-06-30 08:56:33 +02:00
M66B
eb009db29f Updated FAQ 2019-06-30 07:46:26 +02:00
M66B
eb2ecc2003 Updated release stages 2019-06-30 07:25:08 +02:00
M66B
6ed556d476 1.566 release 2019-06-29 21:35:38 +02:00
M66B
257d27b2ca Periodically post available live data, even when invalidated 2019-06-29 21:34:26 +02:00
M66B
3669b9ef2e Added lifecycle-livedata inline 2019-06-29 21:18:24 +02:00
M66B
3fadf58d6a 1.565 release 2019-06-29 17:41:17 +02:00
M66B
8030207323 Crowdin sync 2019-06-29 17:41:11 +02:00
M66B
94f38877ba Fixed flickering 2019-06-29 17:40:17 +02:00
M66B
7262523e77 Fixed crash on new account 2019-06-29 17:30:31 +02:00
M66B
7a988e3005 Report attachment errors 2019-06-29 16:52:43 +02:00
M66B
c94f272714 Sanitize more file name characters 2019-06-29 15:41:16 +02:00
M66B
dcce8a89a0 1.564 release 2019-06-29 10:40:55 +02:00
M66B
a7926d42b1 Crowdin sync 2019-06-29 10:40:38 +02:00
M66B
e28119dbd7 Workaround Android crash 2019-06-29 09:23:50 +02:00
M66B
a3f9cd75ea Attachment fixes 2019-06-29 08:52:15 +02:00
M66B
27bce77069 Fetch live data on foreground 2019-06-28 22:47:15 +02:00
M66B
1a55487a00 Link pro 2019-06-28 21:22:20 +02:00
M66B
1af694cd86 Small layout improvement 2019-06-28 21:04:37 +02:00
M66B
fd9bd9c962 1.563 release 2019-06-28 19:11:26 +02:00
M66B
271927c472 Crowdin sync 2019-06-28 19:11:07 +02:00
M66B
f52e556e2a Simplification 2019-06-28 18:49:50 +02:00
M66B
31ec9bff63 Long press for colored star 2019-06-28 18:43:36 +02:00
M66B
1c3bebdc39 Restored query 2019-06-28 18:31:01 +02:00
M66B
3ef9db42ab Better link sanitization 2019-06-28 18:15:42 +02:00
M66B
7d93011f3a Suppress white space 2019-06-28 16:30:27 +02:00
M66B
2e429e4f8e Simplification 2019-06-28 15:45:11 +02:00
M66B
51606f2763 Cleanup 2019-06-28 12:48:26 +02:00
M66B
d64143c118 Use folder dialog for search 2019-06-28 11:15:01 +02:00
M66B
2498e5be3b Undo query changes 2019-06-28 11:00:55 +02:00
M66B
893980f6d6 Refactoring 2019-06-28 10:55:34 +02:00
M66B
7710b223f0 Revert "Fixed folder sync state"
This reverts commit 227d092c2a.
2019-06-28 10:39:18 +02:00
M66B
59797464ef Query fix 2019-06-28 10:37:22 +02:00
M66B
4795f62b45 For my own convenience 2019-06-28 10:10:16 +02:00
M66B
d70cf95aea Small fix 2019-06-28 10:08:45 +02:00
M66B
227d092c2a Fixed folder sync state 2019-06-28 10:06:32 +02:00
M66B
0eb9698c30 Simplification 2019-06-28 09:50:44 +02:00
M66B
7ecfd7a788 Fixed folder sort order 2019-06-28 09:46:18 +02:00
M66B
851ead228c Added option to ask what to do on swipe 2019-06-28 09:32:06 +02:00
M66B
8ba11789b3 Updated work manager 2019-06-28 08:15:27 +02:00
M66B
8abce39883 Crowdin sync 2019-06-27 23:10:06 +02:00
M66B
ebf2d1a895 Small improvement 2019-06-27 10:09:24 +02:00
M66B
898e7bccf3 Query fixes 2019-06-27 09:43:03 +02:00
M66B
9e6d9903c0 Updated FAQ 2019-06-27 09:41:27 +02:00
M66B
fd5eac4cda Small improvement 2019-06-27 09:20:29 +02:00
M66B
b11f948451 Suppress connection exceptions 2019-06-27 09:18:57 +02:00
M66B
44a55a677a Better consult an SQL guru like Leo ... 2019-06-27 08:09:22 +02:00
M66B
2925312631 Always group notifications 2019-06-27 08:00:36 +02:00
M66B
9b1514e2f0 Improved folder query 2019-06-26 22:08:40 +02:00
M66B
e981120b96 Updated FAQ 2019-06-26 21:28:42 +02:00
M66B
0e3e4ddcf2 Refactoring attachments 2019-06-26 21:21:09 +02:00
M66B
171eb97e55 Prevent crash 2019-06-26 20:42:52 +02:00
M66B
18f90b24a1 Revert "Blue as the sky"
This reverts commit 4cc1ed3a85.
2019-06-26 20:35:45 +02:00
M66B
041406d0c5 Small fix 2019-06-26 20:32:30 +02:00
M66B
286a6a9720 Conditionally sanitize exceptions 2019-06-26 16:51:29 +02:00
M66B
4cc1ed3a85 Blue as the sky 2019-06-26 16:35:46 +02:00
M66B
4bd5f7387c 1.562 release 2019-06-26 16:13:05 +02:00
M66B
5dc3a6b163 Crowdin sync 2019-06-26 16:11:39 +02:00
M66B
bdf9478075 Select action on swipe 2019-06-26 16:11:33 +02:00
M66B
02f129ad07 Fixed junk to inbox icon 2019-06-26 15:40:48 +02:00
M66B
7338ebe7cc Disable double tap for outbox 2019-06-26 14:36:44 +02:00
M66B
fcd9dbd4e9 Added option to enable double tap 2019-06-26 13:55:35 +02:00
M66B
3cf4261118 Cleanup 2019-06-26 13:44:57 +02:00
M66B
e4fdccd098 1.561 release 2019-06-26 13:01:55 +02:00
M66B
9ca617ab44 Crowdin sync 2019-06-26 13:01:35 +02:00
M66B
7df58a4a94 Double tap to mark read/unread 2019-06-26 12:58:36 +02:00
M66B
bbd9c73613 Updated FAQ 2019-06-26 11:28:25 +02:00
M66B
6920dc0aaa Small fix 2019-06-26 08:57:50 +02:00
M66B
bd12f769c9 Added menu item to create sub folders 2019-06-26 08:55:27 +02:00
M66B
384d1180cf Synchronize all for receiving folders only 2019-06-26 08:23:27 +02:00
M66B
6f67982ce0 Store folder separator 2019-06-26 08:13:39 +02:00
M66B
1774a619e0 Check message/attachment files on manual clean only 2019-06-26 07:39:48 +02:00
M66B
965dfdb84b uuid is useful only when reporting is enabled 2019-06-25 19:58:50 +02:00
M66B
45c01d39a4 Check message and attachment files on cleanup 2019-06-25 19:53:04 +02:00
M66B
83464aafe0 Improved logging 2019-06-25 16:43:23 +02:00
M66B
dfcbd40dbe Consider system folders as subscribed to 2019-06-25 16:34:30 +02:00
M66B
3288ae1d45 1.560 release 2019-06-25 14:26:17 +02:00
M66B
693284e5a0 Crowdin sync 2019-06-25 14:26:11 +02:00
M66B
455d6f1cd0 Fixed crash 2019-06-25 14:25:56 +02:00
M66B
4115ed4bbc 1.559 release 2019-06-25 12:48:56 +02:00
M66B
21631c17be Better check 2019-06-25 12:48:04 +02:00
M66B
96f820658b Crowdin sync 2019-06-25 12:46:09 +02:00
M66B
bf835e2207 Added some more menu titles 2019-06-25 10:53:06 +02:00
M66B
e0e881944c Added folder name to long press menu 2019-06-25 10:46:26 +02:00
M66B
272d1adecb Invalidate headers on message add, delete, change 2019-06-25 10:16:37 +02:00
M66B
5f847a1f49 Fixed opening seen messages 2019-06-25 10:04:15 +02:00
M66B
1a5fa5e830 Fixed navigation count 2019-06-25 10:01:39 +02:00
M66B
78e48cd506 Refactoring 2019-06-25 09:47:53 +02:00
M66B
ecfbe1df43 Simplification 2019-06-25 08:56:59 +02:00
M66B
c1d80804d6 Fixed expand flicker 2019-06-25 08:53:33 +02:00
M66B
02936de391 Added logging 2019-06-25 08:05:28 +02:00
M66B
cf03e9f94f Account view has no folders 2019-06-24 21:01:31 +02:00
M66B
ebdb255484 1.558 release 2019-06-24 18:24:34 +02:00
M66B
a165940858 Crowdin sync 2019-06-24 18:24:16 +02:00
M66B
1675ccd62e Fixed autoread on move from/to drafts 2019-06-24 13:16:32 +02:00
M66B
e8e3542a2b Fixed adapter snackbar parent view 2019-06-24 13:08:49 +02:00
M66B
c4a46648a2 Added Play store link for testing 2019-06-24 13:00:41 +02:00
Marcel Bokhorst
4d820e84bd Merge pull request #156 from licaon-kter/patch-8
Update Play link
2019-06-24 12:55:27 +02:00
Licaon_Kter
6c629d9c79 Update Play link 2019-06-24 10:54:34 +00:00
M66B
857bc5f409 Added synchronize all 2019-06-24 12:45:52 +02:00
M66B
5501318b79 Fixed reply from notification 2019-06-24 11:58:44 +02:00
M66B
5932a8672c Simplification 2019-06-24 11:05:30 +02:00
M66B
4558e07710 1.557 release 2019-06-24 10:44:46 +02:00
M66B
80d92b976d Crowdin sync 2019-06-24 10:44:12 +02:00
M66B
0abdc55feb Encrypt by default when pro only 2019-06-24 10:31:09 +02:00
M66B
c9facb64f6 Prevent prematurely stopping send service 2019-06-24 10:22:59 +02:00
M66B
1141cf735f Fixed edge case decode MIME word 2019-06-24 09:59:44 +02:00
M66B
8c0e81d6df Attach snackbar to parent view 2019-06-24 09:49:45 +02:00
M66B
da0f528798 Support moving from/to drafts 2019-06-24 09:07:43 +02:00
M66B
0ed815fff7 Alert improvements 2019-06-24 08:45:55 +02:00
M66B
f5ca8f746d Folder not found is unrecoverable 2019-06-24 08:35:13 +02:00
M66B
833191f5d1 Reset drafts flag 2019-06-24 08:34:56 +02:00
M66B
11d8272633 Observe new folders 2019-06-24 08:34:17 +02:00
M66B
dea368dd96 Simplified error handling 2019-06-23 20:56:07 +02:00
M66B
b5921b8390 Updated description 2019-06-23 20:42:13 +02:00
M66B
0654ed2eb0 Observe messages to send 2019-06-23 19:28:30 +02:00
M66B
978843f134 Small improvements 2019-06-23 18:54:57 +02:00
M66B
fa22664ed3 Prevent crash 2019-06-23 18:20:52 +02:00
M66B
7b2e8f5879 Smaller font for sub/superscript 2019-06-23 17:56:22 +02:00
M66B
fd48898cba Updated text 2019-06-23 17:56:13 +02:00
M66B
0de1896e47 1.556 release 2019-06-23 16:31:38 +02:00
M66B
a8cc0ab5d6 Crowdin sync 2019-06-23 16:31:11 +02:00
M66B
66c2941dd6 Improved logging 2019-06-23 16:25:31 +02:00
M66B
947032f898 Infinity is 99+ 2019-06-23 16:14:30 +02:00
M66B
494162f97d Fixed back to unselect 2019-06-23 16:05:36 +02:00
M66B
6a0c23d012 Small improvement 2019-06-23 15:52:36 +02:00
M66B
1bce1da06d Added update notification channel 2019-06-23 15:17:45 +02:00
M66B
b56d295723 Relaxed file name sanitizing 2019-06-23 14:27:14 +02:00
M66B
7a4c4aa9f4 Locale widget count 2019-06-23 14:05:30 +02:00
M66B
6a81dc4ef0 Small improvement 2019-06-23 13:44:39 +02:00
M66B
192a522551 Minimum hide of 1 minute 2019-06-23 13:23:29 +02:00
M66B
8e9083b343 Fixed updating browsed 2019-06-23 12:37:26 +02:00
M66B
153e165b8f Prevent crash 2019-06-23 12:33:50 +02:00
M66B
923c4ffd91 1.555 release 2019-06-23 10:41:37 +02:00
M66B
14dc411826 Skip confirm on move to folder 2019-06-23 10:34:32 +02:00
M66B
935d2f0e60 Simplifications 2019-06-23 10:28:42 +02:00
M66B
6fb436155a Refactoring 2019-06-23 10:23:49 +02:00
M66B
ce39566d60 Disable ignore on unsnooze 2019-06-23 10:18:55 +02:00
M66B
09744379f6 Small improvement 2019-06-23 10:12:47 +02:00
M66B
9a8b6eb98b Fixed typo 2019-06-23 10:09:39 +02:00
M66B
639c7b3d62 Mark undone 2019-06-23 10:06:07 +02:00
M66B
24126504a6 Skip unhide on browse 2019-06-23 09:50:30 +02:00
M66B
f0058de11b Skip snooze if in past 2019-06-23 09:48:38 +02:00
M66B
5620e8b2c0 Fixed parsing headers 2019-06-23 09:48:29 +02:00
M66B
80200de9dc 1.554 release 2019-06-22 19:39:39 +02:00
M66B
eb29899862 Crowdin sync 2019-06-22 19:39:01 +02:00
M66B
131d585432 Fixed disappearing icons 2019-06-22 19:36:29 +02:00
M66B
8b2e83aca8 Use update notification instead of dialog 2019-06-22 16:34:46 +02:00
M66B
d33d4dfe83 More contrast for letter icon 2019-06-22 15:33:53 +02:00
M66B
4929bf2429 Remove NUL characters from received messages 2019-06-22 15:22:13 +02:00
M66B
087a98359c 1.553 release 2019-06-22 15:03:48 +02:00
M66B
b01391a480 Crowdin sync 2019-06-22 15:03:10 +02:00
M66B
fd88eed6f9 Auto rotate received images 2019-06-22 14:49:02 +02:00
M66B
c55936b806 Refactoring 2019-06-22 14:41:02 +02:00
M66B
af5b3ba467 Inline attachments required a content ID 2019-06-22 14:32:42 +02:00
M66B
23cc120c3a 1.552 release 2019-06-22 13:49:53 +02:00
M66B
ac8b92f8c0 Rule snooze relative to message received time 2019-06-22 13:49:34 +02:00
M66B
8762509e80 Improved message header decoding 2019-06-22 13:26:45 +02:00
M66B
618a0cbfef Decode US-ASCII as ISO_8859_1 2019-06-22 12:34:33 +02:00
M66B
ba9340ab8e Updated FAQ 2019-06-22 12:10:50 +02:00
M66B
626b2f3f53 Updated text 2019-06-22 12:07:27 +02:00
M66B
3c399d0997 Assume ISO_8859_1 to be default charset 2019-06-22 11:29:36 +02:00
M66B
1f6cefab1c Print message header 2019-06-22 09:55:07 +02:00
M66B
311403e3e0 1.551 release 2019-06-22 08:45:09 +02:00
M66B
3762513d4f Crowdin sync 2019-06-22 08:44:04 +02:00
M66B
270aa6eab6 Fixed message count 2019-06-22 08:37:39 +02:00
M66B
1a606cbf6d Simplified signature edit 2019-06-22 08:03:35 +02:00
M66B
8bd364b050 Catch all quick config exceptions 2019-06-21 21:22:29 +02:00
M66B
8c2132fc56 Check on changing insecure 2019-06-21 20:33:50 +02:00
M66B
03ff3d068a Trim usernames 2019-06-21 19:46:46 +02:00
M66B
6a86625862 Updated description 2019-06-21 18:55:15 +02:00
M66B
0eecf0bede Reload on killed 2019-06-21 18:00:52 +02:00
M66B
5f872c51fd Updated FAQ 2019-06-21 12:11:22 +02:00
M66B
f324ef4769 Revert "Small improvement"
This reverts commit cbdb296d13.
2019-06-21 12:03:57 +02:00
M66B
cbdb296d13 Small improvement 2019-06-21 11:38:44 +02:00
M66B
aebed56970 Updated FAQ 2019-06-21 11:07:15 +02:00
M66B
3345ad6505 Updated description 2019-06-21 11:05:07 +02:00
M66B
e4206e74d3 Updated FAQ 2019-06-21 09:47:59 +02:00
M66B
b993791358 1.550 release 2019-06-21 09:34:27 +02:00
M66B
25f2c2e4e5 Crowdin sync 2019-06-21 09:30:16 +02:00
M66B
3376a58adf Updates screenshots 2019-06-21 09:22:12 +02:00
M66B
36848ded6d Crowdin sync 2019-06-21 09:21:56 +02:00
M66B
3314ffaa09 Improved list post header parsing 2019-06-21 08:52:06 +02:00
M66B
f3e5159168 Show if folder has active rules 2019-06-21 08:39:36 +02:00
M66B
08c37785ad Fix NUL characters in decrypted messages 2019-06-21 08:12:35 +02:00
M66B
56a98260f7 Merge branch 'master' of github.com:M66B/open-source-email 2019-06-21 08:09:38 +02:00
M66B
54d630dd59 Fix NUL characters in decrypted messages 2019-06-21 08:06:02 +02:00
Marcel Bokhorst
986d34abe8 Updated FAQ 2019-06-20 21:24:48 +02:00
M66B
a510e8013e Small fix/simplification 2019-06-20 19:09:33 +02:00
M66B
af9fd4c4a0 Updated workmanager 2019-06-20 19:07:22 +02:00
M66B
75f05d52b2 Small improvement 2019-06-20 18:47:26 +02:00
M66B
7150d32872 Use unique alarm intents 2019-06-20 18:22:23 +02:00
M66B
3d7e4e0d75 Check on account/identity error 2019-06-20 18:04:24 +02:00
Marcel Bokhorst
4efae3cdf6 Updated FAQ 2019-06-20 17:12:25 +02:00
M66B
a70e408dee Updated FAQ 2019-06-20 15:00:30 +02:00
M66B
f141e866a9 1.549 release 2019-06-20 11:57:24 +02:00
M66B
39cabfd5f0 Crowdin sync 2019-06-20 11:56:12 +02:00
M66B
a262cd731b Added/updated FAQ 2019-06-20 11:42:36 +02:00
M66B
5215bfce98 Hack to prevent disappearing icons 2019-06-20 10:33:29 +02:00
M66B
7843134b47 Updated FAQ 2019-06-20 10:03:01 +02:00
M66B
e4b33ba7d8 Redirect to XDA for support on third party builds 2019-06-20 09:39:59 +02:00
M66B
bccbd1b2f0 Updated FAQ 2019-06-20 09:23:16 +02:00
M66B
975098de1f 1.548 release 2019-06-20 09:12:34 +02:00
M66B
3309dd5f7d Skip ensuring uid on delete 2019-06-20 09:09:05 +02:00
M66B
ce9b7e82e2 Improved error handling 2019-06-20 08:49:45 +02:00
M66B
c81389769e Crowdin sync 2019-06-20 07:32:07 +02:00
M66B
c95de9e756 Add signature to sent message 2019-06-20 07:25:58 +02:00
M66B
a60ded83dd Cleanup models 2019-06-20 07:25:38 +02:00
M66B
fec7c0f0f0 Updated description 2019-06-19 18:30:34 +02:00
M66B
9dae2b5bd7 Updated icon 2019-06-19 18:10:35 +02:00
M66B
45bdac39d2 Improved widget layout 2019-06-19 17:48:00 +02:00
M66B
2f59257b1c Updated setup instructions 2019-06-19 17:15:21 +02:00
M66B
d15ccee62f Updated FAQ 2019-06-19 17:15:14 +02:00
M66B
3675880c1d 1.547 release 2019-06-19 15:26:12 +02:00
M66B
0b313fa9df Simplification 2019-06-19 15:22:22 +02:00
M66B
cf1452e5b9 Crowdin sync 2019-06-19 15:20:53 +02:00
M66B
65ce1df6ae Cleanup 2019-06-19 15:17:03 +02:00
M66B
fe7150674e Updated FAQ 2019-06-19 15:15:12 +02:00
M66B
2c1a0155d5 Keep XOAUTH2 accounts enabled 2019-06-19 15:13:00 +02:00
M66B
0d35a7a22b Revert "Updated description"
This reverts commit 558eec10b6.
2019-06-19 15:06:08 +02:00
M66B
12bc1c02eb Scroll to save button on selecting save changes 2019-06-19 15:02:13 +02:00
M66B
5cb41067cd Small improvement 2019-06-19 14:55:06 +02:00
M66B
eddfad1e25 Removed select account 2019-06-19 14:51:19 +02:00
M66B
4e72ae5a16 Updated links 2019-06-19 13:11:22 +02:00
M66B
da530944d4 Prevent cleanup of debug info attachments 2019-06-19 12:23:17 +02:00
M66B
ce0e2ba5f8 Refactoring 2019-06-19 12:03:50 +02:00
M66B
b9408f26ce Replaced toasts by snackbars 2019-06-19 10:55:11 +02:00
M66B
ccbd430166 Remove dependency on getUserVisibleHint 2019-06-19 10:17:05 +02:00
M66B
db0c857e29 Added FAQ 2019-06-19 09:33:12 +02:00
M66B
3749521ffb Always add draft once 2019-06-19 07:23:46 +02:00
M66B
b05653b94b Fix invalid HTML 2019-06-18 21:56:34 +02:00
M66B
878037e83e 1.546 release 2019-06-18 21:45:58 +02:00
M66B
96d1e83c95 Crowdin sync 2019-06-18 21:45:36 +02:00
M66B
b40ce24f61 Fixed printing with forced English 2019-06-18 21:41:15 +02:00
M66B
852b90ebb7 Prevent crash on searching 2019-06-18 21:30:57 +02:00
M66B
a1f13d8e3a Prevent crash on refreshing folders 2019-06-18 21:25:31 +02:00
M66B
4dcbe807df Improved error message 2019-06-18 21:22:49 +02:00
M66B
d274ea64f6 Prevent crash on work manager init 2019-06-18 21:20:03 +02:00
M66B
b80f7db3cb Prevent crash 2019-06-18 21:12:24 +02:00
M66B
6e54c67429 Throw exception on refreshing token in third party builds 2019-06-18 21:09:08 +02:00
M66B
1f989b2499 Revert "Catch parse exception of disposition and filename only"
This reverts commit f7e5de7fe3.
2019-06-18 20:14:42 +02:00
M66B
829d6f38ce Updated FAQ 2019-06-18 20:04:57 +02:00
M66B
cb7e42ee77 Report raw operation exceptions 2019-06-18 20:00:22 +02:00
M66B
1a602664c4 Small improvement 2019-06-18 19:54:23 +02:00
M66B
ea2ed1e8cf Updated text 2019-06-18 19:49:17 +02:00
M66B
f7e5de7fe3 Catch parse exception of disposition and filename only 2019-06-18 19:39:53 +02:00
M66B
119df372d6 Messages in conversations should always be expandable 2019-06-18 19:25:50 +02:00
M66B
e56673332e Ensure action foreground color for all states 2019-06-18 18:57:58 +02:00
M66B
ce25f9bf64 Added uid to debug info 2019-06-18 18:30:01 +02:00
M66B
64b56a50a3 Amendments 2019-06-18 17:52:56 +02:00
M66B
43a8ed9744 Merge branch 'master' of github.com:M66B/open-source-email 2019-06-18 17:41:39 +02:00
M66B
349f17d79a Select account is available in original versions only 2019-06-18 17:40:57 +02:00
Marcel Bokhorst
558eec10b6 Updated description 2019-06-18 09:38:48 +02:00
M66B
b280e2bfa5 Fixed typo 2019-06-16 10:38:25 +02:00
M66B
231181da81 Check folder dirty 2019-06-16 10:22:43 +02:00
M66B
757bebd2e4 Check identity dirty 2019-06-16 10:22:37 +02:00
M66B
f9b8afa988 Check account dirty, refactoring 2019-06-16 10:14:44 +02:00
M66B
c885aa4927 Cleanup 2019-06-16 07:36:14 +02:00
M66B
35311118ab Show number of processors 2019-06-15 21:25:49 +02:00
M66B
c98683818b Added comment 2019-06-15 19:28:57 +02:00
M66B
cdada06344 Refactoring 2019-06-15 18:10:33 +02:00
M66B
a2f664e979 1.545 release 2019-06-15 14:11:34 +02:00
M66B
968b441d3b Crowdin sync 2019-06-15 14:11:10 +02:00
M66B
7ba878ef98 Added comment 2019-06-15 14:08:31 +02:00
M66B
26216a88d5 Fixed warnings 2019-06-15 13:26:22 +02:00
M66B
d34833fc8a Default enable sync kept 2019-06-15 13:14:41 +02:00
M66B
1cb056d059 Limit append buffer size to 4 MB to prevent OOM 2019-06-15 12:35:08 +02:00
M66B
3d6ffadc5e Bring back folder hiding 2019-06-15 11:58:56 +02:00
M66B
61b8df8fa8 Added button to directly navigate to the notification channel settings 2019-06-15 11:28:25 +02:00
M66B
07106a6ef9 Sanitize IOException/MessageRemovedException 2019-06-15 10:29:52 +02:00
M66B
1939d28a2d Added hint about notification channel settings 2019-06-15 10:18:44 +02:00
M66B
42bf645ae6 Updated Bugsnag config 2019-06-15 10:02:41 +02:00
M66B
2f635d582d 1.544 release 2019-06-15 09:39:04 +02:00
M66B
ad1e2e4f80 Updated Bugsnag 2019-06-15 09:33:56 +02:00
M66B
025454ae32 Crowdin sync 2019-06-15 09:31:12 +02:00
M66B
dd4262039e Small layout improvement 2019-06-15 09:25:46 +02:00
M66B
917bac6825 Added option to show keyboard by default (enabled) 2019-06-15 08:49:46 +02:00
M66B
d710ddfdaa Removed X-Mailer header 2019-06-15 08:33:46 +02:00
M66B
1bad828624 Small improvement 2019-06-14 22:13:19 +02:00
M66B
0e0386aa49 Manage connectivity on no connection 2019-06-14 22:06:25 +02:00
M66B
31480d8713 Added manage connectivity button 2019-06-14 21:54:36 +02:00
M66B
2088d3a4ba Restore display tab on changing theme 2019-06-14 21:41:53 +02:00
M66B
ca23b0dd85 Small layout improvement 2019-06-14 21:14:51 +02:00
M66B
34fbdfde30 Updated description 2019-06-14 20:30:30 +02:00
M66B
7b0782ae49 Switch to appcompat for vector images 2019-06-14 20:29:40 +02:00
M66B
9f76ae2a26 Reset color using image button 2019-06-14 19:47:48 +02:00
M66B
a267d95b2e 1.543 release 2019-06-14 19:13:16 +02:00
M66B
d0c1ee416f Moved select theme to display tab 2019-06-14 19:12:56 +02:00
M66B
50c3a7b6a9 Moved manage notifications to tab page 2019-06-14 19:05:28 +02:00
M66B
fb1007e9cf Crowdin sync 2019-06-14 18:48:45 +02:00
M66B
f327b7b284 Added account setting to disable partial fetch
https://javaee.github.io/javamail/docs/NOTES.txt
2019-06-14 18:29:17 +02:00
M66B
56858282b9 Added option to send plain text by default 2019-06-14 17:58:09 +02:00
M66B
86c11ad784 Experiment 2019-06-14 17:39:44 +02:00
M66B
2dfc92c5ad Set UTC date header 2019-06-14 17:26:07 +02:00
M66B
c2b34ddd3a Disabled multipart caching 2019-06-14 17:12:05 +02:00
M66B
a4fd99414b Added logging 2019-06-14 16:23:44 +02:00
M66B
c6b977b5e7 Log downloaded body size 2019-06-14 15:32:17 +02:00
M66B
58d38a1b29 Hide no messages on loading 2019-06-14 14:46:00 +02:00
M66B
e27c7b0c2b Handle removed exception when getting message body 2019-06-14 14:43:00 +02:00
M66B
bffbc59b70 Handle IO exceptions when getting message body 2019-06-14 13:51:15 +02:00
M66B
bb3e7bbd69 Improved read text file
This will not add an extra newline at the end
2019-06-14 13:37:30 +02:00
M66B
a678e9292b Fix seek bar 2019-06-14 12:40:27 +02:00
M66B
86a192fa9c Use black theme for system night theme 2019-06-14 12:36:18 +02:00
M66B
2496d8f546 1.542 release 2019-06-14 11:26:35 +02:00
M66B
4c1db52b01 Fixed warnings, simplification 2019-06-14 11:25:56 +02:00
M66B
d44d0d6230 Fixed warnings 2019-06-14 11:20:14 +02:00
M66B
b490e49ee7 Refactoring 2019-06-14 11:18:08 +02:00
M66B
3e9e56d878 Prevent crash 2019-06-14 11:00:34 +02:00
M66B
69acf59245 Crowdin sync 2019-06-14 10:44:42 +02:00
M66B
6839eef7f8 Refactoring 2019-06-14 10:42:54 +02:00
M66B
6ed8168316 Ensure uid 2019-06-14 10:41:34 +02:00
M66B
48a348fcff Improved delete 2019-06-14 10:12:08 +02:00
M66B
5d830c2d16 Cleanup, extra checks 2019-06-14 09:33:39 +02:00
M66B
1f3e4a20f1 1.541 release 2019-06-14 09:06:25 +02:00
M66B
6fc86bc2b5 Crowdin sync 2019-06-14 09:05:57 +02:00
M66B
2c134685ca Updated libraries 2019-06-14 08:57:39 +02:00
M66B
028ce2c455 No is not always no 2019-06-14 08:46:33 +02:00
M66B
fc2840ae30 Removed dependency on UIDPLUS 2019-06-14 08:31:37 +02:00
M66B
5e54bc0c6a Export/import account/folder order 2019-06-13 22:08:43 +02:00
M66B
3c2878c1bf Fixed flagging uid 2019-06-13 22:08:19 +02:00
M66B
5002f8c518 Reset identity on send 2019-06-13 21:17:50 +02:00
M66B
a35c9c6b7a Process message on add message 2019-06-13 20:46:53 +02:00
M66B
2e2fe58cc1 Erase identity on pre move 2019-06-13 17:53:56 +02:00
M66B
4471e08341 Prefer matching synchronizing identities 2019-06-13 16:19:10 +02:00
M66B
e98c5aea27 Show messages with disabled identity dimmed 2019-06-13 14:42:08 +02:00
M66B
02e58770e9 Connection failure is unrecoverable 2019-06-13 14:31:46 +02:00
M66B
e7b9eb56b9 Fixed/improved operation counts 2019-06-13 14:10:37 +02:00
M66B
a59a14a279 1.540 release 2019-06-13 13:21:38 +02:00
M66B
419e4af5ea Small improvement 2019-06-13 13:20:51 +02:00
M66B
492c12483a Crowdin sync 2019-06-13 13:18:12 +02:00
M66B
58d0fd071c Small color fixes 2019-06-13 13:01:07 +02:00
M66B
f014d820da Removed reload from save identity 2019-06-13 12:48:30 +02:00
M66B
35e24c590e Run rules on added messages 2019-06-13 12:41:50 +02:00
M66B
28de9b2b27 Skip counting body tasks 2019-06-13 11:59:35 +02:00
M66B
c4d6f7a89f Revert "Replaced body spinner by hourglass"
This reverts commit b1e2080fa8.
2019-06-13 11:49:54 +02:00
M66B
b1e2080fa8 Replaced body spinner by hourglass 2019-06-13 11:48:36 +02:00
M66B
4bc0776483 Cleanup 2019-06-13 11:38:52 +02:00
M66B
e6e2c6e5bb Run rules on browsed messages 2019-06-13 10:40:09 +02:00
M66B
27cb003558 Improved linkify 2019-06-13 10:20:45 +02:00
M66B
196d204c9b Added forground services types 2019-06-13 08:13:43 +02:00
M66B
ff11c6a9e8 Refactoring 2019-06-13 08:10:47 +02:00
M66B
a7ea56b5a0 Added option to resolve connectivity 2019-06-13 08:09:01 +02:00
M66B
c86ad5339a Small improvement 2019-06-12 22:22:17 +02:00
M66B
3287ce719b Improved displaying no messages 2019-06-12 22:21:55 +02:00
M66B
d169fbd395 Updated texts 2019-06-12 21:38:06 +02:00
M66B
4072367812 Fixed marking seen on moving thread by rule 2019-06-12 19:50:44 +02:00
M66B
805a8cc559 1.539 release 2019-06-12 19:36:00 +02:00
M66B
0b9fa94bb1 Crowdin sync 2019-06-12 19:35:37 +02:00
M66B
d2c39d6404 Press 'back' again to exit 2019-06-12 18:56:38 +02:00
M66B
3b273a2d4a Removed descendant focusability 2019-06-12 18:30:05 +02:00
M66B
58a45aff3f Merge hour glass and spinner 2019-06-12 18:26:15 +02:00
M66B
91f0daface Fixed scroll on select 2019-06-12 17:45:51 +02:00
M66B
119d1fedf3 Merge setup and options 2019-06-12 16:17:03 +02:00
M66B
1f07ecc025 Default init number of days to keep 2019-06-12 14:45:21 +02:00
M66B
5a2a330574 1.538 release 2019-06-10 08:59:34 +02:00
M66B
93465ee5e1 Crowdin sync 2019-06-10 08:59:14 +02:00
M66B
396fba1c95 Reset boundary 2019-06-10 08:38:56 +02:00
M66B
e107cee4e5 Changed send channel priority 2019-06-10 08:22:09 +02:00
M66B
87d8792683 Show drafts icon to move outbox message to drafts 2019-06-09 21:30:18 +02:00
M66B
13f3181d08 Added seen and thread options to move rule 2019-06-09 21:25:56 +02:00
M66B
4578065562 Added send notification channel 2019-06-09 20:51:02 +02:00
M66B
2827e8abeb Small fix 2019-06-09 18:45:33 +02:00
M66B
d1ec0dce7e 1.537 release 2019-06-09 17:22:17 +02:00
M66B
44c5455bba Crowdin sync 2019-06-09 17:22:00 +02:00
M66B
29728faaa9 Fixed log level 2019-06-09 15:51:24 +02:00
M66B
5ffd56869a Imporved List-Post header parsing 2019-06-09 15:48:44 +02:00
M66B
cf35d058ad Added menu to execute rule on existing messages 2019-06-09 14:48:43 +02:00
M66B
ddf6e3ce35 1.536 release 2019-06-09 13:22:33 +02:00
M66B
d56ff5ffde Improved error reporting 2019-06-09 13:21:56 +02:00
M66B
9382b98a63 Show hourglass when busy 2019-06-09 13:13:11 +02:00
M66B
ed29b6e29b 1.535 release 2019-06-09 11:54:48 +02:00
M66B
f464c14b81 Crowdin sync 2019-06-09 11:54:31 +02:00
M66B
26eb0c1f6d Show splash window after 1500 ms 2019-06-09 11:51:03 +02:00
M66B
794f1840a7 Shortcut has accounts 2019-06-09 11:22:50 +02:00
M66B
16b1552bc2 Autofix notification security exception 2019-06-09 10:37:51 +02:00
M66B
9f5c62f534 Cleanup 2019-06-09 10:36:29 +02:00
M66B
07e2d839f5 Small improvement 2019-06-09 10:07:56 +02:00
M66B
5738841280 Delete local browsed messages which are deleted from the server 2019-06-09 10:07:25 +02:00
M66B
ec1871acf2 Reduce error reporting 2019-06-09 09:43:51 +02:00
M66B
1b90faeac8 Prevent crash 2019-06-09 09:28:16 +02:00
M66B
beebc2b852 Moved FAB more down
To make room for more menu items
2019-06-09 09:09:36 +02:00
M66B
35e9614136 Cleanup 2019-06-09 08:25:07 +02:00
M66B
c069e3051c Updated FAQ 2019-06-09 08:22:26 +02:00
M66B
5f41c4eae0 Report issue on XDA 2019-06-08 21:50:17 +02:00
M66B
452ad0525e Made uuid selectable 2019-06-08 21:43:10 +02:00
M66B
52f3ec4c95 1.534 release 2019-06-08 17:02:56 +02:00
M66B
244e0114d8 Crowdin sync 2019-06-08 17:01:21 +02:00
M66B
0c3f4d1e39 Added setting to disable folder list sync 2019-06-08 16:33:25 +02:00
M66B
78162151f6 No guarantee 2019-06-08 16:06:22 +02:00
M66B
631badaace Updated libraries 2019-06-08 13:43:08 +02:00
M66B
12cf106305 Updated billing library 2019-06-08 13:35:24 +02:00
M66B
3639e70450 Updated FAQ 2019-06-08 10:51:13 +02:00
M66B
83c37d5b81 1.533 release 2019-06-08 10:06:16 +02:00
M66B
bac89846a4 Crowdin sync 2019-06-08 10:05:51 +02:00
M66B
9a9ebeb79c Use account id as group id 2019-06-08 09:56:28 +02:00
M66B
183bb1b7a9 Refactored export/import notification channels 2019-06-08 09:34:29 +02:00
M66B
de37f8aabf Updated FAQ 2019-06-08 08:32:11 +02:00
M66B
1e9f979364 Small improvement 2019-06-07 20:13:35 +02:00
M66B
f5132de1f4 Revert "Reduce number of invalidations"
This reverts commit 845eff88b4.
2019-06-07 18:33:48 +02:00
M66B
57a9d0ae54 Hide expander when no childs at all 2019-06-07 18:25:32 +02:00
M66B
46ae47b5c9 Map rule action parameters on import 2019-06-07 17:45:41 +02:00
M66B
d70517cdfe Cleanup 2019-06-07 17:32:30 +02:00
M66B
b4fbf80141 Fixed unified inbox folders with child folders 2019-06-07 17:08:57 +02:00
M66B
845eff88b4 Reduce number of invalidations 2019-06-07 17:01:06 +02:00
M66B
01bfae0ddd 1.532 release 2019-06-07 14:29:48 +02:00
M66B
94b3590118 Request legacy external storage
For compatibility with files shared by outdated apps
2019-06-07 14:21:49 +02:00
M66B
3e56a09074 Refactoring 2019-06-07 14:18:32 +02:00
M66B
df96a02c32 Small improvement 2019-06-07 13:49:27 +02:00
M66B
b35713c6b1 Forever ends now 2019-06-07 12:32:51 +02:00
M66B
72c7e3df08 Limit number of progress updates 2019-06-07 12:19:13 +02:00
M66B
f77cf6c029 Mark sent messages in conversations 2019-06-07 11:24:48 +02:00
M66B
d784a0e494 1.531 release 2019-06-07 10:38:04 +02:00
M66B
badd2879c4 Small improvement 2019-06-07 10:36:36 +02:00
M66B
8134333444 Crowdin sync 2019-06-07 10:35:49 +02:00
M66B
c167024212 Limit number of ROOM tasks 2019-06-07 10:33:19 +02:00
M66B
9d1be95154 Prevent invalidation of paged messages by folder updates 2019-06-07 10:14:44 +02:00
M66B
2784ea389c Limit initial load size 2019-06-07 09:53:29 +02:00
M66B
c5a3a91221 Updated libraries 2019-06-07 08:57:45 +02:00
M66B
8815838685 Use default diff executor 2019-06-07 08:30:34 +02:00
M66B
9c7399c9e7 Run ROOM queries/transaction at foreground priority 2019-06-07 08:10:58 +02:00
M66B
3205db6798 Set background thread name 2019-06-07 08:07:40 +02:00
M66B
537bf56951 Default enable notification channel lights 2019-06-07 07:58:41 +02:00
M66B
3dbbff1fb5 Suppress organization if undetermined 2019-06-07 07:51:08 +02:00
M66B
70568c0e1c 1.530 release 2019-06-06 11:49:02 +02:00
M66B
45731d619f Crowdin sync 2019-06-06 11:48:38 +02:00
M66B
c68ca3fa4c Workaround wrongly encoded mailto links 2019-06-06 11:06:51 +02:00
M66B
fe4e7ebeeb Target Android Q 2019-06-06 10:53:47 +02:00
M66B
fb575b3a53 Reset attachment download on all errors 2019-06-06 10:39:56 +02:00
M66B
db931c6f6f Added text 2019-06-06 10:25:59 +02:00
M66B
85c79f8c89 Added FAQ 2019-06-06 10:19:03 +02:00
M66B
12f6fb2f37 Fixed collapsing/expanding folder selector 2019-06-06 10:14:49 +02:00
M66B
3ac99725fd 1.529 release 2019-06-05 14:28:00 +02:00
M66B
1b21064a77 Crowdin sync 2019-06-05 14:27:38 +02:00
M66B
9a9a55b3bf Handle add without message ID 2019-06-05 14:12:10 +02:00
M66B
bb9a634a07 Check if attachment upload was canceled 2019-06-05 13:48:40 +02:00
M66B
1a08fe8058 Cleanup 2019-06-05 13:13:54 +02:00
M66B
343d687753 Updated text 2019-06-05 12:04:57 +02:00
M66B
581097d4ff Added get organization for email addresses 2019-06-05 10:51:39 +02:00
M66B
4ca69a0bd8 Autolink email addresses 2019-06-05 10:46:35 +02:00
M66B
31679a9560 Switched to AUTOLINK_WEB_URL 2019-06-05 09:51:49 +02:00
M66B
e172a02d48 Updated FAQ 2019-06-05 09:47:41 +02:00
M66B
1e85c2ac37 Fixed visibility preview 2019-06-05 09:40:39 +02:00
M66B
c063e40ff8 Crowdin sync 2019-06-05 08:04:27 +02:00
M66B
1fc5fc387b Hide preview on expanding message 2019-06-05 07:59:34 +02:00
M66B
b7a0987c52 Optimize folder processing 2019-06-05 07:46:06 +02:00
M66B
a3658a869c Removed folder hiding 2019-06-05 07:30:11 +02:00
M66B
ffa7a406f0 Attempt to fix UTF-8 text sent as US-ASCII 2019-06-04 18:55:13 +02:00
M66B
d0d4d15899 Refactoring 2019-06-04 18:35:51 +02:00
M66B
d5eca31bc3 Skip recognized domain names before 'at' sign 2019-06-04 17:43:51 +02:00
M66B
273f377789 Fixed parsing List-Post header 2019-06-04 17:04:01 +02:00
M66B
0fe803156a Skip reporting notification security exception 2019-06-04 16:20:38 +02:00
M66B
e7581e9827 Added hint text 2019-06-02 10:34:31 +02:00
M66B
60dd38d822 Small improvement 2019-06-02 09:16:21 +02:00
M66B
3592bea8a8 1.528 release 2019-06-02 09:02:37 +02:00
M66B
03cedc80d5 Crowdin sync 2019-06-02 09:02:13 +02:00
M66B
5ed2a43633 Added settings to enable sync kept removed 2019-06-02 08:57:45 +02:00
M66B
ef9f68d222 Convert exception 2019-06-02 07:58:43 +02:00
M66B
cc87d33cd7 Empty trash: skip local messages 2019-06-02 07:58:23 +02:00
M66B
75be113b33 Fixed IAB flow 2019-06-02 07:57:29 +02:00
M66B
a54ca0247b Show snoozed conversations with new messages 2019-06-01 21:01:48 +02:00
M66B
d48a87f094 Check for empty UTF8 decoded subject 2019-06-01 19:28:51 +02:00
M66B
194045c92c Prevent cast exception 2019-06-01 19:02:39 +02:00
M66B
dbb1cc1303 Updated FAQ 2019-06-01 16:57:04 +02:00
M66B
d03d87ca4d Fixed exception type 2019-06-01 16:19:24 +02:00
M66B
a312f6ce4d Crowdin sync 2019-06-01 16:10:00 +02:00
M66B
bb81d3c4c0 1.527 release 2019-06-01 15:59:29 +02:00
M66B
04a2a12546 Prevent crash 2019-06-01 15:58:09 +02:00
M66B
f439458899 Crowdin sync 2019-06-01 15:53:29 +02:00
M66B
3b6e4390da Extra checks 2019-06-01 15:49:13 +02:00
M66B
31d5010bb2 Cleanup 2019-06-01 15:37:31 +02:00
M66B
74004c0829 Added snooze rule 2019-06-01 15:27:21 +02:00
M66B
401b2d723a Allow trying pro features as far as possible 2019-06-01 14:38:44 +02:00
M66B
1845fe7a4d Filter/clarify exceptions 2019-06-01 13:21:13 +02:00
M66B
5164637b5a Updated FAQ 2019-06-01 10:31:34 +02:00
M66B
032d12d731 1.526 release 2019-06-01 10:02:37 +02:00
M66B
2cbe22c350 Prevent crash 2019-06-01 10:02:12 +02:00
M66B
3819c5dea7 Crowdin sync 2019-06-01 09:59:53 +02:00
M66B
31e66fb8d8 Updated material components to version 1.1.0-alpha07
This is an attempt to fix disappearing icons in the bottom navigation bars
2019-06-01 09:54:48 +02:00
M66B
af2ea12835 Added hint 2019-06-01 09:25:41 +02:00
M66B
3a7e3936e0 Separate Bugsnag development 2019-06-01 08:50:13 +02:00
M66B
6fa14c93e7 Auto prolong oneshot 2019-06-01 08:44:46 +02:00
M66B
0816a5deb1 Cleanup 2019-06-01 08:04:36 +02:00
M66B
5ba764f010 Small fix 2019-06-01 08:03:12 +02:00
M66B
a0301e65cc Increased one shot duration to 2 minutes 2019-06-01 08:02:15 +02:00
M66B
467a77a66d Removed 30 min poll
It will only use extra battery power without much benefits
2019-06-01 07:55:14 +02:00
M66B
0791c8d47b Setup Bugsnag always 2019-05-31 19:31:28 +02:00
M66B
c6a3da5a58 Prevent loop 2019-05-31 19:19:57 +02:00
M66B
e5edb6b230 Log purchase time 2019-05-31 18:14:20 +02:00
M66B
696f9a7a82 Small improvement 2019-05-31 14:42:41 +02:00
M66B
a29d8d97cc Increase chuck size from 25 to 500
Dovecot users: set imap_max_line_length to something sensible
2019-05-31 12:43:25 +02:00
M66B
f3a3ec12fa 1.525 release 2019-05-31 10:28:40 +02:00
M66B
75b5bfce87 Crowdin sync 2019-05-31 10:28:20 +02:00
M66B
c1ae5bfa6b Cleanup 2019-05-31 10:22:15 +02:00
M66B
ea385e9788 Fixed expanded state 2019-05-31 09:37:28 +02:00
M66B
fd8e202e59 Added logging 2019-05-31 09:05:26 +02:00
M66B
125f708286 Updated ROOM to version 2.1.0-rc01 2019-05-31 08:34:17 +02:00
M66B
201ed53ac1 Sync on folder save only when enabled 2019-05-31 08:33:48 +02:00
M66B
d681b09e58 Fixed hiding/collapsing child folders 2019-05-31 08:32:20 +02:00
M66B
c57d60bfc1 Small improvement, refactoring 2019-05-31 08:14:06 +02:00
M66B
2cb3ab0994 Revert "Always show images in original message"
This reverts commit ef411bb255.
2019-05-31 08:06:40 +02:00
M66B
30b1330ce2 Small improvement 2019-05-31 08:01:16 +02:00
M66B
0b96f5aff4 1.524 release 2019-05-30 20:38:37 +02:00
M66B
ef411bb255 Always show images in original message 2019-05-30 20:35:57 +02:00
M66B
65c84c7b46 Removed inline original view 2019-05-30 20:32:29 +02:00
M66B
0d7332b614 Delay inflating message body 2019-05-30 19:46:58 +02:00
M66B
35514effd8 Reverted to ROOM 2.0.0 2019-05-30 17:59:24 +02:00
M66B
6024db8670 Removed image size 2019-05-30 16:47:57 +02:00
M66B
50eaabd59e 1.523 release 2019-05-30 16:33:20 +02:00
M66B
b17855ab7a Fixed list performance 2019-05-30 15:46:00 +02:00
M66B
91084fbb31 Refactoring 2019-05-30 15:33:15 +02:00
M66B
9d56de35ef Use background executor for paged message list 2019-05-30 14:05:29 +02:00
M66B
b4680dcab9 Use cached thread pool for database work 2019-05-30 13:21:45 +02:00
M66B
116e48acda Suppress deprecation of traditional database transactions 2019-05-30 12:57:57 +02:00
M66B
27416553c8 Updated ROOM 2019-05-30 12:50:07 +02:00
M66B
2f253dac28 1.522 release 2019-05-30 11:35:38 +02:00
M66B
110a1cda94 Crowdin sync 2019-05-30 11:34:18 +02:00
M66B
ff0b4a2dc4 Fixed typeface warnings on some Samsung devices 2019-05-30 10:05:54 +02:00
M66B
16bea88b20 Fixed navigation 2019-05-30 09:19:54 +02:00
M66B
9b4649d3d8 Updated billing client to version 2.0.0 2019-05-30 08:45:03 +02:00
M66B
2ce181cf2e Removed flagged messages sync hint 2019-05-29 19:04:31 +02:00
M66B
c91b6403c5 Updated Bugsnag 2019-05-29 17:08:28 +02:00
M66B
a263c384c0 Reduce memory usage 2019-05-29 16:55:36 +02:00
M66B
6b36d47666 Updated accent color 2019-05-29 16:47:11 +02:00
M66B
025bbc219c Prevent reply to self 2019-05-29 16:03:00 +02:00
M66B
763c4201f2 Using matched identity as from address 2019-05-29 15:47:01 +02:00
M66B
0af96a930f Crowdin sync 2019-05-29 15:15:27 +02:00
M66B
a4705aa7b8 Fixed typo 2019-05-29 15:12:07 +02:00
M66B
89c1c8a57c Simplify reply all 2019-05-29 15:04:18 +02:00
M66B
62af22cb51 Show folder type icon for non inboxes in unified inbox 2019-05-29 14:56:09 +02:00
M66B
11937d07b6 Small fix 2019-05-29 14:30:56 +02:00
M66B
1dd91b878d 1.521 release 2019-05-29 14:13:53 +02:00
M66B
66cd64a7ad Crowdin sync 2019-05-29 14:13:35 +02:00
M66B
5b704136ee Fixed reply all (3) 2019-05-29 14:09:54 +02:00
M66B
b6fd02234b 1.520 release 2019-05-29 12:14:15 +02:00
M66B
bbd6126e79 Crowdin sync 2019-05-29 12:13:56 +02:00
M66B
6f87a5d943 Fixed reply all (2) 2019-05-29 12:13:49 +02:00
M66B
a63c8ae5e4 1.519 release 2019-05-29 10:55:21 +02:00
M66B
c8b6d71d36 Crowdin sync 2019-05-29 10:55:02 +02:00
M66B
931a4b96ec Check for servers at top domain 2019-05-29 10:45:24 +02:00
M66B
fe615f022a Skip importing channels without group 2019-05-29 10:16:38 +02:00
M66B
0d2bd37772 Attempt to fix workers 2019-05-29 10:02:10 +02:00
M66B
822515623c Show PGP signature status 2019-05-29 09:57:24 +02:00
M66B
8b4f9246c7 Simplification 2019-05-29 09:16:01 +02:00
M66B
5f83e3351a Fixed reply all 2019-05-29 09:14:56 +02:00
M66B
74e99f6a3f Added uuid to debug info for cross referencing 2019-05-29 08:51:20 +02:00
M66B
59ce40f9a4 Show debug info in debug version 2019-05-29 08:51:08 +02:00
M66B
f9784e14fd Not our fault 2019-05-29 08:50:48 +02:00
M66B
4d8a61618e Prevent crash 2019-05-28 19:24:12 +02:00
M66B
b420e83992 Prevent crash on saving attachments 2019-05-28 19:18:57 +02:00
M66B
55c65eca7f Prevent crash 2019-05-28 19:14:04 +02:00
M66B
95b709e757 Build unique list of recipients for reply menus 2019-05-28 19:03:14 +02:00
M66B
e50abcbcea Group channels by account 2019-05-28 17:05:52 +02:00
M66B
c596293b61 Fixed restoring wrong notification channel group 2019-05-28 16:40:14 +02:00
M66B
d1df1379cd 1.518 release 2019-05-28 16:04:16 +02:00
M66B
2cb958097b Crowdin sync 2019-05-28 16:01:40 +02:00
M66B
0b5f544ab7 Fixed folder display name 2019-05-28 16:01:22 +02:00
M66B
fa562dc391 Prevent crash 2019-05-28 13:47:55 +02:00
M66B
5715f3972a 1.517 release 2019-05-28 13:45:42 +02:00
M66B
299fd56c0c Crowdin sync 2019-05-28 13:44:43 +02:00
M66B
d5d373f835 Simplify folder names 2019-05-28 13:42:19 +02:00
M66B
dc7879ccc1 Disable list caching on less than 32 MB 2019-05-28 11:57:38 +02:00
M66B
133d30de56 Updated icon 2019-05-28 09:45:40 +02:00
M66B
93991a7696 1.516 release 2019-05-28 08:30:53 +02:00
M66B
3be7a4d1a2 Prevent auto linking of email addresses 2019-05-28 08:18:59 +02:00
M66B
378896195e Expand sender and subject on expanding message 2019-05-28 08:07:09 +02:00
M66B
376e4ff344 Fixed reloading draft text 2019-05-28 07:48:09 +02:00
M66B
709506deb2 Prevent crash 2019-05-27 12:41:44 +02:00
M66B
7f5d9293a4 1.515 release 2019-05-27 11:24:20 +02:00
M66B
58f5fa5787 Crowdin sync 2019-05-27 11:23:22 +02:00
M66B
6e56003ece Disabled Bugsnag orientation listener 2019-05-27 11:11:41 +02:00
M66B
0ed3bbfd20 Updated FAQ 2019-05-27 08:51:56 +02:00
M66B
74d24c9825 1.514 release 2019-05-26 19:39:10 +02:00
M66B
5f8f5cba49 Crowdin sync 2019-05-26 19:38:53 +02:00
M66B
0da464588a Faster folder sync 2019-05-26 14:50:34 +02:00
M66B
6e581c9e92 1.513 release 2019-05-26 13:22:45 +02:00
M66B
2178d6d38a Crowdin sync 2019-05-26 13:21:23 +02:00
M66B
0f0e1935fc Allow selecting child folder of excluded target folder 2019-05-26 13:15:56 +02:00
M66B
ef3a03ca87 Show uuid when debug mode enabled 2019-05-26 12:41:41 +02:00
M66B
89c45410da 1.512 release 2019-05-26 10:56:26 +02:00
M66B
13c2961af3 Crowdin sync 2019-05-26 10:56:08 +02:00
M66B
e98c71334b Hide message action labels in compact view 2019-05-26 10:09:56 +02:00
M66B
34e93cdb48 Improved layout 2019-05-26 09:57:47 +02:00
M66B
dca3447446 Collapsible folder selector 2019-05-26 09:16:13 +02:00
M66B
9dd73d9d46 Simplify collapsible folders 2019-05-25 22:09:15 +02:00
M66B
a3316c1aae Reduce memory usage 2019-05-25 20:04:53 +02:00
M66B
6f8893ebec 1.511 release 2019-05-25 18:39:02 +02:00
M66B
169d1b81d2 Crowdin sync 2019-05-25 18:38:53 +02:00
M66B
2137c272dd Allow moving spam directly to inbox 2019-05-25 17:40:58 +02:00
M66B
7988a82e33 Added quick folder actions 2019-05-25 17:14:51 +02:00
M66B
109ab986c8 Hide debug info 2019-05-25 17:01:04 +02:00
M66B
ba9dfc9aea Added hint 2019-05-25 16:57:16 +02:00
M66B
583b584935 Delete not found folders 2019-05-25 16:45:46 +02:00
M66B
c671c1b9d9 Small improvement 2019-05-25 16:32:08 +02:00
M66B
35ed2e5187 Fixed recreating folder 2019-05-25 16:21:25 +02:00
M66B
7497acf5ed Made calendar replies pro 2019-05-25 16:12:53 +02:00
M66B
7f82cdbe7d Always auto scroll up conversations 2019-05-25 16:11:12 +02:00
M66B
a475aac754 Fixed finding attachments 2019-05-25 16:10:59 +02:00
M66B
b85f41fbc7 Fix wrongly formatted CIDs 2019-05-24 09:19:22 +02:00
M66B
320a196fba Added logging 2019-05-24 09:14:38 +02:00
M66B
75812c06d1 Improved layout 2019-05-24 09:10:20 +02:00
M66B
bc652cc8af 1.510 release 2019-05-23 19:26:23 +02:00
M66B
0544f4252a Disable sybscribed only by default 2019-05-23 19:26:07 +02:00
M66B
eebadf6309 1.509 release 2019-05-23 19:15:44 +02:00
M66B
366183d035 Prevent crash 2019-05-23 19:14:24 +02:00
M66B
eba867dd93 Legacy 2019-05-23 19:05:09 +02:00
M66B
7103571086 1.508 release 2019-05-23 18:56:31 +02:00
M66B
b3a65772d1 Merge branch 'master' of github.com:M66B/open-source-email 2019-05-23 18:52:45 +02:00
M66B
654fb4f4b4 Crowdin sync 2019-05-23 18:52:18 +02:00
M66B
7db95e7e2c Removed cache lists options
List caching is managed automatically
2019-05-23 18:52:08 +02:00
M66B
ebeeeab4d4 Removed cache lists options
List caching is managed automatically
2019-05-23 18:45:11 +02:00
M66B
6add12cfb9 Simplification: removed message size calculation 2019-05-23 18:33:02 +02:00
M66B
3a23df76a0 Reduced fetch size to prevent OOM 2019-05-23 18:27:43 +02:00
M66B
8965d69b99 By default list subscribed folders only 2019-05-23 18:13:03 +02:00
M66B
84b06ef4e4 1.507 release 2019-05-23 17:00:17 +02:00
M66B
39fbf1508d Crowdin sync 2019-05-23 17:00:07 +02:00
M66B
cf189511e3 Serialize account operations, increase IMAP connection pool size to 2 2019-05-23 16:38:53 +02:00
M66B
5f24921230 Reduce logging 2019-05-23 16:20:20 +02:00
M66B
b13a7d62e2 Prevent crash 2019-05-23 15:57:44 +02:00
M66B
6703cb80ac Simplification
This reverts commit 104fa6f8c4.
2019-05-23 15:32:02 +02:00
M66B
a7ce90c2db Refactoring 2019-05-23 12:23:53 +02:00
M66B
455d7a534d 1.506 release 2019-05-23 11:53:52 +02:00
M66B
320b8c1a26 Prevent crash 2019-05-23 11:53:24 +02:00
M66B
8a7d72e624 1.505 release 2019-05-23 11:42:31 +02:00
M66B
e88fb35f3b Crowdin sync 2019-05-23 11:41:27 +02:00
M66B
960c5ae580 Small improvement 2019-05-23 11:38:46 +02:00
M66B
9894f0baf5 Set random Bugsnag client ID 2019-05-23 11:08:04 +02:00
M66B
70726f8c91 Prevent crash 2019-05-23 10:43:49 +02:00
M66B
104fa6f8c4 Copy custom IMAP host to custom SMTP host 2019-05-23 10:37:53 +02:00
M66B
6ff3264e63 Fixed disabling swipe refresh 2019-05-23 10:28:23 +02:00
M66B
50bcc79a0d Added option to synchronize subscribed folders only 2019-05-23 10:02:43 +02:00
M66B
e5d91ac619 Take hidden folders into account for collapsible 2019-05-23 08:40:35 +02:00
M66B
6e5aba2535 Simplification 2019-05-23 07:09:13 +02:00
M66B
a2365a7ba7 Fixed recipients count for reply 2019-05-22 22:44:36 +02:00
M66B
0515ae9ea9 Simplification 2019-05-22 22:36:35 +02:00
M66B
017f7e2b0a Always allow moving to drafts from outbox 2019-05-22 22:26:06 +02:00
M66B
1d4b953daa Refactoring 2019-05-22 21:34:35 +02:00
M66B
c74f32f1e7 Rule fixes/improvements 2019-05-22 21:26:02 +02:00
M66B
7c4c9119ef Updated FAQ 2019-05-22 21:23:22 +02:00
M66B
5ac4ae818b Small improvements 2019-05-22 19:49:02 +02:00
395 changed files with 59890 additions and 12731 deletions

20
ATTRIBUTION.md Normal file
View File

@@ -0,0 +1,20 @@
## Attribution
FairEmail uses:
* [JavaMail](https://projects.eclipse.org/projects/ee4j.javamail). Copyright (c) 1997-2018 Oracle® and/or its affiliates. All rights reserved. [GPLv2+CE license](https://javaee.github.io/javamail/JavaMail-License).
* [jsoup](https://jsoup.org/). Copyright © 2009 - 2017 Jonathan Hedley. [MIT license](https://jsoup.org/license).
* [Android Support Library](https://developer.android.com/tools/support-library/). Copyright (C) 2011 The Android Open Source Project. [Apache license 2.0](https://android.googlesource.com/platform/frameworks/support/+/master/LICENSE.txt).
* [Android Architecture Components](https://developer.android.com/topic/libraries/architecture/). Copyright 2018 The Android Open Source Project, Inc. [Apache license 2.0](https://github.com/googlesamples/android-architecture-components/blob/master/LICENSE).
* [colorpicker](https://android.googlesource.com/platform/frameworks/opt/colorpicker). Copyright (C) 2013 The Android Open Source Project. [Apache license 2.0](https://android.googlesource.com/platform/frameworks/opt/colorpicker/+/master/src/com/android/colorpicker/ColorPickerDialog.java).
* [dnsjava](http://www.xbill.org/dnsjava/). Copyright (c) 1998-2011, Brian Wellington. [BSD License](https://sourceforge.net/p/dnsjava/code/HEAD/tree/trunk/LICENSE).
* [OpenPGP API library](https://github.com/open-keychain/openpgp-api). Copyright (C) 2014-2015 Dominik Schürmann. [Apache License 2.0](https://github.com/open-keychain/openpgp-api/blob/master/LICENSE).
* [Android SQLite support library](https://github.com/requery/sqlite-android). Copyright (C) 2017 requery.io. [Apache License 2.0](https://github.com/requery/sqlite-android/blob/master/LICENSE).
* [App shortcut icon generator](https://romannurik.github.io/AndroidAssetStudio/icons-app-shortcut.html). Copyright ???. [Apache License 2.0](https://github.com/romannurik/AndroidAssetStudio/blob/master/LICENSE).
* [Mozilla ISPDB](https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration#ISPDB). *Free to use for any client.*
* [ShortcutBadger](https://github.com/leolin310148/ShortcutBadger). Copyright 2014 Leo Lin. [Apache license 2.0](https://github.com/leolin310148/ShortcutBadger/blob/master/LICENSE).
* [Bugsnag exception reporter for Android](https://github.com/bugsnag/bugsnag-android). Copyright (c) 2012 Bugsnag. [MIT License](https://github.com/bugsnag/bugsnag-android/blob/master/LICENSE.txt).
* [biweekly](https://github.com/mangstadt/biweekly). Copyright (c) 2013-2018, Michael Angstadt. [BSD 2-Clause](https://github.com/mangstadt/biweekly/blob/master/LICENSE).
* [PhotoView](https://github.com/chrisbanes/PhotoView). Copyright 2018 Chris Banes. [Apache License 2.0](https://github.com/chrisbanes/PhotoView/blob/master/LICENSE).
* [ReLinker](https://github.com/KeepSafe/ReLinker). Copyright 2015 - 2016 KeepSafe Software, Inc. [Apache License 2.0](https://github.com/KeepSafe/ReLinker/blob/master/LICENSE).
* [Markwon](https://github.com/noties/Markwon). Copyright 2019 Dimitry Ivanov. [Apache License 2.0](https://github.com/noties/Markwon/blob/master/LICENSE).

518
FAQ.md
View File

@@ -23,13 +23,15 @@ For authorizing:
## Known problems
* ~~A [bug in Android](https://issuetracker.google.com/issues/78495471) lets FairEmail occasionally crash on long pressing or swiping.~~
* ~~A [bug in Android 5.1 and 6](https://issuetracker.google.com/issues/37054851) causes apps to sometimes show a wrong time format. Toggling the Android setting *Use 24-hour format* might temporarily solve the issue.~~
* ~~A [bug in Google Drive](https://issuetracker.google.com/issues/126362828) causes files exported to Google Drive to be empty.~~
* "*... Couldn't read row ...*" causes sometimes a crash. This could be caused by a bug in the [Room Persistence Library](https://developer.android.com/topic/libraries/architecture/room) but more likely indicates a corrupt database.
* A [bug in Android](https://issuetracker.google.com/issues/119872129) "*... Bad notification posted ...*" lets FairEmail crash on some devices after updating FairEmail and tapping on a notification.
* A [bug in Android](https://issuetracker.google.com/issues/62427912) "*... ActivityRecord not found for ...*" sometimes causes a crash after updating FairEmail.
* Encryption with [YubiKey](https://www.yubico.com/) results into an infinite loop. FairEmail follows the latest version of the [OpenKeychain API](https://github.com/open-keychain/openpgp-api), so this is likely being caused by an external bug.
* ~~A [bug in Android 5.1 and 6](https://issuetracker.google.com/issues/37054851) causes apps to sometimes show a wrong time format. Toggling the Android setting *Use 24-hour format* might temporarily solve the issue. A workaround was added.~~
* ~~A [bug in Google Drive](https://issuetracker.google.com/issues/126362828) causes files exported to Google Drive to be empty. Google has fixed this.~~
* ~~Encryption with [YubiKey](https://www.yubico.com/) results into an infinite loop. FairEmail follows the latest version of the [OpenKeychain API](https://github.com/open-keychain/openpgp-api), so this is likely being caused by an external bug. This seems not be happening anymore.~~
* ~~A [bug in AndroidX](https://issuetracker.google.com/issues/78495471) lets FairEmail occasionally crash on long pressing or swiping. Google has fixed this.~~
* ~~A [bug in AndroidX ROOM](https://issuetracker.google.com/issues/138441698) causes sometimes a crash with "*... Exception while computing database live data ... Couldn't read row ...*". A workaround was added.~~
* A [bug in Android](https://issuetracker.google.com/issues/119872129) lets FairEmail crash with "*... Bad notification posted ...*" on some devices once after updating FairEmail and tapping on a notification.
* A [bug in Android](https://issuetracker.google.com/issues/62427912) sometimes causes a crash with "*... ActivityRecord not found for ...*" after updating FairEmail. Reinstalling ([source](https://stackoverflow.com/questions/46309428/android-activitythread-reportsizeconfigurations-causes-app-to-freeze-with-black)) might fix the problem.
* A bug in Nova Launcher or Android 5.x lets FairEmail crash with a *java.lang.StackOverflowError* when Nova Launcher has access to the accessibility service.
* The folder selector sometimes shows no folders for yet unknown reasons.
## Planned features
@@ -40,13 +42,19 @@ For authorizing:
* ~~Notification settings per folder~~
* ~~Select local images for signatures~~ (this will not be added because it requires image file management and because images are not shown by default in most email clients anyway)
* ~~Show messages matched by a rule~~
* ~~[ManageSieve](https://tools.ietf.org/html/rfc5804)~~ (there are no maintained Java libraries with a suitable license and without dependencies and besides that, FairEmail has its own filter rules)
* ~~Search for messages with/without attachments~~ (this cannot be added because IMAP doesn't support searching for attachments)
* ~~Search for a folder~~ (filtering a hierarchical folder list is problematic)
* ~~Search suggestions~~
* ~~[Autocrypt Setup Message](https://autocrypt.org/autocrypt-spec-1.0.0.pdf) (section 4.4)~~ (IMO it is not a good idea to let an email client handle sensitive encryption keys for an exceptional use case while OpenKeychain can export keys too)
* ~~Generic unified folders~~
* ~~New message notification schedules per account~~ (implemented by added a time condition to rules, so messages can be snoozed in selected periods)
Anything on this list is in random order and *might* be added in the near future.
## Frequently requested features
* *Widget to read messages*: widgets can have limited user interaction only, so a widget to read conversations would not be very convenient. Moreover, it would be not very useful to duplicate functions which are already available in the app.
* *Design*: the design is based on many discussions and if you like you can discuss about it [in this forum](https://forum.xda-developers.com/android/apps-games/source-email-t3824168) too. See below for the design goals.
* *ActiveSync*: using the Exchange ActiveSync protocol requires [a license](https://en.wikipedia.org/wiki/Exchange_ActiveSync#Licensing), so this cannot be added.
@@ -56,8 +64,8 @@ Fonts, sizes, colors, etc should be material design wherever possible.
Since FairEmail is meant to be privacy friendly, the following will not be added:
* Open links without confirmation
* Show original messages from unknown senders without confirmation, see also [this FAQ](#user-content-faq35)
* Opening links without confirmation
* Showing original messages without confirmation, see also [this FAQ](#user-content-faq35)
* Direct file/folder access: for security/privacy reasons (other) apps should use the [Storage Access Framework](https://developer.android.com/guide/topics/providers/document-provider), see also [this FAQ](#user-content-faq49)
Confirmation is just one tap, which is just a small price for better privacy.
@@ -77,9 +85,9 @@ FairEmail follows all the best practices for an email client as decribed in [thi
* [(4) How can I use an invalid security certificate / IMAP STARTTLS / an empty password?](#user-content-faq4)
* [(5) How can I customize the message view?](#user-content-faq5)
* [(6) How can I login to Gmail / G suite?](#user-content-faq6)
* [(7) Why are sent messages not appearing sent folder?](#user-content-faq7)
* [(7) Why are sent messages not appearing (directly) in the sent folder?](#user-content-faq7)
* [(8) Can I use a Microsoft Exchange account?](#user-content-faq8)
* [(9) What are identities?](#user-content-faq9)
* [(9) What are identities / how do I add an alias?](#user-content-faq9)
* [(11) Why is POP not supported?](#user-content-faq11)
* [~~(10) What does 'UIDPLUS not supported' mean?~~](#user-content-faq10)
* [(12) How does encryption/decryption work?](#user-content-faq12)
@@ -92,8 +100,8 @@ FairEmail follows all the best practices for an email client as decribed in [thi
* [(19) Why are the pro features so expensive?](#user-content-faq19)
* [(20) Can I get a refund?](#user-content-faq20)
* [(21) How do I enable the notification light?](#user-content-faq21)
* [(22) What do 'Couldn't connect to host', 'Connection refused', 'Network unreachable', 'Software caused connection abort', 'Connection reset by peer' and 'Read timed out' mean?](#user-content-faq22)
* [(23) Why do I get 'Too many simultaneous connections' ?](#user-content-faq23)
* [(22) What do 'Couldn't connect to host', 'Connection refused', 'Network unreachable', 'Software caused connection abort', 'Connection reset by peer', 'Read timed out' and 'Broken pipe' mean?](#user-content-faq22)
* [(23) Why do I get 'Too many simultaneous connections' or 'Maximum number of connections ... exceeded' ?](#user-content-faq23)
* [(24) What is browse messages on the server?](#user-content-faq24)
* [(25) Why can't I select/open/save an image, attachment or a file?](#user-content-faq25)
* [(26) Can I help to translate FairEmail in my own language?](#user-content-faq26)
@@ -124,7 +132,7 @@ FairEmail follows all the best practices for an email client as decribed in [thi
* [(51) How are folders sorted?](#user-content-faq51)
* [(52) Why does it take some time to reconnect to an account?](#user-content-faq52)
* [(53) Can you stick the message action bar to the top/bottom?](#user-content-faq53)
* [(54) How do I use a namespace prefix?](#user-content-faq54)
* [~~(54) How do I use a namespace prefix?~~](#user-content-faq54)
* [(55) How can I mark all messages as read / move or delete all messages?](#user-content-faq55)
* [(56) Can you add support for JMAP?](#user-content-faq56)
* [(57) Can I use HTML in signatures?](#user-content-faq57)
@@ -134,7 +142,7 @@ FairEmail follows all the best practices for an email client as decribed in [thi
* [(61) Why are some messages shown dimmed?](#user-content-faq61)
* [(62) Which authentication methods are supported?](#user-content-faq62)
* [(63) How are images resized for displaying on screens?](#user-content-faq63)
* [(64) Can you add custom actions for swipe left/right?](#user-content-faq64)
* [~~(64) Can you add custom actions for swipe left/right?~~](#user-content-faq64)
* [(65) Why are some attachments shown dimmed?](#user-content-faq65)
* [(66) Is FairEmail available in the Google Play Family Library?](#user-content-faq66)
* [(67) How can I snooze conversations?](#user-content-faq67)
@@ -156,7 +164,7 @@ FairEmail follows all the best practices for an email client as decribed in [thi
* [(83) What does 'User is authenticated but not connected' mean?](#user-content-faq83)
* [(84) What are local contacts for?](#user-content-faq84)
* [(85) Why is an identity not available?](#user-content-faq85)
* [(86) What are 'extra privacy features'?](#user-content-faq86)
* [~~(86) What are 'extra privacy features'?~~](#user-content-faq86)
* [(87) What does 'invalid credentials' mean?](#user-content-faq87)
* [(88) How can I use a Yahoo! account?](#user-content-faq88)
* [(89) How can I send plain text only messages?](#user-content-faq89)
@@ -178,6 +186,20 @@ FairEmail follows all the best practices for an email client as decribed in [thi
* [(105) How does the roam-like-at-home option work?](#user-content-faq105)
* [(106) Which launchers can show the number of new messages?](#user-content-faq106)
* [(107) How do I used colored stars?](#user-content-faq107)
* [(108) Can you add permanently delete messages from any folder?](#user-content-faq108)
* [~~(109) Why is 'select account' available in official versions only?~~](#user-content-faq109)
* [(110) Why are (some) messages empty and/or attachments corrupted?](#user-content-faq110)
* [(111) Can you add OAuth authentication?](#user-content-faq111)
* [(112) Which email provider do you recommend?](#user-content-faq112)
* [(113) How does biometric authentication work?](#user-content-faq113)
* [(114) Can you add an import for the settings of other email apps?](#user-content-faq114)
* [(115) Can you add email address chips?](#user-content-faq114)
* [(116) How can I show images in messages from trusted senders by default?](#user-content-faq116)
* [(117) Can you help me restore my purchase?](#user-content-faq117)
* [(118) What does 'Remove tracking parameters' exactly?](#user-content-faq118)
* [(119) Can you add colors to the unified inbox widget?](#user-content-faq119)
* [(120) Why are new message notifications not removed on opening the app?](#user-content-faq120)
* [(121) How are messages grouped into a conversation?](#user-content-faq121)
[I have another question.](#support)
@@ -189,13 +211,12 @@ The following Android permissions are needed:
* *have full network access* (INTERNET): to send and receive email
* *view network connections* (ACCESS_NETWORK_STATE): to monitor internet connectivity changes
* *run at startup* (RECEIVE_BOOT_COMPLETED): to start monitoring on device start
* *in-app billing* (BILLING): to allow in-app purchases
* *foreground service* (FOREGROUND_SERVICE): to run a foreground service on Android 9 Pie and later, see also the next question
* *prevent device from sleeping* (WAKE_LOCK): to keep the device awake while synchronizing messages
* *in-app billing* (BILLING): to allow in-app purchases
* Optional: *read your contacts* (READ_CONTACTS): to autocomplete addresses and to show photos
* Optional: *find accounts on the device* (GET_ACCOUNTS): to use [OAuth](https://en.wikipedia.org/wiki/OAuth) instead of passwords
* Optional: *read the contents of your SD card* (READ_EXTERNAL_STORAGE): to accept files from other, outdated apps, see also [this FAQ](#user-content-faq49)
* Android 5.1 Lollipop and before: *use accounts on the device* (USE_CREDENTIALS): needed to select accounts (not used/needed on later Android versions)
* Optional: *use fingerprint hardware* (USE_FINGERPRINT) and use *biometric hardware* (USE_BIOMETRIC): to use biometric authentication
The following permissions are needed to show the count of unread messages as a badge (see also [this FAQ](#user-content-faq106)):
@@ -272,6 +293,9 @@ Invalid security certificate (*Can't verify identity of server*): you should try
because invalid security certificates are insecure and allow [man-in-the-middle attacks](https://en.wikipedia.org/wiki/Man-in-the-middle_attack).
If money is an obstacle, you can get free security certificates from [Lets Encrypt](https://letsencrypt.org).
Note that older Android versions might not recognize newer certification authorities like Lets Encrypt causing connections to be considered insecure,
see also [here](https://developer.android.com/training/articles/security-ssl).
IMAP STARTTLS: the EFF [writes](https://www.eff.org/nl/deeplinks/2018/06/announcing-starttls-everywhere-securing-hop-hop-email-delivery):
"*Additionally, even if you configure STARTTLS perfectly and use a valid certificate, theres still no guarantee your communication will be encrypted.*"
@@ -280,6 +304,8 @@ Empty password: your username is likely easily guessed, so this is very insecure
If you still want to use an invalid security certificate, IMAP STARTTLS or an empty password,
you'll need to enable insecure connections in the account and/or identity settings.
Connections without encryption (either SSL or STARTTLS) are not supported because this is very insecure.
<br />
<a name="faq5"></a>
@@ -290,7 +316,7 @@ In the three dot overflow menu you can enable or disable or select:
* *text size*: for three different font sizes
* *compact view*: for more condensed message items and a smaller message text font
In the display section of the advanced options you can enable or disable:
In the display section of the settings you can enable or disable:
* *Unified inbox*: to disable the unified inbox and to list the folders selected for the unified inbox instead
* *Group by date*: show date header above messages with the same date
@@ -309,7 +335,7 @@ In the display section of the advanced options you can enable or disable:
Note that messages can be previewed only when the message text was downloaded.
Larger message texts are not downloaded by default on metered (generally mobile) networks.
You can change this in the advanced options.
You can change this in the settings.
If the list of addresses is long, you can collapse the addresses section with the *less* icon at the top of the addresses section.
@@ -326,34 +352,58 @@ Unfortunately, it is impossible to make everybody happy and adding lots of setti
<a name="faq6"></a>
**(6) How can I login to Gmail / G suite?**
Preferably select Gmail as provider and select an account on your device.
To use a Gmail/G suite account,
you can either enable access for "less secure apps" and use your account password
or enable two factor authentication and use an app specific password.
If you want/need to use a username/password instead of selecting an account, you'll need to enable access for "less secure" apps,
see [here](https://support.google.com/accounts/answer/6010255) for Google's instructions
Note that an app specific password is required when two factor authentication is enabled.
**Enable "Less secure apps"**
See [here](https://support.google.com/accounts/answer/6010255) about how to enable "less secure apps"
or go [directy to the setting](https://www.google.com/settings/security/lesssecureapps).
You can solve the error *535-5.7.8 Username and Password not accepted* by enabling "less secure" apps.
If you use your account username/password, you might get the alert "*Please log in via your web browser*".
This security measure can for example be triggered when too many IP addresses were used in a too short time or when you are using a VPN.
You can prevent this by using an app specific password.
If you use multiple Gmail accounts, make sure you change the "less secure apps" setting of the right account(s).
To login to Gmail / G suite you'll sometimes need an app specific password, for example when two factor authentication is enabled.
See here for instructions: [https://support.google.com/accounts/answer/185833](https://support.google.com/accounts/answer/185833).
Be aware that you need to leave the "less secure apps" settings screen by using the back arrow to apply the setting.
If this doesn't work, see here for more solutions: [https://support.google.com/mail/accounts/answer/78754](https://support.google.com/mail/accounts/answer/78754)
When "less secure apps" is not enabled,
you'll get the error *Authentication failed - invalid credentials* for accounts (IMAP)
and *Username and Password not accepted* for identities (SMTP).
**App specific password**
See [here](https://support.google.com/accounts/answer/185833) about how to generate an app specific password.
You might get the alert "*Please log in via your web browser*".
This happens when Google considers the network that connects you to the internet (this could be a VPN) to to be unsafe.
This can be prevented by using an app specific password.
See [here](https://support.google.com/mail/answer/7126229) for Google's instructions
and [here](https://support.google.com/mail/accounts/answer/78754) for troubleshooting.
See [this FAQ](#user-content-faq111) about why OAuth is not being used.
<br />
<a name="faq7"></a>
**(7) Why are sent messages not appearing sent folder?**
**(7) Why are sent messages not appearing (directly) in the sent folder?**
Sent messages are normally added to the sent folder as soon as your provider adds the messages to the sent folder.
Sent messages are normally moved from the outbox to the sent folder as soon as your provider adds sent messages to the sent folder.
This requires a sent folder to be selected in the account settings and the sent folder to be set to synchronizing.
If this doesn't happen, your provider might not keep track of sent messages or you might be using an SMTP server not related to the provider.
In these cases you can enable the advanced identity setting *Store sent messages* to workaround this.
Note that FairEmail will automatically add sent messages to the sent folder when performing a full synchronize,
which happens when reconnecting or if you synchronize manually.
Some providers do not keep track of sent messages or the used SMTP server might not be related to the provider.
In these cases FairEmail will automatically add sent messages to the sent folder on synchronizing the sent folder, which will happen after a message have been sent.
Note that this will result in extra internet traffic.
~~If this doesn't happen, your provider might not keep track of sent messages or you might be using an SMTP server not related to the provider.~~
~~In these cases you can enable the advanced identity setting *Store sent messages* to let FairEmail add sent messages to the sent folder right after sending a message.~~
~~Note that enabling this setting might result in duplicate messages if your provider adds sent messages to the sent folder too.~~
~~Also beware that enabling this setting will result in extra data usage, especially when when sending messages with large attachments.~~
~~If sent messages in the outbox are not found in the sent folder on a full synchronize, they will be moved from the outbox to the sent folder too.~~
~~A full synchronize happens when reconnecting to the server or when synchronizing periodically or manually.~~
~~You'll likely want to enable the advanced setting *Store sent messages* instead to move messages to the sent folder sooner.~~
<br />
@@ -361,14 +411,14 @@ which happens when reconnecting or if you synchronize manually.
**(8) Can I use a Microsoft Exchange account?**
You can use a Microsoft Exchange account if it is accessible via IMAP.
See here for more information: [https://support.office.com/en-us/article/what-is-a-microsoft-exchange-account-47f000aa-c2bf-48ac-9bc2-83e5c6036793](https://support.office.com/en-us/article/what-is-a-microsoft-exchange-account-47f000aa-c2bf-48ac-9bc2-83e5c6036793)
See [here](https://support.office.com/en-us/article/what-is-a-microsoft-exchange-account-47f000aa-c2bf-48ac-9bc2-83e5c6036793) for more information.
Please see [here](#frequently-requested-features) about ActiveSync support.
<br />
<a name="faq9"></a>
**(9) What are identities?**
**(9) What are identities / how do I add an alias?**
Identities represent email addresses you are sending *from*.
@@ -412,6 +462,10 @@ First of all you need to install and configure [OpenKeychain](https://f-droid.or
To encrypt and send a message just check the menu *Encrypt* and the message will be encrypted on sending.
Similarly, to decrypt a received message, just select the menu *Decrypt* in the expanded message view.
The first time you send an encrypted message you might be asked for a sign key.
FairEmail will automatically store the sign key ID in the selected identity for the next time.
If you need to reset the sign key, just save the identity to clear the sign key ID again.
You can enable *Encrypt by default* in the identity settings, which replaces *Send* by *Encrypt and send*.
FairEmail will send the [Autocrypt](https://autocrypt.org/) headers for other email clients.
@@ -449,7 +503,7 @@ You can start searching for messages on sender, recipient, subject, keyword or m
You can also search from any app by select *Search email* in the copy/paste popup menu.
Messages will be searched on the device first (all accounts, all folders).
There will be an action button with a cloud download icon at the bottom to search on the server.
There will be an action button with a search again icon at the bottom to search on the server.
When the search was started in a specific folder,
the same folder will be searched in on the server,
else you can select which folder to search in on the server.
@@ -471,10 +525,7 @@ Searching messages is a pro feature.
To use Outlook or Hotmail with two factor authentication enabled, you need to create an app password.
See [here](https://support.microsoft.com/en-us/help/12409/microsoft-account-app-passwords-two-step-verification) for the details.
Unfortunately, Outlook and Hotmail do not properly support OAuth for IMAP/SMTP connections, so there is no other way.
Technical background: [MSAL](https://github.com/AzureAD/microsoft-authentication-library-for-android) is supported for business accounts only
and OAuth requires embedding a client secret in the app.
See [here](https://support.office.com/en-us/article/pop-imap-and-smtp-settings-for-outlook-com-d088b986-291d-42b8-9564-9c414e2aa040) for Microsoft's instructions.
<br />
@@ -485,6 +536,9 @@ The message header and message body are fetched separately from the server.
The message text of larger messages is not being pre-fetched on metered connections and need to be fetched on opening the message.
The message text will keep loading if there is no connection to the account, see also the next question.
You can check the account and folder list for the account and folder state (see the legend for the meaning of the icons)
and the operation list accessible via the main navigation menu for pending operations (see [this FAQ](#user-content-faq3) for the meaning of the operations).
In the advanced settings you can set the maximum size for automatically downloading of messages on metered connections.
Mobile connections are almost always metered and some (paid) Wi-Fi hotspots are too.
@@ -538,6 +592,7 @@ The right question is "*why are there so many taxes and fees?*":
* VAT: 25 % (depending on your country)
* Google fee: 30 %
* Income tax: 50 %
* <sub>Paypal fee: 5-10 % depending on the country/amount</sub>
So, what is left for the developer is just a fraction of what you pay.
@@ -546,6 +601,9 @@ Note that only some convenience and advanced features need to be purchased which
Also note that most free apps will appear not to be sustainable in the end, whereas FairEmail is properly maintained and supported,
and that free apps may have a catch, like sending privacy sensitive information to the internet.
I have been working on FairEmail almost every day for about a year, so I think the price is more than reasonable.
For this reason there won't be discounts either.
<br />
<a name="faq20"></a>
@@ -569,11 +627,13 @@ Before Android 8 Oreo: there is an advanced option in the setup for this.
Android 8 Oreo and later: see [here](https://developer.android.com/training/notify-user/channels) about how to configure notification channels.
You can use the button *Manage notifications* in the setup to directly go to the Android notification settings.
Note that apps cannot change notification settings, including the notification light setting, on Android 8 Oreo and later anymore.
Apps designed and targeting older Android versions might still be able to control the contents of notifications,
but such apps cannot be updated anymore and recent Android versions will show a warning such apps are outdated.
<br />
<a name="faq22"></a>
**(22) What do 'Couldn't connect to host', 'Connection refused', 'Network unreachable', 'Software caused connection abort', 'Connection reset by peer' and 'Read timed out' mean?**
**(22) What do 'Couldn't connect to host', 'Connection refused', 'Network unreachable', 'Software caused connection abort', 'Connection reset by peer', 'Read timed out' and 'Broken pipe' mean?**
The messages *... Couldn't connect to host ...*, *... Connection refused ...* or *... Network unreachable ...*
mean that FairEmail was not able to connect to the email server.
@@ -582,9 +642,11 @@ The message *... Software caused connection abort ...*
means that the email server or something between FairEmail and the email server actively terminated an existing connection.
This can for example happen when connectivity was abruptly lost. A typical example is turning on flight mode.
The message *... Connection reset by peer ...* means that the email server actively terminated an existing connection.
The message *... Connection reset by peer ...* or *... Broken pipe ...* means that the email server actively terminated an existing connection.
The message *... Read timed out ...* means that the email server is not responding anymore or that the internet connction is bad.
The message *... Read timed out ...* means that the email server is not responding anymore or that the internet connection is bad.
See [here](https://linux.die.net/man/3/connect) for what error codes like EHOSTUNREACH and ETIMEDOUT mean.
Possible causes are:
@@ -592,6 +654,7 @@ Possible causes are:
* The host name or port number is invalid
* The are problems with the internet connection
* The email server is refusing to accept connections
* The email server is refusing to accept a message, for example because it is too large or contains unacceptable links
* There are too many connections to the server, see also the next question
If you are using a VPN, the VPN provider might block the connection because it is too aggressively trying to prevent spam.
@@ -602,7 +665,7 @@ This delay will be doubled after each failed attempt to prevent draining the bat
<br />
<a name="faq23"></a>
**(23) Why do I get 'Too many simultaneous connections' ?**
**(23) Why do I get 'Too many simultaneous connections' or 'Maximum number of connections ... exceeded' ?**
The message *Too many simultaneous connections* is sent by the email server
when there are too many folder connections for the same email account at the same time.
@@ -681,14 +744,16 @@ for example to set a specific notification sound or to show notifications on the
FairEmail has the following notification channels:
* Service: used for the foreground service notification, see also [this FAQ](#user-content-faq2)
* Service: used for the notification of the synchronize service, see also [this FAQ](#user-content-faq2)
* Send: used for the notification of the send service
* Notifications: used for new message notifications
* Warning: used for warning notifications
* Error: used for error notifications
See [here](https://developer.android.com/guide/topics/ui/notifiers/notifications#ManageChannels) for details on notification channels.
In short: tap on the notification channel name to access the channel settings.
On Android before Android 8 Oreo you can set the notification sound in the advanced options.
On Android before Android 8 Oreo you can set the notification sound in the settings.
See [this FAQ](#user-content-faq21) if your device has a notification light.
@@ -776,11 +841,13 @@ Setting identity colors is a pro feature.
Viewing remotely stored images (see also [this FAQ](#user-content-faq27)) might not only tell the sender that you have seen the message,
but will also leak your IP address.
Opening attachments or viewing an original message might execute scripts,
Opening attachments or viewing an original message might load remote content and execute scripts,
that might not only cause privacy sensitive information to leak, but can also be a security risk.
Note that your contacts could unknowingly send malicious messages if they got infected with malware.
FairEmail formats messages again causing messages to look different from the original, but also uncovering phishing links.
The Gmail app shows images by default by downloading the images through a Google proxy server.
Since the images are downloaded from the source server [in real-time](https://blog.filippo.io/how-the-new-gmail-image-proxy-works-and-what-this-means-for-you/),
this is even less secure because Google is involved too without providing much benefit.
@@ -833,12 +900,6 @@ If you cannot solve the problem with the purchase, you will have to contact Goog
<a name="faq39"></a>
**(39) How can I reduce the battery usage of FairEmail?**
First of all, update to [the latest version](https://github.com/M66B/open-source-email/releases/).
It is inevitable that synchronizing messages will use battery power because it requires network access and accessing the messages database.
Reconnecting to an email server will use extra battery power, so an unstable internet connection will result in extra battery usage.
Recent Android versions by default report *app usage* as a percentage in the Android battery settings screen.
Confusingly, *app usage* is not the same as *battery usage*.
The app usage will be very high because FairEmail is using a foreground service which is considered as constant app usage by Android.
@@ -847,10 +908,17 @@ The real battery usage can be seen by using the three dot overflow menu *Show fu
As a rule of thumb the battery usage should be below or in any case not be much higher than *Mobile network standby*.
If this isn't the case, please let me know.
It is inevitable that synchronizing messages will use battery power because it requires network access and accessing the messages database.
Reconnecting to an email server will use extra battery power, so an unstable internet connection will result in extra battery usage.
In this case you might want to synchronize periodically, for example each hour, instead of continuously.
Note that polling frequently (more than every 30-60 minutes) will likely use more battery power than synchronizing always
because connection to the server and comparing the local and remotes messages are expensive operations.
Most of the battery usage, not considering viewing messages, is due to synchronization (receiving and sending) of messages.
So, to reduce the battery usage, set the number of days to synchronize message for to a lower value,
especially if there are a lot of recent messages in a folder.
Long press a folder name in the folders list to access this setting.
Long press a folder name in the folders list and select *Edit properties* to access this setting.
If you have at least once a day internet connectivity, it is sufficient to synchronize messages just for one day.
@@ -858,7 +926,7 @@ Note that you can set the number of days to *keep* messages for to a higher numb
You could for example initially synchronize messages for a large number of days and after this has been completed
reduce the number of days to synchronize messages for, but leave the number of days to keep messages for.
Starred messages will always be synchronized,
In the receive settings you can enable to always synchronize starred messages,
which will allow you to keep older messages around while synchronizing messages for a limited number of days.
Disabling the folder option *Automatically download message texts and attachments*
@@ -866,7 +934,24 @@ will result in less network traffic and thus less battery usage.
You could disable this option for example for the sent folder and the archive.
Synchronizing messages at night is mostly not useful, so you can save on battery usage by not synchronizing at night.
In the advanced options you can set a schedule for message synchronization (this is a pro feature). See also [this FAQ](#user-content-faq78).
In the settings you can select a schedule for message synchronization (this is a pro feature). See also [this FAQ](#user-content-faq78).
FairEmail will by default synchronize the folder list on each connection.
Since folders are mostly not created, renamed and deleted very often, you can save some network and battery usage by disabling this in the receive settings.
FairEmail will by default check if old messages were deleted from the server on each connection.
If you don't mind that old messages that were delete from the server are still visible in FairEmail, you can save some network and battery usage by disabling this in the receive settings.
Some providers don't follow the IMAP standard and don't keep connections open long enough, forcing FairEmail to reconnect often, causing extra battery usage.
You can inspect the *Log* via the main navigation menu to check if there are frequent reconnects.
You can workaround this by lowering the keep-alive interval in the advanced account settings to for example 9 or 15 minutes.
Some providers send every two minutes something like '*Still there*' resulting in network traffic and your device to wake up and causing unnecessary extra battery usage.
You can inspect the *Log* via the main navigation menu to check if your provider is doing this.
If your provider is using [Dovecot](https://www.dovecot.org/) as IMAP server,
you could ask your provider to change the [imap_idle_notify_interval](https://wiki.dovecot.org/Timeouts) setting to a higher value or better yet, to disable this.
If your provider is not able or willing to change/disable this, you should consider to switch to periodically instead of continuous synchronization.
You can change this in the receive settings.
If you got the message *This provider does not support push messages* while configuring an account,
consider switching to a modern provider which supports push messages (IMAP IDLE) to reduce battery usage.
@@ -874,6 +959,8 @@ consider switching to a modern provider which supports push messages (IMAP IDLE)
If your device has an [AMOLED](https://en.wikipedia.org/wiki/AMOLED) screen,
you can save battery usage while viewing messages by switching to the black theme.
Finally, make sure you are using [the latest version](https://github.com/M66B/FairEmail/releases/).
<br />
<a name="faq40"></a>
@@ -881,9 +968,8 @@ you can save battery usage while viewing messages by switching to the black them
You can reduce the network usage basically in the same way as reducing battery usage, see the previous question for suggestions.
Additionally, you can set FairEmail to download small messages and attachments on a metered (mobile, paid) connection only
or let FairEmail connect via unmetered connections only.
These advanced settings are accessible via *Setup* > *Advanced options*.
By default FairEmail does not download message texts and attachments larger than 256 KiB when there is a metered (mobile or paid Wi-Fi) internet connection.
You can change this in the connection settings.
<br />
@@ -1066,17 +1152,17 @@ You can use the *Home* button to quickly go to the top of the message.
<br />
<a name="faq54"></a>
**(54) How do I use a namespace prefix?**
**~~(54) How do I use a namespace prefix?~~**
A namespace prefix is used to automatically remove the prefix providers sometimes add to folder names.
~~A namespace prefix is used to automatically remove the prefix providers sometimes add to folder names.~~
For example the Gmail spam folder is called:
~~For example the Gmail spam folder is called:~~
```
[Gmail]/Spam
```
By setting the namespace prefix to *[Gmail]* FairEmail will automatically remove *[Gmail]/* from all folder names.
~~By setting the namespace prefix to *[Gmail]* FairEmail will automatically remove *[Gmail]/* from all folder names.~~
<br />
@@ -1139,11 +1225,11 @@ but even Google's Chrome cannot handle this.
<a name="faq60"></a>
**(60) Did you know ... ?**
* Did you know that starred messages are always synchronized/kept?
* Did you know that starred messages are by default synchronized/kept? (this can be changed in the receive settings)
* Did you know that you can long press the 'write message' icon to go to the drafts folder?
* Did you know that you can long press the account name in the navigation menu to go to the inbox of that account?
* Did you know there is an advanced option to mark messages read when they are moved and that archiving and trashing is also moving?
* Did you know that you can select text (or an email address) in any app on recent Android versions and let FairEmail search for it? You'll need to set a primary account and an archive folder for this to work, so FairEmail knows where to search. There will be 'FairEmail' in the menu with copy, cut, etc.
* Did you know there is an advanced option to mark messages read when they are moved? (archiving and trashing is also moving)
* Did you know that you can select text (or an email address) in any app on recent Android versions and let FairEmail search for it?
* Did you know that FairEmail has a tablet mode? Rotate your device in landscape mode and conversation threads will be opened in a second column if there is enough screen space.
* Did you know that you can long press a reply template to create a draft message from the template?
* Did you know that you can long press, hold and swipe to select a range of messages?
@@ -1151,6 +1237,8 @@ but even Google's Chrome cannot handle this.
* Did you know that you can swipe a conversation left or right to go to the next or previous conversation?
* Did you know that you can tap on an image to see where it will be downloaded from?
* Did you know that you can long press the folder icon in the action bar to select an account?
* Did you know that you can long press the star icon in a conversation thread to set a colored star?
* Did you know that you can open the navigation drawer by swiping from the left, even when viewing a conversation?
<br />
@@ -1177,7 +1265,6 @@ The following authentication methods are supported and used in this order:
* LOGIN
* PLAIN
* NTLM (untested)
* XOAUTH2 (used when a Google account is selected)
SASL authentication methods, like CRAM-MD5, are not supported
because [JavaMail for Android](https://javaee.github.io/javamail/Android) does not support SASL authentication.
@@ -1200,21 +1287,21 @@ There is an advanced option to disable automatically resizing and to set the tar
<br />
<a name="faq64"></a>
**(64) Can you add custom actions for swipe left/right?**
**~~(64) Can you add custom actions for swipe left/right?~~**
The most natural thing to do when swiping a list entry left or right is to remove the entry from the list.
The most natural action in the context of an email app is moving the message out of the folder to another folder.
You can select the folder to move to in the account settings.
~~The most natural thing to do when swiping a list entry left or right is to remove the entry from the list.~~
~~The most natural action in the context of an email app is moving the message out of the folder to another folder.~~
~~You can select the folder to move to in the account settings.~~
Other actions, like marking messages read and snoozing messages are available via multiple selection.
You can long press a message to start multiple selection. See also [this question](#user-content-faq55).
~~Other actions, like marking messages read and snoozing messages are available via multiple selection.~~
~~You can long press a message to start multiple selection. See also [this question](#user-content-faq55).~~
Swiping left or right to mark a message read or unread is unnatural because the message first goes away and later comes back in a different shape.
Note that there is an advanced option to mark messages automatically read on moving,
which is in most cases a perfect replacement for the sequence mark read and move to some folder.
You can also mark messages read from new message notifications.
~~Swiping left or right to mark a message read or unread is unnatural because the message first goes away and later comes back in a different shape.~~
~~Note that there is an advanced option to mark messages automatically read on moving,~~
~~which is in most cases a perfect replacement for the sequence mark read and move to some folder.~~
~~You can also mark messages read from new message notifications.~~
If you want to read a message later, you can hide it until a specific time by using the *snooze* menu.
~~If you want to read a message later, you can hide it until a specific time by using the *snooze* menu.~~
<br />
@@ -1245,6 +1332,8 @@ Select the time the conversation(s) should snooze and confirm by tapping OK.
The conversations will be hidden for the selected time and shown again afterwards.
You will receive a new message notification as reminder.
It is also possible to snooze messages with [a rule](#user-content-faq71).
You can show snoozed messages by using the *Snoozed* item in the three dot overflow menu.
You can tap on the small snooze icon to see until when a conversation is snoozed.
@@ -1271,7 +1360,7 @@ By selecting a zero snooze duration you can cancel snoozing.
The message list is automatically scrolled up when navigating from a new message notification or after a manual refresh.
Always automatically scrolling up on arrival of new messages would interfere with your own scrolling,
but if you like you can enable this in the advanced options.
but if you like you can enable this in the settings.
<br />
@@ -1283,11 +1372,14 @@ When navigation to a conversation one message will be expanded if:
* There is just one message in the conversation
* There is exactly one unread message in the conversation
There is one exception: the message body text was not downloaded yet
and the message body text is too large to download automatically on a metered connection.
There is one exception: the message was not downloaded yet
and the message is too large to download automatically on a metered (mobile) connection.
You can set or disable the maximum message size on the 'connection' settings tab.
Duplicate (archived) messages, trashed messages and draft messages are not counted.
Messages will automatically be marked read on expanding.
<br />
<a name="faq71"></a>
@@ -1302,6 +1394,8 @@ You can disable a rule and you can stop processing other rules after a rule has
All the conditions of a rule need to be true for a filter rule to be executed.
Conditions are optional, but there needs to be at least one condition.
You can use multiple rules, possibly with a *stop processing*, for an *or* condition.
Matching is not case sensitive, unless you use [regular expressions](https://en.wikipedia.org/wiki/Regular_expression).
In the *more* message menu there is an item to create a rule for a received message with the most common conditions filled in.
@@ -1310,7 +1404,10 @@ You can select one of these actions to apply to matching messages:
* Mark as read
* Mark as unread
* Snooze
* Add star
* Move
* Copy
* Reply template
* Automation
@@ -1400,10 +1497,10 @@ so there is little room for performance improvements.
<a name="faq78"></a>
**(78) How do I use schedules?**
In the advanced options you can enable scheduling and set the time to turn synchronizing automatically on and off.
In the settingss you can enable scheduling and set the time to turn synchronizing automatically on and off.
An end time equal to or earlier than the start time is considered to be 24 hours later.
Turning FairEmail on or off, for example by using [a quick settings tile](#user-content-faq30), will not turn scheduling off.
This means that the next schedule event will still turn FairEmail on or off.
@@ -1416,6 +1513,13 @@ You can also automate turning synchronization on and off by sending these comman
Sending these commands will automatically turn scheduling off.
It is also possible to just enable/disable one account, for example the account with the name *Gmail*:
```
(adb shell) am startservice -a eu.faircode.email.ENABLE --es account Gmail
(adb shell) am startservice -a eu.faircode.email.DISABLE --es account Gmail
```
You can automatically send commands with for example [Tasker](https://tasker.joaoapps.com/userguide/en/intents.html):
```
@@ -1425,9 +1529,23 @@ Action: eu.faircode.email.ENABLE
Target: Service
```
To enable/disable an account with the name *Gmail*:
```
Extras: account:Gmail
```
Account names are case sensitive.
Automation can be used for more advanced schedules,
like for example multiple synchronization periods per day or different synchronization periods for different days.
It is possible to install FairEmail in multiple user profiles, for example a personal and a work profile, and to configure FairEmail differently in each profile,
which is another possibility to have different synchronization schedules and to synchronize a different set of accounts.
It is also possible to create [rules](#user-content-faq71) with a time condition and to snooze messages until the end time of the time condition.
This way it is possible to snooze business related messages until the start of the business hours.
Scheduling is a pro feature.
<br />
@@ -1446,6 +1564,8 @@ The synchronization process will also be started to execute [operations](#user-c
for example to mark a message read, move a message or store a draft.
This is to keep the local and remote message store synchronized.
If you want to synchronize some or all folders of an account manually, just disable synchronization for the folders (but not of the account).
<br />
<a name="faq80"></a>
@@ -1519,13 +1639,12 @@ FairEmail will try to select the best identity based on the *to* address of the
<br />
<a name="faq86"></a>
**(86) What are 'extra privacy features'?**
**~~(86) What are 'extra privacy features'?~~**
The advanced option *extra privacy features* enables:
~~The advanced option *extra privacy features* enables:~~
* Looking up the owner of the IP address of a link
* Detection and removal of [tracking images](#user-content-faq82)
* Removal of [Urchin Tracking Module (UTM) parameters](https://en.wikipedia.org/wiki/UTM_parameters) from links
* ~~Looking up the owner of the IP address of a link~~
* ~~Detection and removal of [tracking images](#user-content-faq82)~~
<br />
@@ -1548,7 +1667,8 @@ You will likely need to save the associated identity again as well.
For the correct settings, see [here](https://help.yahoo.com/kb/SLN4075.html).
You might need to enable "*less secure sign in*" for "*outdated*" apps,
see [here](https://help.yahoo.com/kb/grant-temporary-access-outdated-apps-account-settings-sln27791.html).
see [here](https://help.yahoo.com/kb/grant-temporary-access-outdated-apps-account-settings-sln27791.html) for more information.
You can directly access this setting [here](https://login.yahoo.com/account/security#less-secure-apps).
Note that FairEmail is using the standard [IMAP protocol](https://en.wikipedia.org/wiki/Internet_Message_Access_Protocol), which is really not outdated.
@@ -1590,9 +1710,16 @@ This is why texts with dots are sometimes incorrectly recognized as links, which
Spam filtering, verification of the [DKIM](https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail) signature
and [SPF](https://en.wikipedia.org/wiki/Sender_Policy_Framework) authorization is a task of email servers, not of an email client.
However, FairEmail can show a small vertical warning stripe at the end of the message header
if DKIM, SPF or [DMARC](https://en.wikipedia.org/wiki/DMARC) authentication failed on the receiving server.
You can enable this in the advanced options.
However, FairEmail will show a small red warning flag
when DKIM, SPF or [DMARC](https://en.wikipedia.org/wiki/DMARC) authentication failed on the receiving server.
You can enable/disable [authentication verification](https://en.wikipedia.org/wiki/Email_authentication) in the behavior settings.
FairEmail can show a warning flag too when the domain name of the (reply) email address of the sender does not define an MX record pointing to an email server.
This can be enabled in the receive settings. Be aware that this will slow down synchronization of messages significantly.
If legitimate messages are failing authentication, you should notify the sender because this will result in a high risk of messages ending up in the spam folder.
Moreover, without proper authentication there is a risk the sender will be impersonated.
The sender might use [this tool](https://www.mail-tester.com/) to check authentication and other things.
<br />
@@ -1620,7 +1747,7 @@ instead the Storage Access Framework, available and recommended since Android 4.
If an app is listed depends on if the app implements a [document provider](https://developer.android.com/guide/topics/providers/document-provider).
Android Q will make it harder and maybe even impossible to directly access files,
see [here](https://developer.android.com/preview/privacy/scoped-storage) for more details.
see [here](https://developer.android.com/preview/privacy/scoped-storage) and [here](https://www.xda-developers.com/android-q-storage-access-framework-scoped-storage/) for more details.
<br />
@@ -1642,8 +1769,6 @@ About each four hours FairEmail runs a cleanup job that:
* Removes old local contacts
* Removes old log entries
You can see when the last cleanup was performed at the bottom of the advanced options.
Note that the cleanup job will only run when the synchronize service is active.
<br />
@@ -1703,7 +1828,7 @@ The dot is meant as an aid when swiping left/right to go to the previous/next co
<a name="faq102"></a>
**(102) How can I enable auto rotation of images?**
Images will automatically be rotated when automatic resizing of images is enabled in the advanced options (enabled by default).
Images will automatically be rotated when automatic resizing of images is enabled in the settings (enabled by default).
However, automatic rotating depends on the [Exif](https://en.wikipedia.org/wiki/Exif) information to be present and to be correct,
which is not always the case. Particularly not when taking a photo with a camara app from FairEmail.
@@ -1727,7 +1852,7 @@ Unfortunately and surprisingly, most recording apps do not seem to support this
* Error reports will help improve FairEmail
* Error reporting is optional and opt-in
* Error reporting can be enabled/disabled in the advanced options, section miscellaneous
* Error reporting can be enabled/disabled in the settings, section miscellaneous
* Error reports will automatically be sent anonymously to [Bugsnag](https://www.bugsnag.com/)
* Bugsnag for Android is [open source](https://github.com/bugsnag/bugsnag-android)
* See [here](https://docs.bugsnag.com/platforms/android/automatically-captured-data/) about what data will be sent in case of errors
@@ -1760,8 +1885,8 @@ Note that this needs to be enabled in the advance options (default enabled).
<a name="faq107"></a>
**(107) How do I use colored stars?**
You can set a colored star via the *more* message menu, via multiple selection (started by long pressing a message)
or automatically by using [rules](#user-content-faq71).
You can set a colored star via the *more* message menu, via multiple selection (started by long pressing a message),
by long pressing a star in a conversation or automatically by using [rules](#user-content-faq71).
You need to know that colored stars are not supported by the IMAP protocol and can therefore not be synchronized to an email server.
This means that colored stars will not be visible in other email clients and will be lost on downloading messages again.
@@ -1772,9 +1897,192 @@ However, not all servers support IMAP keywords and besides that there are no sta
<br />
<a name="faq108"></a>
**(108) Can you add permanently delete messages from any folder?**
When you delete messages from a folder the messages will be moved to the trash folder, so you have a chance to restore the messages.
You can permanently delete messages from the trash folder.
Permanently delete messages from other folders would defeat the purpose of the trash folder, so this will not be added.
<br />
<a name="faq109"></a>
**~~(109) Why is 'select account' available in official versions only?~~**
~~Using *select account* to select and authorize Google accounts require special permission from Google for security and privacy reasons.~~
~~This special permission can only be acquired for apps a developer manages and is responsible for.~~
~~Third party builds, like the F-Droid builds, are managed by third parties and are the responsibility of these third parties.~~
~~So, only these third parties can acquire the required permission from Google.~~
~~Since these third parties do not actually support FairEmail, they are most likely not going to request the required permission.~~
~~You can solve this in two ways:~~
* ~~Switch to the official version of FairEmail, see [here](https://github.com/M66B/FairEmail/blob/master/README.md#downloads) for the options~~
* ~~Use app specific passwords, see [this FAQ](#user-content-faq6)~~
~~Using *select account* in third party builds is not possible in recent versions anymore.~~
~~In older versions this was possible, but it will now result in the error *UNREGISTERED_ON_API_CONSOLE*.~~
<br />
<a name="faq110"></a>
**(110) Why are (some) messages empty and/or attachments corrupt?**
Empty messages and/or corrupt attachments are probably being caused by a bug in the server software.
Older Microsoft Exchange software is known to cause this problem.
Mostly you can workaround this by disabling *Partial fetch* in the advanced account settings.
After disabling this setting, you can use the message 'more' (three dots) menu to 'resync' empty messages.
Alternatively, you can *Delete local messages* by long pressing the folder(s) in the folder list and synchronize all messages again.
Disabling *Partial fetch* will result in more memory usage.
<br />
<a name="faq111"></a>
**(111) Can you add OAuth authentication?**
(X)OAuth authentication, formerly available as *Select account* for Google accounts, requires creating an online (Google, Microsoft, etc) app,
which would make authentication for many people dependent on one (developer) account, which is a bad idea.
See also [this related article](https://arstechnica.com/gadgets/2019/06/gmails-api-lockdown-will-kill-some-third-party-app-access-starting-july-15/).
Outlook and Hotmail do not properly support OAuth for IMAP/SMTP connections.
[MSAL](https://github.com/AzureAD/microsoft-authentication-library-for-android) is supported for business accounts only
and requires embedding a client secret in the app, which is not a good idea for an open source app.
See also [this FAQ](#user-content-faq6).
<br />
<a name="faq112"></a>
**(112) Which email provider do you recommend?**
Which email provider is best for you depends on your wishes/requirements.
Please see the websites of [Restore privacy](https://restoreprivacy.com/secure-email/) or [Privacy Tools](https://www.privacytools.io/providers/email/)
for a list of privacy friendly email providers with advantages and disadvantages.
<br />
<a name="faq113"></a>
**(113) How does biometric authentication work?**
If your device has a biometric sensor, for example a fingerprint sensor, you can enable/disable biometric authentication in the navigation (hamburger) menu of the setup screen.
When enabled FairEmail will require biometric authentication after a period of inactivity or after the screen has been turned off while FairEmail was running.
Activity is navigation within FairEmail, for example opening a conversation thread.
The inactivity period duration can be configured in the miscellaneous settings.
When biometric authentication is enabled new message notifications will not show any content and FairEmail won't be visible on the Android recents screen.
Biometric authentication is meant to prevent others from seeing your messages only.
FairEmail relies on device encryption for data encryption, see also [this FAQ](#user-content-faq37).
Biometric authentication is a pro feature.
<br />
<a name="faq114"></a>
**(114) Can you add an import for the settings of other email apps?**
The format of the settings files of most other email apps is not documented, so this is difficult.
Sometimes it is possible to reverse engineer the format, but as soon as the settings format changes things will break.
Also, settings are often incompatible.
For example, FairEmail has unlike most other email apps settings for the number of days to synchronize messages for
and for the number of days to keep messages for, mainly to save on battery usage.
Moreover, setting up an account/identity with the quick setup is simple, so it is not really worth the effort.
<br />
<a name="faq115"></a>
**(115) Can you add email address chips?**
Email address [chips](https://material.io/design/components/chips.html) look nice, but cannot be edited,
which is quite inconvenient when you made a typo in an email address.
Chips are not suitable for showing in a list
and since the message header in a list should look similar to the message header of the message view it is not an option to use chips for viewing messages.
Reverted [commit](https://github.com/M66B/FairEmail/commit/2c80c25b8aa75af2287f471b882ec87d5a5a5015).
<br />
<a name="faq116"></a>
**(116) How can I show images in messages from trusted senders by default?**
You can show images in messages from trusted senders by default by enabled the display setting *Automatically show images for known contacts*.
Contacts in the Android contacts list are considered to be known and trusted,
unless the contact is in the group / has the label '*Untrusted*' (case insensitive).
<br />
<a name="faq117"></a>
**(117) Can you help me restore my purchase?**
Google manages all purchases, so as developer I have little control over purchases.
So, the only thing I can do, is give some advice:
* Make sure you have an active internet connection
* Make sure you are logged in with the right Google account and that there is nothing wrong with your Google account
* Open the Play store application and wait at least a minute to give it time to synchronize with the Google servers
* Open FairEmail and navigate to the pro features screen to let FairEmail check the purchases
You can also try to clear the cache of the Play store app via the Android apps settings.
Note that:
* Purchases are stored in the Google cloud and cannot get lost
* There is no time limit on purchases, so they cannot expire
* Google does not expose details (name, e-mail, etc) about buyers to developers
* An application like FairEmail cannot select which Google account to use
<br />
<a name="faq118"></a>
**(118) What does 'Remove tracking parameters' exactly?**
Checking *Remove tracking parameters* will remove all [UTM parameters](https://en.wikipedia.org/wiki/UTM_parameters) from a link.
<br />
<a name="faq119"></a>
**(119) Can you add colors to the unified inbox widget?**
The widget is designed to look good on most home/launcher screens by making it monochrome and by using a half transparent background.
This way the widget will nicely blend in, while still being properly readable.
Adding (account) colors will cause problems with some backgrounds and will cause readability problems, which is why this won't be added.
<br />
<a name="faq120"></a>
**(120) Why are new message notifications not removed on opening the app?**
New message notifications will be removed on swiping notifications away or on marking the associated messages read.
Opening the app will not remove new message notifications.
This gives you a choice to leave new message notifications as a reminder that there are still unread messages.
On Android 7 Nougat and later new message notifications will be [grouped](https://developer.android.com/training/notify-user/group).
Tapping on the summary notification will open the unified inbox.
The summary notification can be expanded to view individual new message notifications.
Tapping on an individual new message notification will open the conversation the message it is part of.
See [this FAQ](#user-content-faq70) about when messages in a conversation will be auto expanded and marked read.
<br />
<a name="faq121"></a>
**(121) How are messages grouped into a conversation?**
By default FairEmail groups messages in conversations. This can be turned of in the display settings.
FairEmail groups messages based on the standard *Message-ID*, *In-Reply-To* and *References* headers.
FairEmail does not group on other criteria, like the subject,
because this could result in grouping unrelated messages and would be at the expense of increased battery usage.
<br />
## Support
If you have another question, want to request a feature or report a bug, you can use [this forum](https://forum.xda-developers.com/android/apps-games/source-email-t3824168).
Registration is free.
If you are a supporter of the project, you can get limited personal support by using [this form](https://contact.faircode.eu/?product=fairemail%2B).
If you are a supporter of the project, you can get limited personal support by using [this form](https://contact.faircode.eu/?product=fairemailsupport).

View File

@@ -5,12 +5,12 @@
# Feature request
* Did you check if there wasn't a similar feature request?
* Did you read [this FAQ](https://github.com/M66B/open-source-email/blob/master/FAQ.md#FAQ15)?
* Did you read [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ15)?
# Bug report
* Did you check if there wasn't a similar bug report?
* Are you using the [latest version](https://github.com/M66B/open-source-email/releases) of the app?
* Are you using the [latest version](https://github.com/M66B/FairEmail/releases) of the app?
## Expected behavior

View File

@@ -1,7 +1,3 @@
# FairEmail
*Open source, privacy friendly email app*
## Privacy policy
FairEmail **does not** collect any information.
@@ -9,14 +5,14 @@ FairEmail **does not** collect any information.
FairEmail **does not** store data on third party servers.
FairEmail **does not** require unnecessary permissions.
For more information on permissions, see [this FAQ](https://github.com/M66B/open-source-email/blob/master/FAQ.md#user-content-faq1).
For more information on permissions, see [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#user-content-faq1).
FairEmail **does not** allow other apps access to messages and attachments without your approval.
FairEmail **does** follow the recommendations of [this EFF article](https://www.eff.org/deeplinks/2019/01/stop-tracking-my-emails).
FairEmail is 100 % **open source**, see [the license](https://github.com/M66B/open-source-email/blob/master/LICENSE).
FairEmail is 100 % **open source**, see [the license](https://github.com/M66B/FairEmail/blob/master/LICENSE).
Error reporting is **opt-in**, see [here](https://github.com/M66B/open-source-email/blob/master/FAQ.md#user-content-faq104) for more information.
Error reporting is **opt-in**, see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#user-content-faq104) for more information.
Copyright &copy; 2018-2019 Marcel Bokhorst.

View File

@@ -1,4 +1,4 @@
# Important
* Did you read the [contributing section](https://github.com/M66B/open-source-email#contributing)?
* Do you agree to [the license and the copyright](https://github.com/M66B/open-source-email#license)?
* Did you read the [contributing section](https://github.com/M66B/FairEmail#contributing)?
* Do you agree to [the license and the copyright](https://github.com/M66B/FairEmail#license)?

112
README.md
View File

@@ -1,10 +1,20 @@
<img src="https://github.com/M66B/FairEmail/raw/master/images/banner_crying6.png" />
<p align="center">
<a href="#downloads">Downloads</a> &bull;
<a href="#privacy">Privacy</a> &bull;
<a href="#support">Support</a> &bull;
<a href="#license">License</a>
</p>
<img align="right" src="https://raw.githubusercontent.com/M66B/FairEmail/master/app/src/main/res/mipmap-hdpi/ic_launcher.png">
# FairEmail
![GitHub](https://img.shields.io/github/license/M66B/open-source-email.svg)
![GitHub release](https://img.shields.io/github/release/M66B/open-source-email.svg)
![GitHub commits since tagged version](https://img.shields.io/github/commits-since/M66B/open-source-email/0.1.svg)
![GitHub stars](https://img.shields.io/github/stars/M66B/open-source-email.svg?style=social)
![GitHub](https://img.shields.io/github/license/M66B/FairEmail.svg)
![GitHub release](https://img.shields.io/github/release/M66B/FairEmail.svg)
![GitHub commits since tagged version](https://img.shields.io/github/commits-since/M66B/FairEmail/0.1.svg)
![GitHub stars](https://img.shields.io/github/stars/M66B/FairEmail.svg?style=social)
*Open source, privacy friendly email app for Android*
@@ -26,35 +36,47 @@ This app starts a foreground service with a low priority status bar notification
## Main features
* 100 % [open source](https://github.com/M66B/open-source-email/blob/master/LICENSE)
* [Privacy friendly](https://github.com/M66B/open-source-email/blob/master/PRIVACY.md)
* 100 % [open source](https://github.com/M66B/FairEmail/blob/master/LICENSE)
* [Privacy friendly](https://github.com/M66B/FairEmail/blob/master/PRIVACY.md)
* Multiple accounts
* Multiple email addresses
* Unified inbox
* Flat [conversation threading](https://en.wikipedia.org/wiki/Conversation_threading)
* Two way synchronization
* Offline storage and operations
* Encryption/decryption ([OpenPGP](https://www.openpgp.org/))
* Battery friendly
* Low data usage
* Small (~6.5 MB)
* Material design
* Small (< 10 MB)
* Material design (including dark theme)
* Maintained and supported
## Privacy features
* Reformat messages to prevent [phishing](https://en.wikipedia.org/wiki/Phishing)
* Confirm showing images to prevent tracking
* Confirm opening links to prevent tracking and phishing
* Automatically recognize and disable tracking images
* Warning if messages could not be [authenticated](https://github.com/M66B/FairEmail/blob/master/FAQ.md#user-content-faq92)
## Pro features
All pro features are convenience or advanced features.
* Account/identity colors
* Colored stars ([instructions](https://github.com/M66B/open-source-email/blob/master/FAQ.md#user-content-faq107))
* Colored stars ([instructions](https://github.com/M66B/FairEmail/blob/master/FAQ.md#user-content-faq107))
* Notification settings (sounds) per account/folder/sender (requires Android 8 Oreo)
* Configurable notification actions
* Snooze messages ([instructions](https://github.com/M66B/open-source-email/blob/master/FAQ.md#user-content-faq67))
* Snooze messages ([instructions](https://github.com/M66B/FairEmail/blob/master/FAQ.md#user-content-faq67))
* Send messages after selected time
* Synchronization scheduling ([instructions](https://github.com/M66B/open-source-email/blob/master/FAQ.md#user-content-faq78))
* Synchronization scheduling ([instructions](https://github.com/M66B/FairEmail/blob/master/FAQ.md#user-content-faq78))
* Reply templates
* Accept/decline calendar invitations
* Filter rules ([instructions](https://github.com/M66B/open-source-email/blob/master/FAQ.md#user-content-faq71))
* Search on device or server ([instructions](https://github.com/M66B/open-source-email/blob/master/FAQ.md#user-content-faq13))
* Filter rules ([instructions](https://github.com/M66B/FairEmail/blob/master/FAQ.md#user-content-faq71))
* Search on device or server ([instructions](https://github.com/M66B/FairEmail/blob/master/FAQ.md#user-content-faq13))
* Keyword management
* Encryption/decryption ([OpenPGP](https://www.openpgp.org/)) ([instructions](https://github.com/M66B/open-source-email/blob/master/FAQ.md#user-content-faq12))
* Biometric authentication ([instructions](https://github.com/M66B/FairEmail/blob/master/FAQ.md#user-content-faq113))
* Unified inbox widget
* Export settings
## Simple
@@ -71,7 +93,7 @@ This app starts a foreground service with a low priority status bar notification
* Confirm opening links, images and attachments
* No special permissions required
* No advertisements
* No analytics and no tracking
* No analytics and no tracking ([error reporting](https://github.com/M66B/FairEmail/blob/master/FAQ.md#user-content-faq104) is opt-in)
* No [Google backup](https://developer.android.com/guide/topics/data/backup)
* FairEmail is an original work, not a fork or a clone
@@ -86,8 +108,15 @@ Please see [here](https://email.faircode.eu/#screenshots) for screenshots.
## Downloads
* [GitHub](https://github.com/M66B/open-source-email/releases)
* [Play store](https://play.google.com/apps/testing/eu.faircode.email)
* [GitHub](https://github.com/M66B/FairEmail/releases)
* [Play store](https://play.google.com/store/apps/details?id=eu.faircode.email)
* [Play store](https://play.google.com/apps/testing/eu.faircode.email) (test)
To download a GitHub release you might need to expand the assets section to download the [APK file](https://en.wikipedia.org/wiki/Android_application_package).
The GitHub version is being updated more often than the Play store version.
The GitHub release will automatically check for updates on GitHub.
You can turn this off in the miscellaneous settings.
Certificate fingerprints:
@@ -101,18 +130,19 @@ One line command to display certificate fingerprints:
```unzip -p fairemail.apk META-INF/CERT.RSA | keytool -printcert```
I do not hand over the signing keys of my apps to Google.
* [F-Droid](https://f-droid.org/en/packages/eu.faircode.email/) ([last build status](https://f-droid.org/wiki/page/eu.faircode.email/lastbuild))
Note that F-Droid builds new versions irregularly and you'll need the F-Droid client to get update notifications.
To get updates in a timely fashion you are advised to use the GitHub release.
The GitHub release will automatically check for updates on GitHub.
You can turn this off in the advanced options.
Because F-Droid builds and GitHub releases are signed differently, an F-Droid build needs to be uninstalled first to be able to update to a GitHub release.
## Privacy
Please see [here](https://github.com/M66B/open-source-email/blob/master/PRIVACY.md#fairemail) for the privacy policy.
Please see [here](https://github.com/M66B/FairEmail/blob/master/PRIVACY.md#fairemail) for the privacy policy.
## Compatibility
@@ -122,11 +152,11 @@ because earlier Android versions do not support notification grouping.
FairEmail will work properly on devices without any Google service installed.
See [here](https://github.com/M66B/open-source-email/blob/master/FAQ.md#known-problems) for known problems.
See [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#known-problems) for known problems.
## Support / frequently asked questions
## Support
See [here](https://github.com/M66B/open-source-email/blob/master/FAQ.md) for a list of often asked questions and about how to get support.
See [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) for a list of often asked questions and about how to get support.
## Contributing
@@ -152,45 +182,21 @@ Please note that you agree to the license below by contributing, including the c
## Attribution
FairEmail uses:
* [JavaMail](https://projects.eclipse.org/projects/ee4j.javamail). Copyright (c) 1997-2018 Oracle® and/or its affiliates. All rights reserved. [GPLv2+CE license](https://javaee.github.io/javamail/JavaMail-License).
* [jsoup](https://jsoup.org/). Copyright © 2009 - 2017 Jonathan Hedley. [MIT license](https://jsoup.org/license).
* [Android Support Library](https://developer.android.com/tools/support-library/). Copyright (C) 2011 The Android Open Source Project. [Apache license](https://android.googlesource.com/platform/frameworks/support/+/master/LICENSE.txt).
* [Android Architecture Components](https://developer.android.com/topic/libraries/architecture/). Copyright 2018 The Android Open Source Project, Inc. [Apache license](https://github.com/googlesamples/android-architecture-components/blob/master/LICENSE).
* [colorpicker](https://android.googlesource.com/platform/frameworks/opt/colorpicker). Copyright (C) 2013 The Android Open Source Project. [Apache license](https://android.googlesource.com/platform/frameworks/opt/colorpicker/+/master/src/com/android/colorpicker/ColorPickerDialog.java).
* [dnsjava](http://www.xbill.org/dnsjava/). Copyright (c) 1998-2011, Brian Wellington. [BSD License](https://sourceforge.net/p/dnsjava/code/HEAD/tree/trunk/LICENSE).
* [OpenPGP API library](https://github.com/open-keychain/openpgp-api). Copyright (C) 2014-2015 Dominik Schürmann. [Apache License 2.0](https://github.com/open-keychain/openpgp-api/blob/master/LICENSE).
* [Android SQLite support library](https://github.com/requery/sqlite-android). Copyright (C) 2017 requery.io. [Apache License 2.0](https://github.com/requery/sqlite-android/blob/master/LICENSE).
* [App shortcut icon generator](https://romannurik.github.io/AndroidAssetStudio/icons-app-shortcut.html). Copyright ???. [Apache License 2.0](https://github.com/romannurik/AndroidAssetStudio/blob/master/LICENSE).
* [Mozilla ISPDB](https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration#ISPDB). *Free to use for any client.*
* [ShortcutBadger](https://github.com/leolin310148/ShortcutBadger). Copyright 2014 Leo Lin. [Apache license](https://github.com/leolin310148/ShortcutBadger/blob/master/LICENSE).
* [PhotoView](https://github.com/chrisbanes/PhotoView). Copyright 2018 Chris Banes. [Apache License](https://github.com/chrisbanes/PhotoView/blob/master/LICENSE).
* [Bugsnag exception reporter for Android](https://github.com/bugsnag/bugsnag-android). Copyright (c) 2012 Bugsnag. [MIT License](https://github.com/bugsnag/bugsnag-android/blob/master/LICENSE.txt).
* [biweekly](https://github.com/mangstadt/biweekly). Copyright (c) 2013-2018, Michael Angstadt. [BSD 2-Clause](https://github.com/mangstadt/biweekly/blob/master/LICENSE).
See [here](https://github.com/M66B/FairEmail/blob/master/ATTRIBUTION.md) for a list of used libraries and associated license information.
Error reporting is sponsored by:
![Bugsnag Logo](/images/bugsnag_logo_navy.png)
[Bugsnag](https://www.bugsnag.com/) monitors application stability
and is used to [help improve FairEmail](https://github.com/M66B/open-source-email/blob/master/FAQ.md#user-content-faq104).
and is used to [help improve FairEmail](https://github.com/M66B/FairEmail/blob/master/FAQ.md#user-content-faq104).
## License
Copyright &copy; 2018-2019 Marcel Bokhorst. All rights reserved.
[GNU General Public License version 3](https://www.gnu.org/licenses/gpl.txt)
Copyright &copy; 2018-2019 Marcel Bokhorst. All rights reserved
> FairEmail is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
FairEmail is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
FairEmail 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with FairEmail. If not, see [https://www.gnu.org/licenses/](https://www.gnu.org/licenses/).
> FairEmail 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 General Public License for more details.

View File

@@ -7,42 +7,44 @@ The quick setup will add an account and an identity in one go for most major pro
## Requirements
An internet connection is required to setup accounts and identities.
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
If you have a Google account, preferably use *Select account* instead of entering an email address and a password.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Setup account - to receive email
## Set up account - to receive email
To add an account, tap on *Manage accounts* and tap on the orange *add* button at the bottom.
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom.
Select a provider from the list, enter the username, which is mostly your email address and enter your password.
If you use Gmail, tap *Select account* to fill in the username and password.
Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders.
After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*.
Enter the domain name, for example *gmail.com* and tap *Get settings*.
If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the host name and port number,
else check the setup instructions of your provider for the right IMAP host name and port number.
For more about this, please see [here](https://github.com/M66B/open-source-email/blob/master/FAQ.md#authorizing-accounts).
If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number,
else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS).
For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Setup identity - to send email
## Set up identity - to send email
Similarly, to add an identity, tap on *Manage identity* and tap on the orange *add* button at the bottom.
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom.
Enter the name you want to appear in de from address of the emails you send and select a linked account.
Tap *Save* to add the identity.
See [this FAQ](https://github.com/M66B/open-source-email/blob/master/FAQ.md#FAQ9) about using aliases.
If the account was configured manually, you likely need to configure the identity manually too.
Enter the domain name, for example *gmail.com* and tap *Get settings*.
If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number,
else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
@@ -51,13 +53,14 @@ If you want to lookup email addresses, have contact photos shown, etc, you'll ne
Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive email
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage.
If you want to receive new emails without delays, you should disable battery optimizations for FairEmail.
Tap *Disable battery optimizations* and follow the instructions.
## Questions
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/open-source-email/blob/master/FAQ.md).
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md)
or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -7,21 +7,51 @@ def keystoreProperties = new Properties()
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
android {
compileSdkVersion 28
compileSdkVersion 29
defaultConfig {
applicationId "eu.faircode.email"
minSdkVersion 21
targetSdkVersion 28
versionCode 504
versionName "1.504"
targetSdkVersion 29
versionCode 641
versionName "1.641"
archivesBaseName = "FairEmail-v$versionName"
// https://en.wikipedia.org/wiki/List_of_dinosaur_genera
// Hypsilophodon, Iguanodon
buildConfigField "String", "RELEASE_NAME", "\"Gastonia\""
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
}
}
// https://developer.android.com/guide/topics/graphics/vector-drawable-resources
vectorDrawables.useSupportLibrary = true
ndk {
// Bugsnag
abiFilters "armeabi-v7a", "x86", "arm64-v8a", "x86_64"
}
}
lintOptions {
disable 'MissingTranslation'
abortOnError false
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
packagingOptions {
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/README.md'
exclude 'META-INF/CHANGES'
}
signingConfigs {
release {
storeFile file(keystoreProperties['storeFile'])
@@ -56,14 +86,14 @@ android {
buildConfigField "boolean", "PLAY_STORE_RELEASE", "false"
buildConfigField "String", "INVITE_URI", "\"https://email.faircode.eu/\""
buildConfigField "String", "PRO_FEATURES_URI", "\"https://email.faircode.eu/donate/\""
buildConfigField "String", "CHANGELOG", "\"https://github.com/M66B/open-source-email/releases/\""
buildConfigField "String", "CHANGELOG", "\"https://github.com/M66B/FairEmail/releases/\""
buildConfigField "String", "GITHUB_LATEST_API", "\"https://api.github.com/repos/M66B/open-source-email/releases/latest\""
}
play_beta {
dimension "all"
buildConfigField "boolean", "BETA_RELEASE", "true"
buildConfigField "boolean", "PLAY_STORE_RELEASE", "true"
buildConfigField "String", "INVITE_URI", "\"https://play.google.com/apps/testing/eu.faircode.email\""
buildConfigField "String", "INVITE_URI", "\"https://play.google.com/store/apps/details?id=eu.faircode.email\""
buildConfigField "String", "PRO_FEATURES_URI", "\"https://email.faircode.eu/#pro\""
buildConfigField "String", "CHANGELOG", "\"\""
buildConfigField "String", "GITHUB_LATEST_API", "\"\""
@@ -79,17 +109,6 @@ android {
}
}
lintOptions {
disable 'MissingTranslation'
abortOnError false
}
packagingOptions {
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/README.md'
exclude 'META-INF/CHANGES'
}
bugsnag {
// https://docs.bugsnag.com/build-integrations/gradle/
apiKey "9d2d57476a0614974449a3ec33f2604a"
@@ -102,6 +121,34 @@ android {
}
}
task copyMarkdown(type: Copy) {
from "${rootDir}"
into "src/main/assets"
include "SETUP.md"
include "PRIVACY.md"
include "ATTRIBUTION.md"
}
preBuild.dependsOn copyMarkdown
task copySetup(type: Copy) {
from "src/main/res"
include '**/SETUP.md'
into "src/main/assets"
eachFile { file ->
file.relativePath = new RelativePath(true, "SETUP-${file.file.parentFile.name}.md")
}
includeEmptyDirs = false
}
preBuild.dependsOn copySetup
task cleanCrowdin(type: Delete) {
delete fileTree("src/main/res").matching {
include "**/SETUP.md"
}
}
preBuild.dependsOn cleanCrowdin
cleanCrowdin.shouldRunAfter copySetup
repositories {
google()
jcenter()
@@ -109,48 +156,52 @@ repositories {
maven { url "https://jitpack.io" }
}
configurations.all {
// Workaround https://issuetracker.google.com/issues/138441698
// Support @69c481c39a17d4e1e44a4eb298bb81c48f226eef
exclude group: "androidx.room", module: "room-runtime"
// Workaround https://issuetracker.google.com/issues/134685570
exclude group: "androidx.lifecycle", module: "lifecycle-livedata"
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
def appcompat_version = "1.0.2"
def recyclerview_version = "1.0.0"
def coordinatorlayout_version = "1.0.0"
def constraintlayout_version = "1.1.3"
def material_version = "1.0.0"
def appcompat_version = "1.1.0-rc01"
def recyclerview_version = "1.1.0-beta01"
def coordinatorlayout_version = "1.1.0-beta01"
def constraintlayout_version = "2.0.0-beta2"
def material_version = "1.1.0-alpha07"
def browser_version = "1.0.0"
def lifecycle_version = "2.0.0"
def room_version = "2.0.0"
def lifecycle_version = "2.1.0-rc01"
def room_version = "2.1.0"
def paging_version = "2.1.0"
def preference_version = "1.0.0"
def work_version = "2.1.0-alpha02"
def exif_version = "1.0.0"
def billingclient_version = "1.2.2"
def preference_version = "1.1.0-rc01"
def work_version = "2.2.0-rc01"
def exif_version = "1.1.0-beta01"
def biometric_version = "1.0.0-alpha04"
def billingclient_version = "2.0.2"
def javamail_version = "1.6.3"
def jsoup_version = "1.11.3"
def dnsjava_version = "2.1.8"
def jsoup_version = "1.12.1"
def dnsjava_version = "2.1.9"
def openpgp_version = "12.0"
def requery_version = "3.28.0"
def requery_version = "3.29.0"
def badge_version = "1.1.22"
def photoview_version = "2.3.0"
def bugsnag_version = "4.14.0"
def bugsnag_version = "4.17.0"
def biweekly_version = "0.6.3"
def photoview_version = "2.3.0"
def relinker_version = "1.3.1"
def markwon_version = "4.0.2"
// https://developer.android.com/jetpack/androidx/releases/
// Pin arch/core and sqlite
implementation "androidx.core:core:1.0.2"
implementation "androidx.arch.core:core-common:2.0.1"
implementation "androidx.arch.core:core-runtime:2.0.1"
implementation "androidx.sqlite:sqlite:2.0.1"
implementation "androidx.sqlite:sqlite-framework:2.0.1"
// https://mvnrepository.com/artifact/androidx.appcompat/appcompat
implementation "androidx.appcompat:appcompat:$appcompat_version"
// https://mvnrepository.com/artifact/androidx.recyclerview/recyclerview
// https://mvnrepository.com/artifact/androidx.recyclerview/recyclerview-selection
implementation "androidx.recyclerview:recyclerview:$recyclerview_version"
//implementation "androidx.recyclerview:recyclerview-selection:1.1.0-alpha05"
//implementation "androidx.recyclerview:recyclerview-selection:1.1.0-alpha06"
// https://mvnrepository.com/artifact/androidx.coordinatorlayout/coordinatorlayout
implementation "androidx.coordinatorlayout:coordinatorlayout:$coordinatorlayout_version"
@@ -159,22 +210,27 @@ dependencies {
implementation "androidx.constraintlayout:constraintlayout:$constraintlayout_version"
// https://mvnrepository.com/artifact/com.google.android.material/material
// https://github.com/material-components/material-components-android/releases
implementation "com.google.android.material:material:$material_version"
// https://mvnrepository.com/artifact/androidx.browser/browser
implementation "androidx.browser:browser:$browser_version"
// https://mvnrepository.com/artifact/androidx.lifecycle/lifecycle-runtime
// https://mvnrepository.com/artifact/androidx.lifecycle/lifecycle-livedata
// https://mvnrepository.com/artifact/androidx.lifecycle/lifecycle-livedata-core
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
// https://mvnrepository.com/artifact/androidx.room/room-runtime
implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-common:$room_version" // because of exclude
// https://mvnrepository.com/artifact/androidx.sqlite/sqlite-framework
implementation "androidx.sqlite:sqlite-framework:2.0.1" // because of exclude
annotationProcessor "androidx.room:room-compiler:$room_version"
// https://mvnrepository.com/artifact/androidx.paging/paging-runtime
implementation "androidx.paging:paging-runtime:$paging_version"
// https://mvnrepository.com/artifact/androidx.preference/preference
implementation "androidx.preference:preference:$preference_version"
@@ -184,6 +240,10 @@ dependencies {
// https://mvnrepository.com/artifact/androidx.exifinterface/exifinterface
implementation "androidx.exifinterface:exifinterface:$exif_version"
// https://mvnrepository.com/artifact/androidx.biometric/biometric
// https://developer.android.com/jetpack/androidx/releases/biometric
implementation "androidx.biometric:biometric:$biometric_version"
// https://developer.android.com/google/play/billing/billing_library_releases_notes
implementation "com.android.billingclient:billing:$billingclient_version"
@@ -212,17 +272,29 @@ dependencies {
// https://mvnrepository.com/artifact/me.leolin/ShortcutBadger
implementation "me.leolin:ShortcutBadger:$badge_version"
// https://github.com/chrisbanes/PhotoView
implementation "com.github.chrisbanes:PhotoView:$photoview_version"
// https://github.com/bugsnag/bugsnag-android
implementation "com.bugsnag:bugsnag-android:$bugsnag_version"
implementation("com.bugsnag:bugsnag-android:$bugsnag_version") {
exclude group: "com.bugsnag", module: "bugsnag-plugin-android-anr"
exclude group: "com.bugsnag", module: "bugsnag-plugin-android-ndk"
}
// https://github.com/mangstadt/biweekly
implementation("net.sf.biweekly:biweekly:$biweekly_version") {
exclude group: 'com.fasterxml.jackson.core', module: 'jackson-core'
}
// https://github.com/chrisbanes/PhotoView
implementation "com.github.chrisbanes:PhotoView:$photoview_version"
// https://github.com/KeepSafe/ReLinker
implementation "com.getkeepsafe.relinker:relinker:$relinker_version"
// https://square.github.io/leakcanary/getting_started/
//debugImplementation "com.squareup.leakcanary:leakcanary-android:2.0-alpha-3"
// https://github.com/noties/Markwon
implementation "io.noties.markwon:core:$markwon_version"
// git clone https://android.googlesource.com/platform/frameworks/opt/colorpicker
implementation project(path: ':colorpicker')
}

View File

@@ -29,9 +29,12 @@
-keep class androidx.appcompat.app.AppCompatViewInflater {<init>(...);}
-keepclassmembers class * implements android.os.Parcelable {static ** CREATOR;}
#android.os.BadParcelableException: Parcelable protocol requires a Parcelable.Creator object called CREATOR on class androidx...
-keep class androidx.work.impl.** {*;} #Due to compiling ROOM inline
-keepnames class androidx.** {*;}
#IAB
-keep class com.android.billingclient.** {*;}
-keepnames class com.android.billingclient.** {*;}
-keep class com.android.vending.billing.** {*;}
-keepnames class com.android.vending.billing.** {*;}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -6,14 +6,11 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission
android:name="android.permission.USE_CREDENTIALS"
android:maxSdkVersion="22" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<!-- https://developer.android.com/guide/topics/manifest/uses-feature-element#features-reference -->
<uses-feature
@@ -25,6 +22,9 @@
<uses-feature
android:name="android.hardware.camera"
android:required="false" />
<uses-feature
android:name="android.hardware.fingerprint"
android:required="false" />
<application
android:name=".ApplicationEx"
@@ -33,6 +33,7 @@
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:networkSecurityConfig="@xml/network_security_config"
android:requestLegacyExternalStorage="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppThemeLight">
@@ -48,6 +49,14 @@
android:name="android.allow_multiple_resumed_activities"
android:value="true" />
<!-- Bugsnag -->
<meta-data
android:name="com.bugsnag.android.DETECT_ANRS"
android:value="false" />
<meta-data
android:name="com.bugsnag.android.DETECT_NDK_CRASHES"
android:value="false" />
<activity
android:name=".ActivityMain"
android:excludeFromRecents="true"
@@ -77,6 +86,14 @@
</intent-filter>
</activity>
<activity
android:name=".ActivityWidgetUnified"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
</intent-filter>
</activity>
<activity
android:name=".ActivityView"
android:exported="false"
@@ -183,13 +200,19 @@
</intent-filter>
</activity>
<service android:name=".ServiceSynchronize" />
<service
android:name=".ServiceSynchronize"
android:foregroundServiceType="dataSync" />
<service android:name=".ServiceSend" />
<service
android:name=".ServiceSend"
android:foregroundServiceType="dataSync" />
<service android:name=".ServiceUI" />
<service android:name=".ServiceExternal">
<service
android:name=".ServiceExternal"
android:foregroundServiceType="dataSync">
<intent-filter>
<action android:name="${applicationId}.ENABLE" />
<action android:name="${applicationId}.DISABLE" />
@@ -229,7 +252,7 @@
<receiver
android:name=".Widget"
android:label="@string/app_name">
android:label="@string/tile_unseen">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
@@ -240,7 +263,24 @@
android:resource="@xml/widget" />
</receiver>
<receiver android:name=".ReceiverAutostart">
<receiver
android:name=".WidgetUnified"
android:label="@string/title_folder_unified">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_unified" />
</receiver>
<service
android:name="WidgetUnifiedService"
android:permission="android.permission.BIND_REMOTEVIEWS" />
<receiver android:name=".ReceiverAutoStart">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.room;
/**
* RPC Callbacks for {@link IMultiInstanceInvalidationService}.
*
* @hide
*/
interface IMultiInstanceInvalidationCallback {
/**
* Called when invalidation is detected in another instance of the same database.
*
* @param tables List of invalidated table names
*/
oneway void onInvalidation(in String[] tables);
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.room;
import androidx.room.IMultiInstanceInvalidationCallback;
/**
* RPC Service that controls interaction about multi-instance invalidation.
*
* @hide
*/
interface IMultiInstanceInvalidationService {
/**
* Registers a new {@link IMultiInstanceInvalidationCallback} as a client of this service.
*
* @param callback The RPC callback.
* @param name The name of the database file as it is passed to {@link RoomDatabase.Builder}.
* @return A new client ID. The client needs to hold on to this ID and pass it to the service
* for subsequent calls.
*/
int registerCallback(IMultiInstanceInvalidationCallback callback, String name);
/**
* Unregisters the specified {@link IMultiInstanceInvalidationCallback} from this service.
* <p>
* Clients might die without explicitly calling this method. In that case, the service should
* handle the clean up.
*
* @param callback The RPC callback.
* @param clientId The client ID returned from {@link #registerCallback}.
*/
void unregisterCallback(IMultiInstanceInvalidationCallback callback, int clientId);
/**
* Broadcasts invalidation of database tables to other clients registered to this service.
* <p>
* The broadcast is delivered to {@link IMultiInstanceInvalidationCallback#onInvalidation} of
* the registered clients. The client calling this method will not receive its own broadcast.
* Clients that are associated with a different database file will not be notified.
*
* @param clientId The client ID returned from {@link #registerCallback}.
* @param tables The names of invalidated tables.
*/
oneway void broadcastInvalidation(int clientId, in String[] tables);
}

View File

@@ -0,0 +1,20 @@
## Attribution
FairEmail uses:
* [JavaMail](https://projects.eclipse.org/projects/ee4j.javamail). Copyright (c) 1997-2018 Oracle® and/or its affiliates. All rights reserved. [GPLv2+CE license](https://javaee.github.io/javamail/JavaMail-License).
* [jsoup](https://jsoup.org/). Copyright © 2009 - 2017 Jonathan Hedley. [MIT license](https://jsoup.org/license).
* [Android Support Library](https://developer.android.com/tools/support-library/). Copyright (C) 2011 The Android Open Source Project. [Apache license 2.0](https://android.googlesource.com/platform/frameworks/support/+/master/LICENSE.txt).
* [Android Architecture Components](https://developer.android.com/topic/libraries/architecture/). Copyright 2018 The Android Open Source Project, Inc. [Apache license 2.0](https://github.com/googlesamples/android-architecture-components/blob/master/LICENSE).
* [colorpicker](https://android.googlesource.com/platform/frameworks/opt/colorpicker). Copyright (C) 2013 The Android Open Source Project. [Apache license 2.0](https://android.googlesource.com/platform/frameworks/opt/colorpicker/+/master/src/com/android/colorpicker/ColorPickerDialog.java).
* [dnsjava](http://www.xbill.org/dnsjava/). Copyright (c) 1998-2011, Brian Wellington. [BSD License](https://sourceforge.net/p/dnsjava/code/HEAD/tree/trunk/LICENSE).
* [OpenPGP API library](https://github.com/open-keychain/openpgp-api). Copyright (C) 2014-2015 Dominik Schürmann. [Apache License 2.0](https://github.com/open-keychain/openpgp-api/blob/master/LICENSE).
* [Android SQLite support library](https://github.com/requery/sqlite-android). Copyright (C) 2017 requery.io. [Apache License 2.0](https://github.com/requery/sqlite-android/blob/master/LICENSE).
* [App shortcut icon generator](https://romannurik.github.io/AndroidAssetStudio/icons-app-shortcut.html). Copyright ???. [Apache License 2.0](https://github.com/romannurik/AndroidAssetStudio/blob/master/LICENSE).
* [Mozilla ISPDB](https://developer.mozilla.org/en-US/docs/Mozilla/Thunderbird/Autoconfiguration#ISPDB). *Free to use for any client.*
* [ShortcutBadger](https://github.com/leolin310148/ShortcutBadger). Copyright 2014 Leo Lin. [Apache license 2.0](https://github.com/leolin310148/ShortcutBadger/blob/master/LICENSE).
* [Bugsnag exception reporter for Android](https://github.com/bugsnag/bugsnag-android). Copyright (c) 2012 Bugsnag. [MIT License](https://github.com/bugsnag/bugsnag-android/blob/master/LICENSE.txt).
* [biweekly](https://github.com/mangstadt/biweekly). Copyright (c) 2013-2018, Michael Angstadt. [BSD 2-Clause](https://github.com/mangstadt/biweekly/blob/master/LICENSE).
* [PhotoView](https://github.com/chrisbanes/PhotoView). Copyright 2018 Chris Banes. [Apache License 2.0](https://github.com/chrisbanes/PhotoView/blob/master/LICENSE).
* [ReLinker](https://github.com/KeepSafe/ReLinker). Copyright 2015 - 2016 KeepSafe Software, Inc. [Apache License 2.0](https://github.com/KeepSafe/ReLinker/blob/master/LICENSE).
* [Markwon](https://github.com/noties/Markwon). Copyright 2019 Dimitry Ivanov. [Apache License 2.0](https://github.com/noties/Markwon/blob/master/LICENSE).

View File

@@ -0,0 +1,18 @@
## Privacy policy
FairEmail **does not** collect any information.
FairEmail **does not** store data on third party servers.
FairEmail **does not** require unnecessary permissions.
For more information on permissions, see [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#user-content-faq1).
FairEmail **does not** allow other apps access to messages and attachments without your approval.
FairEmail **does** follow the recommendations of [this EFF article](https://www.eff.org/deeplinks/2019/01/stop-tracking-my-emails).
FairEmail is 100 % **open source**, see [the license](https://github.com/M66B/FairEmail/blob/master/LICENSE).
Error reporting is **opt-in**, see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#user-content-faq104) for more information.
Copyright &copy; 2018-2019 Marcel Bokhorst.

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Opsætningshjælp
At opsætte FairEmail er ret enkelt. Du skal tilføje mindst én konto for at modtage e-mail og mindst én identitet for at sende e-mail. Den hurtige opsætning tilføjer på én gang en konto og en identitet for de fleste større udbydere.
## Forudsætninger
En Internetforbindelse kræves for at opsætte konti og identiteter.
## Hurtig opsætning
Angiv blot dit navn, e-mail og adgangskode, og tryk på *Go*.
Dette vil fungere for de fleste større udbydere.
Fungerer hurtig-opsætningen ikke, så opsæt på anden vis en konto og en identitet (se instruktioner nedenfor).
## Opsæt konto - for at modtage e-mail
For at tilføje en konto, så tryk på*Håndtér Konti* og tryk på orange knap *Tilføj* nederst. Vælg en udbyder på listen, angiv brugernavnet, som for det meste er din e-mail, og angiv dit adgangskode. Tryk *Tjek* for at lade FairEmail forbinde til e-mailserveren og hente en liste over systemmapper. Efter gennemgang af systemmappeudvalget kan du tilføje kontoen ved at trykke på*Gem*.
Er din udbyder ikke på listen over udbydere, så vælg *Tilpasset*. Angiv domænenavnet, f.eks. *gmail.com*, og tryk på *Hent indstillinger*. Understøtter din udbyder [auto-detektering](https://tools.ietf.org/html/rfc6186), udfylder FairEmail værtsnavnet og portnummeret, ellers tjek opsætningsvejledningen fra din udbyder vedr. korrekt IMAP-værtsnavn, portnummer og protokol (SSL/TLS eller STARTTLS). For mere om dette, se [hér](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Opsæt identitet - for at sende e-mail
På samme vis, så tryk på *Håndtér Identitet* for at tilføje en identitet og tryk på den orange knap *Tilføj* nederst. Angiv det navn, du ønsker vist i fra-adressen i e-mails, du sender, og vælg en tilknyttet konto. Tryk *Gem* for at tilføje identiteten.
Blev kontoen opsat manuelt, skal du sandsynligvis også opsætte identiteten manuelt. Angiv domænenavnet, f.eks. *gmail.com*, og tryk på *Hent indstillinger*. Understøtter din udbyder [auto-detektering](https://tools.ietf.org/html/rfc6186), udfylder FairEmail værtsnavnet og portnummeret, ellers tjek opsætningsvejledningen fra din udbyder vedr. korrekt SMTP-værtsnavn, portnummer og protokol (SSL/TLS eller STARTTLS).
Se [denne FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) vedr. brug af aliasser.
## Giv tilladelser - for at tilgå kontaktoplysninger
Hvis du vil slå e-mailadresser op, få vist kontaktfotos mv., skal du give FairEmail læs kontakter-tilladelse. Tryk blot på *Giv tilladelse* og vælg *Tillad*.
## Opsæt batterioptimeringer - til løbende modtagelse af e-mail
I nyere Android-versioner sættes apps i dvale for at reducere batteriforbruget, når skærmen har været slukket i nogen tid. Vil du gerne modtage nye e-mails uden forsinkelser, skal du deaktivere batterioptimeringer for FairEmail. Tryk på *Deaktivér Batterioptimeringer* og følg anvisningerne.
## Spørgsmål eller problemer
Har du spørgsmål eller problemer, så [tjek hér](https://github.com/M66B/FairEmail/blob/master/FAQ.md) eller benyt [ denne kontaktformular](https://contact.faircode.eu/?product=fairemailsupport) til at bede om hjælp (du kan benytte transaktionsnummeret "*opsætningshjælp*").

View File

@@ -0,0 +1,41 @@
# Setuphilfe
FairEmail einzurichten, ist ziemlich einfach. Sie müssen mindestens ein Konto hinzufügen, um E-Mail zu erhalten, und mindestens eine Identität, wenn Sie E-Mail senden möchten. Die Schnelleinrichtung wird ein Konto und eine Identität in einem Vorgang für die meisten großen Anbieter erstellen.
## Anforderungen
Für die Einrichtung von Konten und Identitäten ist eine Internetverbindung erforderlich.
## Schnelleinrichtung
Geben Sie einfach Ihren Namen, Ihre E-Mail-Adresse und Ihr Passwort ein und tippen Sie auf *prüfen*.
Dies funktioniert für die meisten großen E-Mail-Anbieter.
Wenn die Schnelleinrichtung nicht funktioniert, müssen Sie ein Konto und eine Identität auf andere Weise einrichten, siehe unten für Anweisungen dazu.
## Konto einrichten - um E-Mail zu erhalten
Um ein Konto hinzuzufügen, tippen Sie unter 'Konten einrichten' auf *Verwalten* und tippen Sie dann auf den orange-farbenen Button mit dem *Plus-Zeichen* unten. Wählen Sie einen Anbieter aus der Liste, geben Sie den Benutzernamen ein, der meistens Ihre E-Mail-Adresse ist, und geben Sie Ihr Passwort ein. Tippen Sie auf *Prüfen* um FairEmail mit dem E-Mail-Server zu verbinden und eine Liste von Systemordnern zu laden. Nach der Überprüfung der Systemordner-Auswahl können Sie das Konto hinzufügen, indem Sie auf *Speichern* klicken.
Wenn Ihr Provider nicht in der Liste der Anbieter ist, wählen Sie *Benutzerdefiniert*. Geben Sie den Domain-Namen ein, zum Beispiel *gmail.com*, und tippen Sie auf *Einstellungen abrufen*. Wenn Ihr Provider [Auto-discovery](https://tools.ietf.org/html/rfc6186) unterstützt, wird FairEmail den Hostnamen und die Portnummer ausfüllen, so dass Sie die Angaben Ihres Providers für den richtigen IMAP-Hostnamen, Port-Nummer und Protokoll (SSL/TLS oder STARTTLS) nur noch überprüfen müssen. Weitere Informationen dazu finden Sie [hier](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Identität einrichten - um E-Mail zu senden
Vergleichbar mit der Konto-Einrichtung tippen Sie unter 'Identitäten einrichten' auf *Verwalten* und tippen Sie dann auf den orange-farbenen Button mit dem *Plus-Zeichen* unten. Geben Sie den Namen ein, der in E-Mails, die Sie von dieser Absender-Adresse senden, erscheinen soll, und wählen Sie ein verknüpftes Konto. Tippen Sie auf *Speichern*, um die Identität hinzuzufügen.
Wenn das Konto manuell konfiguriert wurde, müssen Sie wahrscheinlich auch die Identität manuell konfigurieren. Geben Sie den Domain-Namen ein, zum Beispiel *gmail.com*, und tippen Sie auf *Einstellungen abrufen*. Wenn Ihr Provider [Auto-discovery](https://tools.ietf.org/html/rfc6186) unterstützt, wird FairEmail den Hostnamen und die Portnummer ausfüllen, so dass Sie die Angaben Ihres Providers für den richtigen IMAP-Hostnamen, Port-Nummer und Protokoll (SSL/TLS oder STARTTLS) nur noch überprüfen müssen.
Siehe [diese FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) über die Verwendung von Alias-Adressen.
## Berechtigungen gewähren - Zugriff auf Kontaktinformationen
Wenn Sie E-Mail-Adressen suchen wollen, Kontaktfotos anzeigen lassen möchten etc., müssen Sie FairEmail die Lese-Kontaktberechtigung erteilen. Tippe einfach auf *Berechtigungen erteilen* und wählen Sie *Zulassen*.
## Akku-Optimierungen einrichten - um E-Mails kontinuierlich zu erhalten
Bei den aktuellen Android-Versionen wird Android alle Apps zum Schlafen bringen, wenn der Bildschirm für einige Zeit ausgeschaltet ist, um die Akkunutzung zu reduzieren. Wenn Sie neue E-Mails ohne Verzögerung erhalten möchten, sollten Sie die Batterie-Optimierung für FairEmail deaktivieren. Tippen Sie auf *Akku-Optimierung einrichten* und folgen Sie den Anweisungen.
## Fragen oder Probleme
Wenn Sie Fragen oder Probleme haben, bitte [hier](https://github.com/M66B/FairEmail/blob/master/FAQ.md) nachschauen oder benutzen Sie [dieses Kontaktformular](https://contact.faircode.eu/?product=fairemailsupport), um um Hilfe zu bitten (Sie können dabei die Transaktionsnummer "*Setup help*" verwenden).

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Asetusohje
FairEmailin käyttöönotto on melko yksinkertaista. Tarvitset ainakin yhden tilin vastaanottaaksesi sähköpostia ja ainakin yhden tilin lähettääksesi sähköpostia. Pika-asennus lisää samalla kertaa tilin ja identiteetin useimmilla palveluntarjoajilla.
## Vaatimukset
Verkkoyhteys tarvitaan tilien ja identiteettien asettamiseksi.
## Pika-asennus
Lisää vain nimesi, sähköpostiosoitteesi ja salasanasi sekä paina *Aloita*.
Tämä toimii useimmille sähköpostipalveluntarjoajille.
Jos pika-asennus ei toimi, täytyy tili ja identiteetti asettaa erikseen, katso ohjeet alapuolelta.
## Aseta tili - sähköpostien vastaanottamiseksi
Tilin lisäämiseksi napauta *Aseta tilit* ja napauta alhaalla olevaa oranssia *lisää*-painiketta. Valitse palveluntarjoaja listasta, anna käyttäjätunnus, joka useimmiten on sähköpostiosoite, ja anna salasana. Napauta *Tarkista* antaaksesi FairEmailin yhdistää sähköpostipalvelimeen ja hakea listan järjestelmän kansioista. Järjestelmän kansiovalintojen tarkistuksen jälkeen voit lisätä tilin napauttamalla *Tallenna*.
Jos palveluntarjoajasi ei ole palveluntarjoajalistalla, valitse *Mukautettu*. Lisää verkkotunnus, esimerkiksi *gmail.com*, ja napauta *Hae asetukset*. Jos palveluntarjoajasi tukee [auto-discoverya](https://tools.ietf.org/html/rfc6186), FairEmail täyttää verkko-osoitteen ja porttinumeron. Muussa tapauksessa tarkista palveluntarjoajasi asetusohjeista IMAP verkko-osoite, porttinumero ja protokolla (SSL/TLS tai STARTTLS). Katso [täältä](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts) lisätietoja.
## Aseta identiteetti - viestien lähettämiseksi
Vastaavasti identiteetin lisäämiseksi napauta *Aseta identiteetit* ja napauta alhaalla olevaa oranssia *lisää*-painiketta. Anna nimi, jonka haluat näkyvän lähettämiesi viestien lähetysosoitteessa ja valitse siihen liittyvä tili. Napauta *Tallenna* lisätäksesi identiteetin.
Jos tili määritettiin manuaalisesti, luultavasti identiteettikin pitää asettaa manuaalisesti. Lisää verkkotunnus, esimerkiksi *gmail.com*, ja napauta *Hae asetukset*. Jos palveluntarjoajasi tukee [auto-discoverya](https://tools.ietf.org/html/rfc6186), FairEmail täyttää verkko-osoitteen ja porttinumeron. Muussa tapauksessa tarkista palveluntarjoajasi asetusohjeista IMAP verkko-osoite, porttinumero ja protokolla (SSL/TLS tai STARTTLS).
Lue [tämä usein kysytyt kysymykset](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) aliasten käytöstä.
## Myönnä käyttöoikeudet - yhteystietoihin pääsemiseksi
FairEmail tarvitsee käyttöoikeudet yhteystietoihin, jos haluat etsiä sähköpostiosoitteita, näyttää yhteystietojen kuvia, jne. Napauta vain *Myönnä käyttöoikeudet* ja valitse *Myönnä*.
## Aseta akun käytön optimointi - sähköpostien vastaanottamiseksi ilman keskeytyksiä
Viimeisimmissä Android-versiossa sovellukset laitetaan lepotilaan akun käytön vähentämiseksi, kun näyttö on ollut jonkin aikaa pois päältä. Jos haluat vastaanottaa sähköposteja ilman keskeytyksiä, pitää akun käytön optimointi ottaa pois käytöstä FairEmailille. Napauta *Poista akun käytön optimoinnit* ua seuraa ohjeita.
## Kysymyksiä tai ongelmia
Jos sinulla on kysymys tai ongelma, [katso täältä](https://github.com/M66B/FairEmail/blob/master/FAQ.md) tai käytä [tätä yhteydenotyolomaketta](https://contact.faircode.eu/?product=fairemailsupport) pyytääksesi apua (voit käyttää tapahtumanumeroa "*asetusohje*").

View File

@@ -0,0 +1,41 @@
# Aide de configuration
La configuration de FairEmail est assez simple. Vous devrez ajouter au moins un compte pour recevoir des courriels et au moins une identité si vous voulez envoyer des courriels. La configuration rapide ajoutera un compte et une identité en une seule opération pour la plupart des principaux fournisseurs.
## Pré-requis
Une connexion Internet est nécessaire pour configurer les comptes et les identités.
## Configuration rapide
Entrez simplement votre nom, votre adresse e-mail et votre mot de passe et appuyez sur *Aller*.
Ceci fonctionnera pour la plupart des principaux fournisseurs de messagerie.
Si la configuration rapide ne fonctionne pas, vous devez configurer un compte et une identité d'une autre manière, voir ci-dessous pour les instructions.
## Configuration d'un compte - recevoir des e-mails
Pour ajouter un compte, appuyez sur *Gérer les comptes* et appuyez sur le bouton orange *ajouter* en bas. Sélectionnez un fournisseur dans la liste, entrez le nom d'utilisateur, qui est principalement votre adresse e-mail et entrez votre mot de passe. Appuyez sur *Vérifier* pour permettre à FairEmail de se connecter au serveur de messagerie et de récupérer une liste de dossiers système. Après avoir examiné la sélection des dossiers système, vous pouvez ajouter le compte en appuyant sur *Enregistrer*.
Si votre fournisseur n'est pas dans la liste des fournisseurs, sélectionnez *Personnalisé*. Entrez le nom de domaine, par exemple *gmail.com* et appuyez sur *Obtenir les paramètres*. Si votre fournisseur supporte l'[auto-découverte](https://tools.ietf.org/html/rfc6186), FairEmail remplira le nom d'hôte et le numéro de port, sinon vérifiez les instructions de configuration de votre fournisseur pour le nom d'hôte IMAP, le numéro de port et le protocole (SSL/TLS ou STARTTLS). Pour plus d'informations, veuillez voir [ici](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Configuration d'une identité - envoyer des e-mails
De même, pour ajouter une identité, appuyez sur *Gérer les identités* et appuyez sur le bouton orange *ajouter* en bas. Entrez le nom que vous souhaitez voir apparaître dans le champ de: pour les e-mails que vous envoyez et sélectionnez un compte lié. Appuyez sur *Enregistrer* pour ajouter l'identité.
Si le compte a été configuré manuellement, vous devez probablement configurer l'identité également manuellement. Entrez le nom de domaine, par exemple *gmail.com* et appuyez sur *Obtenir les paramètres*. Si votre fournisseur supporte l'[auto-découverte](https://tools.ietf.org/html/rfc6186), FairEmail remplira le nom d'hôte et le numéro de port, sinon vérifiez les instructions de configuration de votre fournisseur pour le nom d'hôte IMAP, le numéro de port et le protocole (SSL/TLS ou STARTTLS).
Voir [cette FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) sur l'utilisation des alias.
## Accorder les permissions - accéder aux informations de contact
Si vous souhaitez rechercher des adresses e-mail, avoir les photos de contact affichées, etc, vous devez accorder la permission de lire les contacts à FairEmail. Appuyez simplement sur *Accorder les autorisations* et sélectionnez *Autoriser*.
## Configurer les optimisations de batterie - recevoir des e-mails en continu
Sur les versions récentes d'Android, Android mettra les applications en veille lorsque l'écran est éteint pendant un certain temps pour réduire l'utilisation de la batterie. Si vous souhaitez recevoir de nouveaux courriels sans retard, vous devez désactiver les optimisations de batterie pour FairEmail. Appuyez sur *Désactiver les optimisations de batterie* et suivez les instructions.
## Questions ou problèmes
Si vous avez une question ou un problème, veuillez [voir ici](https://github.com/M66B/FairEmail/blob/master/FAQ.md) ou utiliser [ce formulaire de contact](https://contact.faircode.eu/?product=fairemailsupport) pour demander de l'aide (vous pouvez utiliser le numéro de transaction "*aide d'installation*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Pomoć pri postavljanju
Postavljanje FairEmail-a prilično je jednostavno. Trebate dodati barem jedan račun da biste primili e-poštu i barem jedan identitet ako želite slati e-poštu. Brzo postavljanje dodat će račun i identitet većini glavnih dobavljača e-pošte.
## Zahtjevi
Za postavljanje računa i identiteta potrebna je internetska veza.
## Brzo postavljanje
Samo unesite svoje ime, adresu e-pošte i lozinku i dodirnite *Idi*.
Ovo će raditi za većinu glavnih pružatelja usluga e-pošte.
Ako brzo postavljanje ne uspije, morat ćete postaviti račun i identitet na drugi način, upute potražite u nastavku.
## Postavljanje računa - za primanje e-pošte
Da biste dodali račun, dodirnite *Upravljanje računima* i na dnu narančastog botuna *dodaj*. S popisa odaberite provajdera, unesite korisničko ime, koje je uglavnom vaša adresa e-pošte i unesite svoju lozinku. Dodirnite *Provjeri* da biste se omogućili da se FairEmail poveže s poslužiteljem e-pošte i preuzme popis sistemskih mapa. Nakon pregleda izbora mape sustava možete dodati račun dodirom na *Spremi*.
Ako vaš davatelj nije na popisu pružatelja usluga, odaberite *Custom*. Unesite ime domene, primjerice *gmail.com* i dodirnite *Dohvati postavke*. Ako vaš pružatelj usluga podržava [automatsko otkrivanje](https://tools.ietf.org/html/rfc6186), FairEmail će ispuniti ime računala i broj porta, osim toga, provjerite upute za postavljanje vašeg davatelja za pravo IMAP ime glavnog računala, broj porta i protokol (SSL / TLS ili STARTTLS). Više o ovome pogledajte [ovdje](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Postavljanje identiteta - za slanje e-pošte
Slično tome, da biste dodali identitet, dodirnite *Upravljanje identitetom* i dodirnite narančastu tipku *dodaj* na dnu. Unesite ime koje želite da se pojavi u adresi e-pošte koju šaljete i odaberite povezani račun. Dodirnite *Spremi* da biste dodali identitet.
Ako je račun konfiguriran ručno, vjerojatno ćete morati i ručno konfigurirati identitet. Unesite ime domene, primjerice *gmail.com* i dodirnite *Dohvati postavke*. Ako vaš pružatelj usluga podržava [automatsko otkrivanje](https://tools.ietf.org/html/rfc6186), FairEmail će ispuniti ime računala i broj porta, osim toga, provjerite upute za postavljanje vašeg davatelja za pravo IMAP ime glavnog računala, broj porta i protokol (SSL / TLS ili STARTTLS).
Pogledajte [ovaj FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) o korištenju aliasa.
## Davanje dozvola - za pristup podacima o kontaktima
Ako želite pretraživati adrese e-pošte, prikazivati fotografije kontakata itd., Trebate odobriti FairEmailu dozvolu za čitanje kontakata. Samo dodirnite *Davanje dozvola* i odaberite *Dopusti*.
## Postavljanje optimizacija za baterije - za kontinuirano primanje e-poruka
Na novijim verzijama Androida Android će staviti uređaje za spavanje kad je ekran neko vrijeme isključen kako bi smanjio potrošnju baterije. Ako želite primati nove poruke e-pošte bez odgađanja, trebali biste onemogućiti optimizaciju baterije za FairEmail. Dodirnite *Onemogući optimizaciju baterije* i slijedite upute.
## Pitanja ili problemi
Ako imate pitanje ili problem, molimo [pogledajte ovdje](https://github.com/M66B/FairEmail/blob/master/FAQ.md) ili tražite [ovaj obrazac za kontakt](https://contact.faircode.eu/?product=fairemailsupport) da biste zatražili pomoć (možete koristiti broj transakcije "*pomoć u postavljanju*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Aiuto impostazione
La configurazione di FairEmail è abbastanza semplice. Dovrai aggiungere almeno un account per ricevere email e almeno un'identità se vuoi inviare email. La configurazione rapida aggiungerà un account e un'identità in un solo colpo per la maggior parte dei principali provider.
## Requisiti
È necessaria una connessione internet per configurare account e identità.
## Configurazione rapida
Inserisci il tuo nome, indirizzo email e password e tocca *Vai*.
Questo funzionerà per la maggior parte dei principali provider di posta elettronica.
Se la configurazione rapida non funziona, dovrai impostare un account e un'identità in un altro modo, vedere sotto per le istruzioni.
## Imposta account - per ricevere email
Per aggiungere un account, tocca *Gestisci account* e tocca il pulsante arancione *aggiungi* in basso. Seleziona un provider dalla lista, inserisci il nome utente, che di solito corrisponde al tuo indirizzo email e inserisci la password. Tocca *Controlla* per consentire a FairEmail di connettersi al server email e recuperare una lista di cartelle di sistema. Dopo aver esaminato la selezione della cartella di sistema è possibile aggiungere l'account toccando *Salva*.
Se il tuo provider non è nella lista dei provider, seleziona *Custom*. Inserisci il nome del dominio, ad esempio *gmail.com* e tocca *Ottieni impostazioni*. Se il tuo provider supporta [l'auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail riempirà il nome host e il numero di porta, altrimenti controlla le istruzioni di installazione del tuo provider per il corretto nome host IMAP, il numero di porta e il protocollo (SSL/TLS o STARTTLS). Per ulteriori informazioni, si prega di vedere [qui](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Imposta identità - per inviare email
Analogamente, per aggiungere un'identità, tocca *Gestisci identità* e tocca il pulsante arancione *aggiungi* in basso. Inserisci il nome che vuoi appaia nell'indirizzo del mittente e seleziona un account collegato. Tocca *Salva* per aggiungere l'identità.
Se l'account è stato configurato manualmente, è probabile che sia necessario configurare l'identità anche manualmente. Inserisci il nome del dominio, ad esempio *gmail.com* e tocca *Ottieni impostazioni*. Se il tuo provider supporta [l'auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail riempirà il nome host e il numero di porta, altrimenti controlla le istruzioni di installazione del tuo provider per il nome host SMTP corretto, il numero di porta e il protocollo (SSL/TLS o STARTTLS).
Vedi [questa FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) sull'utilizzo degli alias.
## Concedi permessi - per accedere alle informazioni di contatto
Se vuoi cercare indirizzi email, visualizzare le foto dei contatto, ecc, dovrai concedere il permesso di lettura dei contatti a FairEmail. Basta toccare *Concedi permessi* e selezionare *Consenti*.
## Imposta ottimizzazioni batteria - per ricevere e-mail senza soluzione di continuità
Nelle versioni recenti di Android, Android metterà le app in standby quando lo schermo è spento per un certo tempo per ridurre l'uso della batteria. Se vuoi ricevere nuove email senza ritardi, dovresti disabilitare le ottimizzazioni della batteria per FairEmail. Tocca *Disabilita le ottimizzazioni della batteria* e segui le istruzioni.
## Domande o problemi
Se hai una domanda o un problema, si prega di [vedere qui](https://github.com/M66B/FairEmail/blob/master/FAQ.md) o utilizzare [questo modulo di contatto](https://contact.faircode.eu/?product=fairemailsupport) per chiedere aiuto (è possibile utilizzare il numero di transazione "*configurare aiuto*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Help bij instellen
Het instellen van FairEmail is vrij eenvoudig. U moet ten minste één account toevoegen om e-mail te ontvangen en ten minste één identiteit om e-mail te verzenden. Snel instellen zal in één keer een account en een identiteit toevoegen voor de meeste grote providers.
## Vereisten
Een internetverbinding is vereist om accounts en identiteiten in te stellen.
## Snel instellen
Voer gewoon je naam, e-mailadres en wachtwoord in en tik op *Ga*.
Dit zal werken voor de meeste grote e-mailproviders.
Als het snelle instellen niet werkt, moet je een account en een identiteit op een andere manier instellen, zie hieronder voor instructies.
## Account instellen - om e-mail te ontvangen
Om een account toe te voegen, tik op *Beheer accounts* en tik op de oranje *voeg toe* knop onderaan. Selecteer een provider uit de lijst, voer de gebruikersnaam in, meestal uw e-mailadres, en voer uw wachtwoord in. Tik *Controleer* om FairEmail te laten verbinden met de e-mailserver en een lijst van systeemmappen op te laten halen. Na het controleren van de selectie van systeemmappen kunt u het account toevoegen door op *Bewaren* te klikken.
Als uw provider niet in de lijst van providers staat, selecteer *Aangepast*. Voer de domeinnaam in, bijvoorbeeld *gmail.com* en tik op *Instellingen ophalen*. Als uw provider [auto-discovery](https://tools.ietf.org/html/rfc6186) ondersteunt, zal FairEmail de hostnaam en poortnummer invullen, controleer anders de instructies van uw provider voor de juiste IMAP host naam, poortnummer en protocol (SSL/TLS of STARTTLS). Voor meer informatie, zie [hier](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Identiteit instellen - om e-mail te verzenden
Om een identiteit toe te voegen, tik op *Beheer identiteit* en tik op de oranje *voeg toe* knop onderaan. Voer de naam in die u wilt laten verschijnen in het 'van' adres van de e-mails die u verzendt en selecteer een gekoppeld account. Tik op *Opslaan* om de identiteit toe te voegen.
Als het account handmatig werd geconfigureerd, moet u waarschijnlijk ook de identiteit handmatig configureren. Voer de domeinnaam in, bijvoorbeeld *gmail.com* en tik op *Instellingen ophalen*. Als uw provider [auto-discovery](https://tools.ietf.org/html/rfc6186) ondersteunt, zal FairEmail de hostnaam en poortnummer invullen, controleer anders de instructies van uw provider voor de juiste SMTP host naam, poortnummer en protocol (SSL/TLS of STARTTLS).
Zie [deze FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) over het gebruik van aliassen.
## Toestemmingen verlenen - om contact informatie op te vragen
Als u e-mailadressen wilt opzoeken, contactfoto's wilt zien, enz. moet u aan FairEmail toestemmingen verlenen om contacten te lezen. Tik op *Toestemmingen verlenen* en selecteer *Toestaan*.
## Accuoptimalisaties instellen - om voortdurend e-mail te ontvangen
Recente Android versies zetten apps in de slaapstand wanneer het scherm enige tijd uit is om het accugebruik te verminderen. Als u nieuwe e-mails zonder vertraging wilt ontvangen, dan moet u de accuoptimalisaties voor FairEmail uitschakelen. Tik *Schakel accuoptimalisaties uit* en volg de instructies.
## Vragen of problemen
Als u een vraag of probleem heeft, kijk [hier](https://github.com/M66B/FairEmail/blob/master/FAQ.md) of gebruik [dit contactformulier](https://contact.faircode.eu/?product=fairemailsupport) om hulp te vragen (u kunt het transactienummer "*setup help*" gebruiken).

View File

@@ -0,0 +1,41 @@
# Installasjonshjelp
Det er ganske enkelt å sette opp FairEmail. Du må legge til minst en konto for å motta e-post og minst en identitet hvis du vil sende e-post. Det raske oppsettet vil legge til en konto og en identitet på en gang for de fleste store leverandører.
## Krav
Det kreves en internettforbindelse for å konfigurere kontoer og identiteter.
## Hurtig oppsett
Bare skriv inn navn, e-postadresse og passord og trykk **.
Dette vil fungere for de fleste store e-postleverandører.
Hvis hurtigoppsettet ikke fungerer, må du konfigurere en konto og en identitet på en annen måte, se nedenfor for instruksjoner.
## Oppsett av konto - for å motta e-post
For å legge til en konto, trykk på *Administrer kontoer* og trykk på den oransje *legg til* knappen nederst. Velg en leverandør fra listen, skriv inn brukernavnet, som for det meste er din e-postadresse, og skriv inn passordet ditt. Trykk på *Kontroller* for å la FairEmail koble til e-postserveren og hente en liste over systemmapper. Etter å ha gjennomgått valg av systemmappe, kan du legge til kontoen ved å trykke *Lagre*.
Hvis leverandøren din ikke er på listen over leverandører, velger du *Tilpasset*. Skriv inn domenenavnet, for eksempel *gmail.com* og trykk *Hent innstillinger*. Hvis leverandøren din støtter [auto-oppdagelse](https://tools.ietf.org/html/rfc6186), vil FairEmail fylle ut vertsnavnet og portnummeret, ellers sjekk installasjonsinstruksjonene til leverandøren din for riktig IMAP-vertsnavn, portnummer og protokoll (SSL/TLS eller STARTTLS). For mer om dette, vennligst se [her](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Oppsett identitet - for å sende e-post
For å legge til en identitet, trykk på *Administrer identitet* og trykk på den oransje *legg til* -knappen nederst. Skriv inn navnet du vil skal vises i fra adressen til e-postene du sender, og velg en lenket konto. Trykk på *Lagre* for å legge til identiteten.
Hvis kontoen ble konfigurert manuelt, må du sannsynligvis konfigurere identiteten manuelt. Skriv inn domenenavnet, for eksempel *gmail.com* og trykk *Hent innstillinger*. Hvis leverandøren din støtter [auto-oppdagelse](https://tools.ietf.org/html/rfc6186), vil FairEmail fylle ut vertsnavnet og portnummeret, ellers sjekk installasjonsinstruksjonene til leverandøren din for riktig SMTP vertsnavn, portnummer og protokoll (SSL/TLS eller STARTTLS).
Se [denne FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) om bruk av aliaser.
## Gi tillatelser - for å få tilgang til kontaktinformasjon
Hvis du vil slå opp e-postadresser, ha kontaktbilder vist osv., Må du gi tillatelse til å lese kontakter til FairEmail. Bare trykk på *Gi tillatelser* og velg *Tillat*.
## Konfigurer batterioptimaliseringer - for kontinuerlig å motta e-post
På nyere Android-versjoner vil Android legge appene i dvale når skjermen er slått av en stund for å redusere batteribruken. Hvis du vil motta nye e-postmeldinger uten forsinkelser, bør du deaktivere batterioptimaliseringer for FairEmail. Trykk på *Deaktiver batterioptimaliseringer* og følg instruksjonene.
## Spørsmål eller problemer
Hvis du har et spørsmål eller et problem, kan du [se her](https://github.com/M66B/FairEmail/blob/master/FAQ.md) eller bruk [dette kontaktskjemaet](https://contact.faircode.eu/?product=fairemailsupport) for å be om hjelp (du kan bruke transaksjonsnummeret "*konfigurasjonshjelp*").

View File

@@ -0,0 +1,41 @@
# Installasjonshjelp
Det er ganske enkelt å sette opp FairEmail. Du må legge til minst en konto for å motta e-post og minst en identitet hvis du vil sende e-post. Det raske oppsettet vil legge til en konto og en identitet på en gang for de fleste store leverandører.
## Krav
Det kreves en internettforbindelse for å konfigurere kontoer og identiteter.
## Hurtig oppsett
Bare skriv inn navn, e-postadresse og passord og trykk **.
Dette vil fungere for de fleste store e-postleverandører.
Hvis hurtigoppsettet ikke fungerer, må du konfigurere en konto og en identitet på en annen måte, se nedenfor for instruksjoner.
## Oppsett av konto - for å motta e-post
For å legge til en konto, trykk på *Administrer kontoer* og trykk på den oransje *legg til* knappen nederst. Velg en leverandør fra listen, skriv inn brukernavnet, som for det meste er din e-postadresse, og skriv inn passordet ditt. Trykk på *Kontroller* for å la FairEmail koble til e-postserveren og hente en liste over systemmapper. Etter å ha gjennomgått valg av systemmappe, kan du legge til kontoen ved å trykke *Lagre*.
Hvis leverandøren din ikke er på listen over leverandører, velger du *Tilpasset*. Skriv inn domenenavnet, for eksempel *gmail.com* og trykk *Hent innstillinger*. Hvis leverandøren din støtter [auto-oppdagelse](https://tools.ietf.org/html/rfc6186), vil FairEmail fylle ut vertsnavnet og portnummeret, ellers sjekk installasjonsinstruksjonene til leverandøren din for riktig IMAP-vertsnavn, portnummer og protokoll (SSL/TLS eller STARTTLS). For mer om dette, vennligst se [her](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Oppsett identitet - for å sende e-post
For å legge til en identitet, trykk på *Administrer identitet* og trykk på den oransje *legg til* -knappen nederst. Skriv inn navnet du vil skal vises i fra adressen til e-postene du sender, og velg en lenket konto. Trykk på *Lagre* for å legge til identiteten.
Hvis kontoen ble konfigurert manuelt, må du sannsynligvis konfigurere identiteten manuelt. Skriv inn domenenavnet, for eksempel *gmail.com* og trykk *Hent innstillinger*. Hvis leverandøren din støtter [auto-oppdagelse](https://tools.ietf.org/html/rfc6186), vil FairEmail fylle ut vertsnavnet og portnummeret, ellers sjekk installasjonsinstruksjonene til leverandøren din for riktig SMTP vertsnavn, portnummer og protokoll (SSL/TLS eller STARTTLS).
Se [denne FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) om bruk av aliaser.
## Gi tillatelser - for å få tilgang til kontaktinformasjon
Hvis du vil slå opp e-postadresser, ha kontaktbilder vist osv., Må du gi tillatelse til å lese kontakter til FairEmail. Bare trykk på *Gi tillatelser* og velg *Tillat*.
## Konfigurer batterioptimaliseringer - for kontinuerlig å motta e-post
På nyere Android-versjoner vil Android legge appene i dvale når skjermen er slått av en stund for å redusere batteribruken. Hvis du vil motta nye e-postmeldinger uten forsinkelser, bør du deaktivere batterioptimaliseringer for FairEmail. Trykk på *Deaktiver batterioptimaliseringer* og følg instruksjonene.
## Spørsmål eller problemer
Hvis du har et spørsmål eller et problem, kan du [se her](https://github.com/M66B/FairEmail/blob/master/FAQ.md) eller bruk [dette kontaktskjemaet](https://contact.faircode.eu/?product=fairemailsupport) for å be om hjelp (du kan bruke transaksjonsnummeret "*konfigurasjonshjelp*").

View File

@@ -0,0 +1,41 @@
# Pomoc instalacyjna
Konfiguracja FairEmail jest dość prosta. Musisz dodać co najmniej jedno konto, aby otrzymywać wiadomości email i przynajmniej jedną tożsamość, jeśli chcesz wysłać wiadomości email. Szybka konfiguracja doda konto i tożsamość za jednym razem dla większości głównych dostawców.
## Wymagania
Do skonfigurowania kont i tożsamości wymagane jest połączenie internetowe.
## Szybka konfiguracja
Po prostu wpisz swoje imię i nazwisko, adres email i hasło, a następnie naciśnij przycisk *Idź*.
To działa dla większości głównych dostawców poczty email.
Jeśli szybka konfiguracja nie zadziała, musisz skonfigurować konto i tożsamość w inny sposób, patrz poniżej, aby uzyskać instrukcje.
## Konfiguracja konta - aby odbierać wiadomości email
Aby dodać konto, naciśnij *Zarządzaj kontami* i naciśnij pomarańczowy przycisk *dodaj* na dole. Wybierz dostawcę z listy, wprowadź nazwę użytkownika, która jest najczęściej Twoim adresem email i wprowadź hasło. Naciśnij *Sprawdź*, aby FairEmail mógł połączyć się z serwerem email i pobrać listę folderów systemowych. Po zapoznaniu się z wyborem folderu systemowego możesz dodać konto, naciskając *Zapisz*.
Jeśli Twój dostawca nie znajduje się na liście dostawców, wybierz *Własne*. Wprowadź nazwę domeny, na przykład *gmail.com* i naciśnij *Pobierz ustawienia*. Jeśli Twój dostawca obsługuje [automatyczne wykrywanie](https://tools.ietf.org/html/rfc6186), FairEmail wypełni nazwę hosta i numer portu, w przeciwnym razie sprawdź instrukcje instalacji swojego dostawcy, aby uzyskać prawidłową nazwę hosta IMAP, numer portu i protokół (SSL/TLS lub STARTTLS). Więcej informacji na ten temat można znaleźć [tutaj](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Konfiguracja tożsamości - aby wysyłać wiadomości email
Podobnie, aby dodać tożsamość, naciśnij *Zarządzaj tożsamościami* i naciśnij pomarańczowy przycisk *dodaj* na dole. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Помощь по установке
Настройка FairEmail довольно проста. Вам нужно добавить хотя бы одну учетную запись для получения электронной почты и хотя бы один идентификатор, если вы хотите отправлять письма. Быстрая настройка сразу добавит учетную запись и идентификатор для большинства основных провайдеров.
## Требования
Для настройки учетных записей и идентификаторов требуется подключение к Интернету.
## Быстрая настройка
Просто введите имя, адрес электронной почты и пароль и нажмите *Перейти*.
Это будет работать для большинства основных провайдеров электронной почты.
Если быстрая настройка не работает, вам нужно будет настроить учетную запись и идентификатор другим способом, см. ниже для инструкций.
## Настройка учётной записи - для получения электронной почты
Чтобы добавить учётную запись, нажмите *Управление учётными записями* и нажмите на оранжевую кнопку *добавить* внизу. Выберите провайдера из списка, введите имя пользователя, в большинстве случаев ваш адрес электронной почты, и пароль. Нажмите *Проверить* чтобы FairEmail подключился к почтовому серверу и получил список системных папок. После просмотра списка системных папок можно добавить учетную запись, нажав *Сохранить*.
Если ваш провайдер не входит в список провайдеров, выберите *Пользовательский*. Введите имя домена, например *gmail.com* и нажмите *Получить настройки*. Если ваш провайдер поддерживает [авто-обнаружение](https://tools.ietf.org/html/rfc6186), FairEmail заполнит имя хоста и номер порта, еще прочтите инструкции по установке вашего провайдера для правильного имени IMAP хоста, номера порта и протокола (SSL/TLS или STARTTLS). Подробнее об этом смотрите [здесь](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Настройка идентификатора - чтобы отправлять почту
Аналогичным образом, чтобы добавить идентификатор, нажмите *Управление идентификаторами* и нажмите на оранжевую кнопку *добавить* внизу. Введите имя, которое вы хотите вставлять в поле адреса "От:" отправленных вами писем и выберите связанную учетную запись. Нажмите *Сохранить* для добавления идентификатора.
Если учетная запись была настроена вручную, вам, вероятно, также нужно настроить идентификатор вручную. Введите имя домена, например *gmail.com* и нажмите *Получить настройки*. Если ваш провайдер поддерживает [авто-обнаружение](https://tools.ietf.org/html/rfc6186), FairEmail заполнит имя хоста и номер порта, еще прочтите инструкции по установке вашего провайдера для правильного имени хоста SMTP, номера порта и протокола (SSL/TLS или STARTTLS).
Смотрите [этот FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) об использовании псевдонимов.
## Предоставить разрешения - доступ к контактной информации
Если вы хотите находить адреса электронной почты, видеть фотографии контактов, и т. д., вам нужно предоставить FairEmail разрешение на чтение контактов. Просто нажмите *Предоставить разрешения* и выберите *Разрешить*.
## Настройка оптимизации батареи - для непрерывного получения электронной почты
Android последних версий будет помещать приложения в спящий режим, когда экран выключен в течение некоторого времени, чтобы уменьшить потребление батареи. Если вы хотите получать новые письма без задержек, то вам следует отключить оптимизацию батареи для FairEmail. Нажмите *Отключить оптимизацию батареи* и следуйте инструкциям.
## Вопросы или проблемы
Если у вас есть вопрос или проблема, пожалуйста [см. здесь](https://github.com/M66B/FairEmail/blob/master/FAQ.md) или используйте [эту контактную форму](https://contact.faircode.eu/?product=fairemailsupport) для получения помощи (вы можете использовать номер транзакции "*помощь по установке*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,41 @@
# 设置帮助
设置FairEmail相当简单 如果您想发送电邮,需要添加至少一个身份,并添加至少一个账户来接受电邮。 快速设置将为大多数主要电邮服务提供商一举添加一个账户和一个身份。
## 要求
需要互联网连接以设置账户和身份
## 快速设置
只需输入您的姓名、电子邮件地址和密码,并点击*跳转到*。
这对大多数主要的电子邮件服务供应商行之有效。
如果快速设置不起作用,您需要以另一种方式设置一个帐户和身份,见下文说明。
## 设置帐户 - 接收电子邮件
要添加一个帐户,点击*管理帐户*并点击底部橙色的 *添加*按钮。 从列表中选择一个供应商,输入用户名,大多为您的电子邮件地址,并输入您的密码。 点击 *检查* 让FairEmail连接到电子邮件服务器并获取系统文件夹列表。 在检查过系统文件夹选择后,您可以通过点击 *保存*添加账户。
如果您的提供商不在名单中,请选择 *自定义*。 输入域名,例如 *gmail.com*并点击*获取设置*。 如果你的供应商支持 [自动发现](https://tools.ietf.org/html/rfc6186)FairEmail将填写主机名和端口号请您检查IMAP主机名、端口号和协议(SSL/TLS 或 STARTLS)是否正确。 关于这一问题的更多内容,请见[此处](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts)。
## 设置身份 - 发送电子邮件
类似的,要添加一个帐户,点击*管理身份*并点击底部橙色的 *添加*按钮。 输入您想要在您发送的电子邮件的发件人一栏中出现的名称,并选择一个链接的帐户。 点击 *保存* 来添加身份。
如果手动配置了帐户,您也可能需要手动配置身份。 输入域名,例如 *gmail.com*并点击*获取设置*。 如果你的供应商支持 [自动发现](https://tools.ietf.org/html/rfc6186)FairEmail将填写主机名和端口号请您检查SMTP主机名、端口号和协议(SSL/TLS 或 STARTLS)是否正确。
有关使用邮件别名请参阅此 [FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) 。
## 授权权限 - 访问联系人信息
如果您想要查找邮件地址,显示了联系照片等,您需要授予 FairEmail 阅读联系人的权限。 只需点击 *授权*并选择 *允许*
## 设置电池优化 - 持续接收电子邮件
在最近的 Android 版本中,当屏幕关闭一段时间后,系统会让应用进入休眠状态减少电池使用。 如果您希望在无延迟地接收新邮件您应该禁用针对FairEmail的电量优化。 点击*禁用电池优化*并遵循指示。
## 疑问或问题
如果您有问题或疑问,请 [见此处 ](https://github.com/M66B/FairEmail/blob/master/FAQ.md),或使用 [此联系表单](https://contact.faircode.eu/?product=fairemailsupport) 来请求帮助 (您可以使用交易号“*设置帮助*")。

View File

@@ -0,0 +1,41 @@
# Setup help
Setting up FairEmail is fairly simple. You'll need to add at least one account to receive email and at least one identity if you want to send email. The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom. Select a provider from the list, enter the username, which is mostly your email address and enter your password. Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders. After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS). For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom. Enter the name you want to appear in de from address of the emails you send and select a linked account. Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too. Enter the domain name, for example *gmail.com* and tap *Get settings*. If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number, else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail. Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage. If you want to receive new emails without delays, you should disable battery optimizations for FairEmail. Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md) or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

View File

@@ -0,0 +1,66 @@
# Setup help
Setting up FairEmail is fairly simple.
You'll need to add at least one account to receive email and at least one identity if you want to send email.
The quick setup will add an account and an identity in one go for most major providers.
## Requirements
An internet connection is required to set up accounts and identities.
## Quick setup
Just enter your name, email address and password and tap *Go*.
This will work for most major email providers.
If the quick setup doesn't work, you'll need to setup an account and an identity in another way, see below for instructions.
## Set up account - to receive email
To add an account, tap *Manage accounts* and tap the orange *add* button at the bottom.
Select a provider from the list, enter the username, which is mostly your email address and enter your password.
Tap *Check* to let FairEmail connect to the email server and fetch a list of system folders.
After reviewing the system folder selection you can add the account by tapping *Save*.
If your provider is not in the list of providers, select *Custom*.
Enter the domain name, for example *gmail.com* and tap *Get settings*.
If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number,
else check the setup instructions of your provider for the right IMAP hostname, port number and protocol (SSL/TLS or STARTTLS).
For more about this, please see [here](https://github.com/M66B/FairEmail/blob/master/FAQ.md#authorizing-accounts).
## Set up identity - to send email
Similarly, to add an identity, tap *Manage identity* and tap the orange *add* button at the bottom.
Enter the name you want to appear in de from address of the emails you send and select a linked account.
Tap *Save* to add the identity.
If the account was configured manually, you likely need to configure the identity manually too.
Enter the domain name, for example *gmail.com* and tap *Get settings*.
If your provider supports [auto-discovery](https://tools.ietf.org/html/rfc6186), FairEmail will fill in the hostname and port number,
else check the setup instructions of your provider for the right SMTP hostname, port number and protocol (SSL/TLS or STARTTLS).
See [this FAQ](https://github.com/M66B/FairEmail/blob/master/FAQ.md#FAQ9) about using aliases.
## Grant permissions - to access contact information
If you want to lookup email addresses, have contact photos shown, etc, you'll need to grant read contacts permission to FairEmail.
Just tap *Grant permissions* and select *Allow*.
## Setup battery optimizations - to continuously receive emails
On recent Android versions, Android will put apps to sleep when the screen is off for some time to reduce battery usage.
If you want to receive new emails without delays, you should disable battery optimizations for FairEmail.
Tap *Disable battery optimizations* and follow the instructions.
## Questions or problems
If you have a question or problem, please [see here](https://github.com/M66B/FairEmail/blob/master/FAQ.md)
or use [this contact form](https://contact.faircode.eu/?product=fairemailsupport) to ask for help (you can use the transaction number "*setup help*").

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,158 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.lifecycle;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
import androidx.arch.core.executor.ArchTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* A LiveData class that can be invalidated & computed when there are active observers.
* <p>
* It can be invalidated via {@link #invalidate()}, which will result in a call to
* {@link #compute()} if there are active observers (or when they start observing)
* <p>
* This is an internal class for now, might be public if we see the necessity.
*
* @param <T> The type of the live data
* @hide internal
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public abstract class ComputableLiveData<T> {
@SuppressWarnings("WeakerAccess") /* synthetic access */
final Executor mExecutor;
@SuppressWarnings("WeakerAccess") /* synthetic access */
final LiveData<T> mLiveData;
@SuppressWarnings("WeakerAccess") /* synthetic access */
final AtomicBoolean mInvalid = new AtomicBoolean(true);
@SuppressWarnings("WeakerAccess") /* synthetic access */
final AtomicBoolean mComputing = new AtomicBoolean(false);
/**
* Creates a computable live data that computes values on the arch IO thread executor.
*/
@SuppressWarnings("WeakerAccess")
public ComputableLiveData() {
this(ArchTaskExecutor.getIOThreadExecutor());
}
/**
* Creates a computable live data that computes values on the specified executor.
*
* @param executor Executor that is used to compute new LiveData values.
*/
@SuppressWarnings("WeakerAccess")
public ComputableLiveData(@NonNull Executor executor) {
mExecutor = executor;
mLiveData = new LiveData<T>() {
@Override
protected void onActive() {
mExecutor.execute(mRefreshRunnable);
}
};
}
/**
* Returns the LiveData managed by this class.
*
* @return A LiveData that is controlled by ComputableLiveData.
*/
@SuppressWarnings("WeakerAccess")
@NonNull
public LiveData<T> getLiveData() {
return mLiveData;
}
@VisibleForTesting
final Runnable mRefreshRunnable = new Runnable() {
@WorkerThread
@Override
public void run() {
boolean computed;
long last;
do {
computed = false;
// compute can happen only in 1 thread but no reason to lock others.
if (mComputing.compareAndSet(false, true)) {
// as long as it is invalid, keep computing.
try {
last = android.os.SystemClock.elapsedRealtime();
T value = null;
while (mInvalid.compareAndSet(true, false)) {
long now = android.os.SystemClock.elapsedRealtime();
if (last + 1500 < now && value != null) {
eu.faircode.email.Log.i(mLiveData + " post age=" + (now - last));
last = now;
mLiveData.postValue(value);
}
computed = true;
value = compute();
}
if (computed) {
mLiveData.postValue(value);
}
} finally {
// release compute lock
mComputing.set(false);
}
}
// check invalid after releasing compute lock to avoid the following scenario.
// Thread A runs compute()
// Thread A checks invalid, it is false
// Main thread sets invalid to true
// Thread B runs, fails to acquire compute lock and skips
// Thread A releases compute lock
// We've left invalid in set state. The check below recovers.
} while (computed && mInvalid.get());
}
};
// invalidation check always happens on the main thread
@VisibleForTesting
final Runnable mInvalidationRunnable = new Runnable() {
@MainThread
@Override
public void run() {
boolean isActive = mLiveData.hasActiveObservers();
if (mInvalid.compareAndSet(false, true)) {
if (isActive) {
mExecutor.execute(mRefreshRunnable);
}
}
}
};
/**
* Invalidates the LiveData.
* <p>
* When there are active observers, this will trigger a call to {@link #compute()}.
*/
public void invalidate() {
ArchTaskExecutor.getInstance().executeOnMainThread(mInvalidationRunnable);
}
// TODO https://issuetracker.google.com/issues/112197238
@SuppressWarnings({"WeakerAccess", "UnknownNullness"})
@WorkerThread
protected abstract T compute();
}

View File

@@ -0,0 +1,465 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.lifecycle;
import static androidx.lifecycle.Lifecycle.State.DESTROYED;
import static androidx.lifecycle.Lifecycle.State.STARTED;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.arch.core.executor.ArchTaskExecutor;
import androidx.arch.core.internal.SafeIterableMap;
import java.util.Iterator;
import java.util.Map;
/**
* LiveData is a data holder class that can be observed within a given lifecycle.
* This means that an {@link Observer} can be added in a pair with a {@link LifecycleOwner}, and
* this observer will be notified about modifications of the wrapped data only if the paired
* LifecycleOwner is in active state. LifecycleOwner is considered as active, if its state is
* {@link Lifecycle.State#STARTED} or {@link Lifecycle.State#RESUMED}. An observer added via
* {@link #observeForever(Observer)} is considered as always active and thus will be always notified
* about modifications. For those observers, you should manually call
* {@link #removeObserver(Observer)}.
*
* <p> An observer added with a Lifecycle will be automatically removed if the corresponding
* Lifecycle moves to {@link Lifecycle.State#DESTROYED} state. This is especially useful for
* activities and fragments where they can safely observe LiveData and not worry about leaks:
* they will be instantly unsubscribed when they are destroyed.
*
* <p>
* In addition, LiveData has {@link LiveData#onActive()} and {@link LiveData#onInactive()} methods
* to get notified when number of active {@link Observer}s change between 0 and 1.
* This allows LiveData to release any heavy resources when it does not have any Observers that
* are actively observing.
* <p>
* This class is designed to hold individual data fields of {@link ViewModel},
* but can also be used for sharing data between different modules in your application
* in a decoupled fashion.
*
* @param <T> The type of data held by this instance
* @see ViewModel
*/
public abstract class LiveData<T> {
@SuppressWarnings("WeakerAccess") /* synthetic access */
final Object mDataLock = new Object();
static final int START_VERSION = -1;
@SuppressWarnings("WeakerAccess") /* synthetic access */
static final Object NOT_SET = new Object();
private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers =
new SafeIterableMap<>();
// how many observers are in active state
@SuppressWarnings("WeakerAccess") /* synthetic access */
int mActiveCount = 0;
private volatile Object mData;
// when setData is called, we set the pending data and actual data swap happens on the main
// thread
@SuppressWarnings("WeakerAccess") /* synthetic access */
volatile Object mPendingData = NOT_SET;
private int mVersion;
private boolean mDispatchingValue;
@SuppressWarnings("FieldCanBeLocal")
private boolean mDispatchInvalidated;
private final Runnable mPostValueRunnable = new Runnable() {
@Override
public void run() {
Object newValue;
synchronized (mDataLock) {
newValue = mPendingData;
mPendingData = NOT_SET;
}
//noinspection unchecked
setValue((T) newValue);
}
};
/**
* Creates a LiveData initialized with the given {@code value}.
*
* @param value initial value
*/
public LiveData(T value) {
mData = value;
mVersion = START_VERSION + 1;
}
/**
* Creates a LiveData with no value assigned to it.
*/
public LiveData() {
mData = NOT_SET;
mVersion = START_VERSION;
}
private void considerNotify(ObserverWrapper observer) {
if (!observer.mActive) {
return;
}
// Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
//
// we still first check observer.active to keep it as the entrance for events. So even if
// the observer moved to an active state, if we've not received that event, we better not
// notify for a more predictable notification order.
if (!observer.shouldBeActive()) {
observer.activeStateChanged(false);
return;
}
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
//noinspection unchecked
observer.mObserver.onChanged((T) mData);
}
@SuppressWarnings("WeakerAccess") /* synthetic access */
void dispatchingValue(@Nullable ObserverWrapper initiator) {
if (mDispatchingValue) {
mDispatchInvalidated = true;
return;
}
mDispatchingValue = true;
do {
mDispatchInvalidated = false;
if (initiator != null) {
considerNotify(initiator);
initiator = null;
} else {
for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
considerNotify(iterator.next().getValue());
if (mDispatchInvalidated) {
break;
}
}
}
} while (mDispatchInvalidated);
mDispatchingValue = false;
}
/**
* Adds the given observer to the observers list within the lifespan of the given
* owner. The events are dispatched on the main thread. If LiveData already has data
* set, it will be delivered to the observer.
* <p>
* The observer will only receive events if the owner is in {@link Lifecycle.State#STARTED}
* or {@link Lifecycle.State#RESUMED} state (active).
* <p>
* If the owner moves to the {@link Lifecycle.State#DESTROYED} state, the observer will
* automatically be removed.
* <p>
* When data changes while the {@code owner} is not active, it will not receive any updates.
* If it becomes active again, it will receive the last available data automatically.
* <p>
* LiveData keeps a strong reference to the observer and the owner as long as the
* given LifecycleOwner is not destroyed. When it is destroyed, LiveData removes references to
* the observer &amp; the owner.
* <p>
* If the given owner is already in {@link Lifecycle.State#DESTROYED} state, LiveData
* ignores the call.
* <p>
* If the given owner, observer tuple is already in the list, the call is ignored.
* If the observer is already in the list with another owner, LiveData throws an
* {@link IllegalArgumentException}.
*
* @param owner The LifecycleOwner which controls the observer
* @param observer The observer that will receive the events
*/
@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
assertMainThread("observe");
if (owner.getLifecycle().getCurrentState() == DESTROYED) {
// ignore
return;
}
LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing != null && !existing.isAttachedTo(owner)) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
owner.getLifecycle().addObserver(wrapper);
}
/**
* Adds the given observer to the observers list. This call is similar to
* {@link LiveData#observe(LifecycleOwner, Observer)} with a LifecycleOwner, which
* is always active. This means that the given observer will receive all events and will never
* be automatically removed. You should manually call {@link #removeObserver(Observer)} to stop
* observing this LiveData.
* While LiveData has one of such observers, it will be considered
* as active.
* <p>
* If the observer was already added with an owner to this LiveData, LiveData throws an
* {@link IllegalArgumentException}.
*
* @param observer The observer that will receive the events
*/
@MainThread
public void observeForever(@NonNull Observer<? super T> observer) {
assertMainThread("observeForever");
AlwaysActiveObserver wrapper = new AlwaysActiveObserver(observer);
ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
if (existing instanceof LiveData.LifecycleBoundObserver) {
throw new IllegalArgumentException("Cannot add the same observer"
+ " with different lifecycles");
}
if (existing != null) {
return;
}
wrapper.activeStateChanged(true);
}
/**
* Removes the given observer from the observers list.
*
* @param observer The Observer to receive events.
*/
@MainThread
public void removeObserver(@NonNull final Observer<? super T> observer) {
assertMainThread("removeObserver");
ObserverWrapper removed = mObservers.remove(observer);
if (removed == null) {
return;
}
removed.detachObserver();
removed.activeStateChanged(false);
}
/**
* Removes all observers that are tied to the given {@link LifecycleOwner}.
*
* @param owner The {@code LifecycleOwner} scope for the observers to be removed.
*/
@SuppressWarnings("WeakerAccess")
@MainThread
public void removeObservers(@NonNull final LifecycleOwner owner) {
assertMainThread("removeObservers");
for (Map.Entry<Observer<? super T>, ObserverWrapper> entry : mObservers) {
if (entry.getValue().isAttachedTo(owner)) {
removeObserver(entry.getKey());
}
}
}
/**
* Posts a task to a main thread to set the given value. So if you have a following code
* executed in the main thread:
* <pre class="prettyprint">
* liveData.postValue("a");
* liveData.setValue("b");
* </pre>
* The value "b" would be set at first and later the main thread would override it with
* the value "a".
* <p>
* If you called this method multiple times before a main thread executed a posted task, only
* the last value would be dispatched.
*
* @param value The new value
*/
protected void postValue(T value) {
boolean postTask;
synchronized (mDataLock) {
postTask = mPendingData == NOT_SET;
mPendingData = value;
}
if (!postTask) {
return;
}
ArchTaskExecutor.getInstance().postToMainThread(mPostValueRunnable);
}
/**
* Sets the value. If there are active observers, the value will be dispatched to them.
* <p>
* This method must be called from the main thread. If you need set a value from a background
* thread, you can use {@link #postValue(Object)}
*
* @param value The new value
*/
@MainThread
protected void setValue(T value) {
assertMainThread("setValue");
mVersion++;
mData = value;
dispatchingValue(null);
}
/**
* Returns the current value.
* Note that calling this method on a background thread does not guarantee that the latest
* value set will be received.
*
* @return the current value
*/
@Nullable
public T getValue() {
Object data = mData;
if (data != NOT_SET) {
//noinspection unchecked
return (T) data;
}
return null;
}
int getVersion() {
return mVersion;
}
/**
* Called when the number of active observers change to 1 from 0.
* <p>
* This callback can be used to know that this LiveData is being used thus should be kept
* up to date.
*/
protected void onActive() {
}
/**
* Called when the number of active observers change from 1 to 0.
* <p>
* This does not mean that there are no observers left, there may still be observers but their
* lifecycle states aren't {@link Lifecycle.State#STARTED} or {@link Lifecycle.State#RESUMED}
* (like an Activity in the back stack).
* <p>
* You can check if there are observers via {@link #hasObservers()}.
*/
protected void onInactive() {
}
/**
* Returns true if this LiveData has observers.
*
* @return true if this LiveData has observers
*/
@SuppressWarnings("WeakerAccess")
public boolean hasObservers() {
return mObservers.size() > 0;
}
/**
* Returns true if this LiveData has active observers.
*
* @return true if this LiveData has active observers
*/
@SuppressWarnings("WeakerAccess")
public boolean hasActiveObservers() {
return mActiveCount > 0;
}
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
@NonNull
final LifecycleOwner mOwner;
LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
super(observer);
mOwner = owner;
}
@Override
boolean shouldBeActive() {
return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
}
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
removeObserver(mObserver);
return;
}
activeStateChanged(shouldBeActive());
}
@Override
boolean isAttachedTo(LifecycleOwner owner) {
return mOwner == owner;
}
@Override
void detachObserver() {
mOwner.getLifecycle().removeObserver(this);
}
}
private abstract class ObserverWrapper {
final Observer<? super T> mObserver;
boolean mActive;
int mLastVersion = START_VERSION;
ObserverWrapper(Observer<? super T> observer) {
mObserver = observer;
}
abstract boolean shouldBeActive();
boolean isAttachedTo(LifecycleOwner owner) {
return false;
}
void detachObserver() {
}
void activeStateChanged(boolean newActive) {
if (newActive == mActive) {
return;
}
// immediately set active state, so we'd never dispatch anything to inactive
// owner
mActive = newActive;
boolean wasInactive = LiveData.this.mActiveCount == 0;
LiveData.this.mActiveCount += mActive ? 1 : -1;
if (wasInactive && mActive) {
onActive();
}
if (LiveData.this.mActiveCount == 0 && !mActive) {
onInactive();
}
if (mActive) {
dispatchingValue(this);
}
}
}
private class AlwaysActiveObserver extends ObserverWrapper {
AlwaysActiveObserver(Observer<? super T> observer) {
super(observer);
}
@Override
boolean shouldBeActive() {
return true;
}
}
static void assertMainThread(String methodName) {
if (!ArchTaskExecutor.getInstance().isMainThread()) {
throw new IllegalStateException("Cannot invoke " + methodName + " on a background"
+ " thread");
}
}
}

View File

@@ -0,0 +1,156 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.lifecycle;
import androidx.annotation.CallSuper;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.arch.core.internal.SafeIterableMap;
import java.util.Map;
/**
* {@link LiveData} subclass which may observe other {@code LiveData} objects and react on
* {@code OnChanged} events from them.
* <p>
* This class correctly propagates its active/inactive states down to source {@code LiveData}
* objects.
* <p>
* Consider the following scenario: we have 2 instances of {@code LiveData}, let's name them
* {@code liveData1} and {@code liveData2}, and we want to merge their emissions in one object:
* {@code liveDataMerger}. Then, {@code liveData1} and {@code liveData2} will become sources for
* the {@code MediatorLiveData liveDataMerger} and every time {@code onChanged} callback
* is called for either of them, we set a new value in {@code liveDataMerger}.
*
* <pre>
* LiveData<Integer> liveData1 = ...;
* LiveData<Integer> liveData2 = ...;
*
* MediatorLiveData<Integer> liveDataMerger = new MediatorLiveData<>();
* liveDataMerger.addSource(liveData1, value -> liveDataMerger.setValue(value));
* liveDataMerger.addSource(liveData2, value -> liveDataMerger.setValue(value));
* </pre>
* <p>
* Let's consider that we only want 10 values emitted by {@code liveData1}, to be
* merged in the {@code liveDataMerger}. Then, after 10 values, we can stop listening to {@code
* liveData1} and remove it as a source.
* <pre>
* liveDataMerger.addSource(liveData1, new Observer<Integer>() {
* private int count = 1;
*
* {@literal @}Override public void onChanged(@Nullable Integer s) {
* count++;
* liveDataMerger.setValue(s);
* if (count > 10) {
* liveDataMerger.removeSource(liveData1);
* }
* }
* });
* </pre>
*
* @param <T> The type of data hold by this instance
*/
@SuppressWarnings("WeakerAccess")
public class MediatorLiveData<T> extends MutableLiveData<T> {
private SafeIterableMap<LiveData<?>, Source<?>> mSources = new SafeIterableMap<>();
/**
* Starts to listen the given {@code source} LiveData, {@code onChanged} observer will be called
* when {@code source} value was changed.
* <p>
* {@code onChanged} callback will be called only when this {@code MediatorLiveData} is active.
* <p> If the given LiveData is already added as a source but with a different Observer,
* {@link IllegalArgumentException} will be thrown.
*
* @param source the {@code LiveData} to listen to
* @param onChanged The observer that will receive the events
* @param <S> The type of data hold by {@code source} LiveData
*/
@MainThread
public <S> void addSource(@NonNull LiveData<S> source, @NonNull Observer<? super S> onChanged) {
Source<S> e = new Source<>(source, onChanged);
Source<?> existing = mSources.putIfAbsent(source, e);
if (existing != null && existing.mObserver != onChanged) {
throw new IllegalArgumentException(
"This source was already added with the different observer");
}
if (existing != null) {
return;
}
if (hasActiveObservers()) {
e.plug();
}
}
/**
* Stops to listen the given {@code LiveData}.
*
* @param toRemote {@code LiveData} to stop to listen
* @param <S> the type of data hold by {@code source} LiveData
*/
@MainThread
public <S> void removeSource(@NonNull LiveData<S> toRemote) {
Source<?> source = mSources.remove(toRemote);
if (source != null) {
source.unplug();
}
}
@CallSuper
@Override
protected void onActive() {
for (Map.Entry<LiveData<?>, Source<?>> source : mSources) {
source.getValue().plug();
}
}
@CallSuper
@Override
protected void onInactive() {
for (Map.Entry<LiveData<?>, Source<?>> source : mSources) {
source.getValue().unplug();
}
}
private static class Source<V> implements Observer<V> {
final LiveData<V> mLiveData;
final Observer<? super V> mObserver;
int mVersion = START_VERSION;
Source(LiveData<V> liveData, final Observer<? super V> observer) {
mLiveData = liveData;
mObserver = observer;
}
void plug() {
mLiveData.observeForever(this);
}
void unplug() {
mLiveData.removeObserver(this);
}
@Override
public void onChanged(@Nullable V v) {
if (mVersion != mLiveData.getVersion()) {
mVersion = mLiveData.getVersion();
mObserver.onChanged(v);
}
}
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.lifecycle;
/**
* {@link LiveData} which publicly exposes {@link #setValue(T)} and {@link #postValue(T)} method.
*
* @param <T> The type of data hold by this instance
*/
@SuppressWarnings("WeakerAccess")
public class MutableLiveData<T> extends LiveData<T> {
/**
* Creates a MutableLiveData initialized with the given {@code value}.
*
* @param value initial value
*/
public MutableLiveData(T value) {
super(value);
}
/**
* Creates a MutableLiveData with no value assigned to it.
*/
public MutableLiveData() {
super();
}
@Override
public void postValue(T value) {
super.postValue(value);
}
@Override
public void setValue(T value) {
super.setValue(value);
}
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.lifecycle;
/**
* A simple callback that can receive from {@link LiveData}.
*
* @param <T> The type of the parameter
*
* @see LiveData LiveData - for a usage description.
*/
public interface Observer<T> {
/**
* Called when the data is changed.
* @param t The new data
*/
void onChanged(T t);
}

View File

@@ -0,0 +1,194 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.lifecycle;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.arch.core.util.Function;
/**
* Transformation methods for {@link LiveData}.
* <p>
* These methods permit functional composition and delegation of {@link LiveData} instances. The
* transformations are calculated lazily, and will run only when the returned {@link LiveData} is
* observed. Lifecycle behavior is propagated from the input {@code source} {@link LiveData} to the
* returned one.
*/
@SuppressWarnings("WeakerAccess")
public class Transformations {
private Transformations() {
}
/**
* Returns a {@code LiveData} mapped from the input {@code source} {@code LiveData} by applying
* {@code mapFunction} to each value set on {@code source}.
* <p>
* This method is analogous to {@link io.reactivex.Observable#map}.
* <p>
* {@code transform} will be executed on the main thread.
* <p>
* Here is an example mapping a simple {@code User} struct in a {@code LiveData} to a
* {@code LiveData} containing their full name as a {@code String}.
*
* <pre>
* LiveData<User> userLiveData = ...;
* LiveData<String> userFullNameLiveData =
* Transformations.map(
* userLiveData,
* user -> user.firstName + user.lastName);
* });
* </pre>
*
* @param source the {@code LiveData} to map from
* @param mapFunction a function to apply to each value set on {@code source} in order to set
* it
* on the output {@code LiveData}
* @param <X> the generic type parameter of {@code source}
* @param <Y> the generic type parameter of the returned {@code LiveData}
* @return a LiveData mapped from {@code source} to type {@code <Y>} by applying
* {@code mapFunction} to each value set.
*/
@MainThread
@NonNull
public static <X, Y> LiveData<Y> map(
@NonNull LiveData<X> source,
@NonNull final Function<X, Y> mapFunction) {
final MediatorLiveData<Y> result = new MediatorLiveData<>();
result.addSource(source, new Observer<X>() {
@Override
public void onChanged(@Nullable X x) {
result.setValue(mapFunction.apply(x));
}
});
return result;
}
/**
* Returns a {@code LiveData} mapped from the input {@code source} {@code LiveData} by applying
* {@code switchMapFunction} to each value set on {@code source}.
* <p>
* The returned {@code LiveData} delegates to the most recent {@code LiveData} created by
* calling {@code switchMapFunction} with the most recent value set to {@code source}, without
* changing the reference. In this way, {@code switchMapFunction} can change the 'backing'
* {@code LiveData} transparently to any observer registered to the {@code LiveData} returned
* by {@code switchMap()}.
* <p>
* Note that when the backing {@code LiveData} is switched, no further values from the older
* {@code LiveData} will be set to the output {@code LiveData}. In this way, the method is
* analogous to {@link io.reactivex.Observable#switchMap}.
* <p>
* {@code switchMapFunction} will be executed on the main thread.
* <p>
* Here is an example class that holds a typed-in name of a user
* {@code String} (such as from an {@code EditText}) in a {@link MutableLiveData} and
* returns a {@code LiveData} containing a List of {@code User} objects for users that have
* that name. It populates that {@code LiveData} by requerying a repository-pattern object
* each time the typed name changes.
* <p>
* This {@code ViewModel} would permit the observing UI to update "live" as the user ID text
* changes.
*
* <pre>
* class UserViewModel extends AndroidViewModel {
* MutableLiveData<String> nameQueryLiveData = ...
*
* LiveData<List<String>> getUsersWithNameLiveData() {
* return Transformations.switchMap(
* nameQueryLiveData,
* name -> myDataSource.getUsersWithNameLiveData(name));
* }
*
* void setNameQuery(String name) {
* this.nameQueryLiveData.setValue(name);
* }
* }
* </pre>
*
* @param source the {@code LiveData} to map from
* @param switchMapFunction a function to apply to each value set on {@code source} to create a
* new delegate {@code LiveData} for the returned one
* @param <X> the generic type parameter of {@code source}
* @param <Y> the generic type parameter of the returned {@code LiveData}
* @return a LiveData mapped from {@code source} to type {@code <Y>} by delegating
* to the LiveData returned by applying {@code switchMapFunction} to each
* value set
*/
@MainThread
@NonNull
public static <X, Y> LiveData<Y> switchMap(
@NonNull LiveData<X> source,
@NonNull final Function<X, LiveData<Y>> switchMapFunction) {
final MediatorLiveData<Y> result = new MediatorLiveData<>();
result.addSource(source, new Observer<X>() {
LiveData<Y> mSource;
@Override
public void onChanged(@Nullable X x) {
LiveData<Y> newLiveData = switchMapFunction.apply(x);
if (mSource == newLiveData) {
return;
}
if (mSource != null) {
result.removeSource(mSource);
}
mSource = newLiveData;
if (mSource != null) {
result.addSource(mSource, new Observer<Y>() {
@Override
public void onChanged(@Nullable Y y) {
result.setValue(y);
}
});
}
}
});
return result;
}
/**
* Creates a new {@link LiveData} object that does not emit a value until the source LiveData
* value has been changed. The value is considered changed if {@code equals()} yields
* {@code false}.
*
* @param source the input {@link LiveData}
* @param <X> the generic type parameter of {@code source}
* @return a new {@link LiveData} of type {@code X}
*/
@MainThread
@NonNull
public static <X> LiveData<X> distinctUntilChanged(@NonNull LiveData<X> source) {
final MediatorLiveData<X> outputLiveData = new MediatorLiveData<>();
outputLiveData.addSource(source, new Observer<X>() {
boolean mFirstTime = true;
@Override
public void onChanged(X currentValue) {
final X previousValue = outputLiveData.getValue();
if (mFirstTime
|| (previousValue == null && currentValue != null)
|| (previousValue != null && !previousValue.equals(currentValue))) {
mFirstTime = false;
outputLiveData.setValue(currentValue);
}
}
});
return outputLiveData;
}
}

View File

@@ -16,19 +16,22 @@
package androidx.recyclerview.selection;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import static androidx.annotation.VisibleForTesting.PACKAGE_PRIVATE;
import android.graphics.Point;
import androidx.annotation.NonNull;
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
/**
* Provides support for auto-scrolling a view.
*
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@RestrictTo(LIBRARY)
@VisibleForTesting(otherwise = PACKAGE_PRIVATE)
public abstract class AutoScroller {
/**

View File

@@ -16,7 +16,7 @@
package androidx.recyclerview.selection;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import static androidx.core.util.Preconditions.checkArgument;
import static androidx.core.util.Preconditions.checkState;
import static androidx.recyclerview.selection.Shared.DEBUG;
@@ -49,7 +49,7 @@ import java.util.Set;
*
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@RestrictTo(LIBRARY)
public class DefaultSelectionTracker<K> extends SelectionTracker<K> {
private static final String TAG = "DefaultSelectionTracker";
@@ -296,6 +296,11 @@ public class DefaultSelectionTracker<K> extends SelectionTracker<K> {
private void extendRange(int position, @RangeType int type) {
checkState(isRangeActive(), "Range start point not set.");
if (position == RecyclerView.NO_POSITION) {
Log.w(TAG, "Invalid position: Cannot extend selection to: " + position);
return;
}
mRange.extendRange(position, type);
// We're being lazy here notifying even when something might not have changed.
@@ -343,6 +348,10 @@ public class DefaultSelectionTracker<K> extends SelectionTracker<K> {
return mRange != null;
}
boolean isOverlapping(int position, int count) {
return (mRange != null && mRange.isOverlapping(position, count));
}
private boolean canSetState(@NonNull K key, boolean nextState) {
return mSelectionPredicate.canSetStateForKey(key, nextState);
}
@@ -573,17 +582,21 @@ public class DefaultSelectionTracker<K> extends SelectionTracker<K> {
@Override
public void onItemRangeInserted(int startPosition, int itemCount) {
mSelectionTracker.endRange();
if (mSelectionTracker.isOverlapping(startPosition, itemCount))
mSelectionTracker.endRange();
}
@Override
public void onItemRangeRemoved(int startPosition, int itemCount) {
mSelectionTracker.endRange();
if (mSelectionTracker.isOverlapping(startPosition, itemCount))
mSelectionTracker.endRange();
}
@Override
public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
mSelectionTracker.endRange();
if (mSelectionTracker.isOverlapping(fromPosition, itemCount) ||
mSelectionTracker.isOverlapping(toPosition, itemCount))
mSelectionTracker.endRange();
}
}
}

View File

@@ -16,7 +16,7 @@
package androidx.recyclerview.selection;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import static androidx.annotation.VisibleForTesting.PACKAGE_PRIVATE;
import static androidx.core.util.Preconditions.checkArgument;
import static androidx.recyclerview.selection.Shared.VERBOSE;
@@ -38,7 +38,7 @@ import androidx.recyclerview.widget.RecyclerView;
*
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@RestrictTo(LIBRARY)
@VisibleForTesting(otherwise = PACKAGE_PRIVATE)
public class EventBridge {

View File

@@ -94,11 +94,17 @@ final class GestureSelectionHelper implements OnItemTouchListener {
@Override
/** @hide */
public boolean onInterceptTouchEvent(@NonNull RecyclerView unused, @NonNull MotionEvent e) {
// TODO(b/132447183): For some reason we're not receiving an ACTION_UP
// event after a > long-press NOT followed by a ACTION_MOVE < event.
if (mStarted) {
handleTouch(e);
}
switch (e.getActionMasked()) {
case MotionEvent.ACTION_MOVE:
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
return mStarted && mSelectionMgr.isRangeActive();
case MotionEvent.ACTION_UP:
return mStarted;
default:
return false;
}
@@ -127,32 +133,25 @@ final class GestureSelectionHelper implements OnItemTouchListener {
* so this methods return value is irrelevant to it.
* </ol>
*/
private boolean handleTouch(MotionEvent e) {
if (!mStarted) {
return false;
}
private void handleTouch(MotionEvent e) {
if (!mSelectionMgr.isRangeActive()) {
Log.e(TAG,
"Internal state of GestureSelectionHelper out of sync w/ SelectionTracker "
+ "(isRangeActive is false). Ignoring event and resetting state.");
endSelection();
return false;
}
switch (e.getActionMasked()) {
case MotionEvent.ACTION_MOVE:
handleMoveEvent(e);
return true;
break;
case MotionEvent.ACTION_UP:
handleUpEvent();
return true;
break;
case MotionEvent.ACTION_CANCEL:
handleCancelEvent();
return true;
break;
}
return false;
}
@Override

View File

@@ -16,7 +16,7 @@
package androidx.recyclerview.selection;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import android.view.MotionEvent;
@@ -72,7 +72,7 @@ public abstract class ItemDetailsLookup<K> {
* @return true if there is an item w/ a stable ID at the event coordinates.
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@RestrictTo(LIBRARY)
protected boolean overItemWithSelectionKey(@NonNull MotionEvent e) {
return overItem(e) && hasSelectionKey(getItemDetails(e));
}

View File

@@ -170,6 +170,11 @@ final class Range {
mCallbacks.updateForRange(begin, end, selected, type);
}
boolean isOverlapping(int position, int count) {
return (position >= mBegin && position <= mEnd) ||
(position + count >= mBegin && position + count <= mEnd);
}
@Override
public String toString() {
return "Range{begin=" + mBegin + ", end=" + mEnd + "}";

View File

@@ -16,7 +16,7 @@
package androidx.recyclerview.selection;
import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
import static androidx.annotation.RestrictTo.Scope.LIBRARY;
import static androidx.core.util.Preconditions.checkArgument;
import android.content.Context;
@@ -180,7 +180,7 @@ public abstract class SelectionTracker<K> {
public abstract boolean deselect(@NonNull K key);
/** @hide */
@RestrictTo(LIBRARY_GROUP)
@RestrictTo(LIBRARY)
protected abstract AdapterDataObserver getAdapterDataObserver();
/**
@@ -192,7 +192,7 @@ public abstract class SelectionTracker<K> {
* work with the established anchor point to define selection ranges.
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@RestrictTo(LIBRARY)
public abstract void startRange(int position);
/**
@@ -208,7 +208,7 @@ public abstract class SelectionTracker<K> {
* must have been started by a call to {@link #startRange(int)}.
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@RestrictTo(LIBRARY)
public abstract void extendRange(int position);
/**
@@ -217,14 +217,14 @@ public abstract class SelectionTracker<K> {
* {@link #mergeProvisionalSelection()} is called first.)
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@RestrictTo(LIBRARY)
public abstract void endRange();
/**
* @return Whether or not there is a current range selection active.
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@RestrictTo(LIBRARY)
public abstract boolean isRangeActive();
/**
@@ -237,7 +237,7 @@ public abstract class SelectionTracker<K> {
* @param position the anchor position. Must already be selected.
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@RestrictTo(LIBRARY)
public abstract void anchorRange(int position);
/**
@@ -246,7 +246,7 @@ public abstract class SelectionTracker<K> {
* @param position the end point.
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@RestrictTo(LIBRARY)
protected abstract void extendProvisionalRange(int position);
/**
@@ -254,14 +254,14 @@ public abstract class SelectionTracker<K> {
* @param newSelection
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@RestrictTo(LIBRARY)
protected abstract void setProvisionalSelection(@NonNull Set<K> newSelection);
/**
* Clears any existing provisional selection
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@RestrictTo(LIBRARY)
protected abstract void clearProvisionalSelection();
/**
@@ -269,7 +269,7 @@ public abstract class SelectionTracker<K> {
* provisional selection.
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@RestrictTo(LIBRARY)
protected abstract void mergeProvisionalSelection();
/**
@@ -769,7 +769,7 @@ public abstract class SelectionTracker<K> {
try {
gestureHelper.start();
} catch (IllegalStateException ex) {
ex.printStackTrace();
eu.faircode.email.Log.w(ex);
}
}
}

View File

@@ -0,0 +1,294 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.room;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.sqlite.db.SupportSQLiteOpenHelper;
import java.io.File;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;
/**
* Configuration class for a {@link RoomDatabase}.
*/
@SuppressWarnings("WeakerAccess")
public class DatabaseConfiguration {
/**
* The factory to use to access the database.
*/
@NonNull
public final SupportSQLiteOpenHelper.Factory sqliteOpenHelperFactory;
/**
* The context to use while connecting to the database.
*/
@NonNull
public final Context context;
/**
* The name of the database file or null if it is an in-memory database.
*/
@Nullable
public final String name;
/**
* Collection of available migrations.
*/
@NonNull
public final RoomDatabase.MigrationContainer migrationContainer;
@Nullable
public final List<RoomDatabase.Callback> callbacks;
/**
* Whether Room should throw an exception for queries run on the main thread.
*/
public final boolean allowMainThreadQueries;
/**
* The journal mode for this database.
*/
public final RoomDatabase.JournalMode journalMode;
/**
* The Executor used to execute asynchronous queries.
*/
@NonNull
public final Executor queryExecutor;
/**
* The Executor used to execute asynchronous transactions.
*/
@NonNull
public final Executor transactionExecutor;
/**
* If true, table invalidation in an instance of {@link RoomDatabase} is broadcast and
* synchronized with other instances of the same {@link RoomDatabase} file, including those
* in a separate process.
*/
public final boolean multiInstanceInvalidation;
/**
* If true, Room should crash if a migration is missing.
*/
public final boolean requireMigration;
/**
* If true, Room should perform a destructive migration when downgrading without an available
* migration.
*/
public final boolean allowDestructiveMigrationOnDowngrade;
/**
* The collection of schema versions from which migrations aren't required.
*/
private final Set<Integer> mMigrationNotRequiredFrom;
/**
* The assets path to a pre-packaged database to copy from.
*/
@Nullable
public final String copyFromAssetPath;
/**
* The pre-packaged database file to copy from.
*/
@Nullable
public final File copyFromFile;
/**
* Creates a database configuration with the given values.
*
* @deprecated Use {@link #DatabaseConfiguration(Context, String,
* SupportSQLiteOpenHelper.Factory, RoomDatabase.MigrationContainer, List, boolean,
* RoomDatabase.JournalMode, Executor, Executor, boolean, boolean, boolean, Set, String, File)}
*
* @param context The application context.
* @param name Name of the database, can be null if it is in memory.
* @param sqliteOpenHelperFactory The open helper factory to use.
* @param migrationContainer The migration container for migrations.
* @param callbacks The list of callbacks for database events.
* @param allowMainThreadQueries Whether to allow main thread reads/writes or not.
* @param journalMode The journal mode. This has to be either TRUNCATE or WRITE_AHEAD_LOGGING.
* @param queryExecutor The Executor used to execute asynchronous queries.
* @param requireMigration True if Room should require a valid migration if version changes,
* instead of recreating the tables.
* @param migrationNotRequiredFrom The collection of schema versions from which migrations
* aren't required.
*
* @hide
*/
@Deprecated
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public DatabaseConfiguration(@NonNull Context context, @Nullable String name,
@NonNull SupportSQLiteOpenHelper.Factory sqliteOpenHelperFactory,
@NonNull RoomDatabase.MigrationContainer migrationContainer,
@Nullable List<androidx.room.RoomDatabase.Callback> callbacks,
boolean allowMainThreadQueries,
RoomDatabase.JournalMode journalMode,
@NonNull Executor queryExecutor,
boolean requireMigration,
@Nullable Set<Integer> migrationNotRequiredFrom) {
this(context, name, sqliteOpenHelperFactory, migrationContainer, callbacks,
allowMainThreadQueries, journalMode, queryExecutor, queryExecutor, false,
requireMigration, false, migrationNotRequiredFrom, null, null);
}
/**
* Creates a database configuration with the given values.
*
* @deprecated Use {@link #DatabaseConfiguration(Context, String,
* SupportSQLiteOpenHelper.Factory, RoomDatabase.MigrationContainer, List, boolean,
* RoomDatabase.JournalMode, Executor, Executor, boolean, boolean, boolean, Set, String, File)}
*
* @param context The application context.
* @param name Name of the database, can be null if it is in memory.
* @param sqliteOpenHelperFactory The open helper factory to use.
* @param migrationContainer The migration container for migrations.
* @param callbacks The list of callbacks for database events.
* @param allowMainThreadQueries Whether to allow main thread reads/writes or not.
* @param journalMode The journal mode. This has to be either TRUNCATE or WRITE_AHEAD_LOGGING.
* @param queryExecutor The Executor used to execute asynchronous queries.
* @param transactionExecutor The Executor used to execute asynchronous transactions.
* @param multiInstanceInvalidation True if Room should perform multi-instance invalidation.
* @param requireMigration True if Room should require a valid migration if version changes,
* @param allowDestructiveMigrationOnDowngrade True if Room should recreate tables if no
* migration is supplied during a downgrade.
* @param migrationNotRequiredFrom The collection of schema versions from which migrations
* aren't required.
*
* @hide
*/
@Deprecated
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public DatabaseConfiguration(@NonNull Context context, @Nullable String name,
@NonNull SupportSQLiteOpenHelper.Factory sqliteOpenHelperFactory,
@NonNull RoomDatabase.MigrationContainer migrationContainer,
@Nullable List<RoomDatabase.Callback> callbacks,
boolean allowMainThreadQueries,
RoomDatabase.JournalMode journalMode,
@NonNull Executor queryExecutor,
@NonNull Executor transactionExecutor,
boolean multiInstanceInvalidation,
boolean requireMigration,
boolean allowDestructiveMigrationOnDowngrade,
@Nullable Set<Integer> migrationNotRequiredFrom) {
this(context, name, sqliteOpenHelperFactory, migrationContainer, callbacks,
allowMainThreadQueries, journalMode, queryExecutor, transactionExecutor,
multiInstanceInvalidation, requireMigration, allowDestructiveMigrationOnDowngrade,
migrationNotRequiredFrom, null, null);
}
/**
* Creates a database configuration with the given values.
*
* @param context The application context.
* @param name Name of the database, can be null if it is in memory.
* @param sqliteOpenHelperFactory The open helper factory to use.
* @param migrationContainer The migration container for migrations.
* @param callbacks The list of callbacks for database events.
* @param allowMainThreadQueries Whether to allow main thread reads/writes or not.
* @param journalMode The journal mode. This has to be either TRUNCATE or WRITE_AHEAD_LOGGING.
* @param queryExecutor The Executor used to execute asynchronous queries.
* @param transactionExecutor The Executor used to execute asynchronous transactions.
* @param multiInstanceInvalidation True if Room should perform multi-instance invalidation.
* @param requireMigration True if Room should require a valid migration if version changes,
* @param allowDestructiveMigrationOnDowngrade True if Room should recreate tables if no
* migration is supplied during a downgrade.
* @param migrationNotRequiredFrom The collection of schema versions from which migrations
* aren't required.
* @param copyFromAssetPath The assets path to the pre-packaged database.
* @param copyFromFile The pre-packaged database file.
*
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public DatabaseConfiguration(@NonNull Context context, @Nullable String name,
@NonNull SupportSQLiteOpenHelper.Factory sqliteOpenHelperFactory,
@NonNull RoomDatabase.MigrationContainer migrationContainer,
@Nullable List<RoomDatabase.Callback> callbacks,
boolean allowMainThreadQueries,
RoomDatabase.JournalMode journalMode,
@NonNull Executor queryExecutor,
@NonNull Executor transactionExecutor,
boolean multiInstanceInvalidation,
boolean requireMigration,
boolean allowDestructiveMigrationOnDowngrade,
@Nullable Set<Integer> migrationNotRequiredFrom,
@Nullable String copyFromAssetPath,
@Nullable File copyFromFile) {
this.sqliteOpenHelperFactory = sqliteOpenHelperFactory;
this.context = context;
this.name = name;
this.migrationContainer = migrationContainer;
this.callbacks = callbacks;
this.allowMainThreadQueries = allowMainThreadQueries;
this.journalMode = journalMode;
this.queryExecutor = queryExecutor;
this.transactionExecutor = transactionExecutor;
this.multiInstanceInvalidation = multiInstanceInvalidation;
this.requireMigration = requireMigration;
this.allowDestructiveMigrationOnDowngrade = allowDestructiveMigrationOnDowngrade;
this.mMigrationNotRequiredFrom = migrationNotRequiredFrom;
this.copyFromAssetPath = copyFromAssetPath;
this.copyFromFile = copyFromFile;
}
/**
* Returns whether a migration is required from the specified version.
*
* @param version The schema version.
* @return True if a valid migration is required, false otherwise.
*
* @deprecated Use {@link #isMigrationRequired(int, int)} which takes
* {@link #allowDestructiveMigrationOnDowngrade} into account.
*/
@Deprecated
public boolean isMigrationRequiredFrom(int version) {
return isMigrationRequired(version, version + 1);
}
/**
* Returns whether a migration is required between two versions.
*
* @param fromVersion The old schema version.
* @param toVersion The new schema version.
* @return True if a valid migration is required, false otherwise.
*/
public boolean isMigrationRequired(int fromVersion, int toVersion) {
// Migrations are not required if its a downgrade AND destructive migration during downgrade
// has been allowed.
final boolean isDowngrade = fromVersion > toVersion;
if (isDowngrade && allowDestructiveMigrationOnDowngrade) {
return false;
}
// Migrations are required between the two versions if we generally require migrations
// AND EITHER there are no exceptions OR the supplied fromVersion is not one of the
// exceptions.
return requireMigration
&& (mMigrationNotRequiredFrom == null
|| !mMigrationNotRequiredFrom.contains(fromVersion));
}
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.room;
import androidx.annotation.RestrictTo;
import androidx.sqlite.db.SupportSQLiteStatement;
/**
* Implementations of this class knows how to delete or update a particular entity.
* <p>
* This is an internal library class and all of its implementations are auto-generated.
*
* @param <T> The type parameter of the entity to be deleted
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
@SuppressWarnings({"WeakerAccess", "unused"})
public abstract class EntityDeletionOrUpdateAdapter<T> extends SharedSQLiteStatement {
/**
* Creates a DeletionOrUpdateAdapter that can delete or update the entity type T on the given
* database.
*
* @param database The database to delete / update the item in.
*/
public EntityDeletionOrUpdateAdapter(RoomDatabase database) {
super(database);
}
/**
* Create the deletion or update query
*
* @return An SQL query that can delete or update instances of T.
*/
@Override
protected abstract String createQuery();
/**
* Binds the entity into the given statement.
*
* @param statement The SQLite statement that prepared for the query returned from
* createQuery.
* @param entity The entity of type T.
*/
protected abstract void bind(SupportSQLiteStatement statement, T entity);
/**
* Deletes or updates the given entities in the database and returns the affected row count.
*
* @param entity The entity to delete or update
* @return The number of affected rows
*/
public final int handle(T entity) {
final SupportSQLiteStatement stmt = acquire();
try {
bind(stmt, entity);
return stmt.executeUpdateDelete();
} finally {
release(stmt);
}
}
/**
* Deletes or updates the given entities in the database and returns the affected row count.
*
* @param entities Entities to delete or update
* @return The number of affected rows
*/
public final int handleMultiple(Iterable<? extends T> entities) {
final SupportSQLiteStatement stmt = acquire();
try {
int total = 0;
for (T entity : entities) {
bind(stmt, entity);
total += stmt.executeUpdateDelete();
}
return total;
} finally {
release(stmt);
}
}
/**
* Deletes or updates the given entities in the database and returns the affected row count.
*
* @param entities Entities to delete or update
* @return The number of affected rows
*/
public final int handleMultiple(T[] entities) {
final SupportSQLiteStatement stmt = acquire();
try {
int total = 0;
for (T entity : entities) {
bind(stmt, entity);
total += stmt.executeUpdateDelete();
}
return total;
} finally {
release(stmt);
}
}
}

View File

@@ -0,0 +1,251 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.room;
import androidx.annotation.RestrictTo;
import androidx.sqlite.db.SupportSQLiteStatement;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Implementations of this class knows how to insert a particular entity.
* <p>
* This is an internal library class and all of its implementations are auto-generated.
*
* @param <T> The type parameter of the entity to be inserted
* @hide
*/
@SuppressWarnings({"WeakerAccess", "unused"})
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public abstract class EntityInsertionAdapter<T> extends SharedSQLiteStatement {
/**
* Creates an InsertionAdapter that can insert the entity type T into the given database.
*
* @param database The database to insert into.
*/
public EntityInsertionAdapter(RoomDatabase database) {
super(database);
}
/**
* Binds the entity into the given statement.
*
* @param statement The SQLite statement that prepared for the query returned from
* createInsertQuery.
* @param entity The entity of type T.
*/
protected abstract void bind(SupportSQLiteStatement statement, T entity);
/**
* Inserts the entity into the database.
*
* @param entity The entity to insert
*/
public final void insert(T entity) {
final SupportSQLiteStatement stmt = acquire();
try {
bind(stmt, entity);
stmt.executeInsert();
} finally {
release(stmt);
}
}
/**
* Inserts the given entities into the database.
*
* @param entities Entities to insert
*/
public final void insert(T[] entities) {
final SupportSQLiteStatement stmt = acquire();
try {
for (T entity : entities) {
bind(stmt, entity);
stmt.executeInsert();
}
} finally {
release(stmt);
}
}
/**
* Inserts the given entities into the database.
*
* @param entities Entities to insert
*/
public final void insert(Iterable<? extends T> entities) {
final SupportSQLiteStatement stmt = acquire();
try {
for (T entity : entities) {
bind(stmt, entity);
stmt.executeInsert();
}
} finally {
release(stmt);
}
}
/**
* Inserts the given entity into the database and returns the row id.
*
* @param entity The entity to insert
* @return The SQLite row id or -1 if no row is inserted
*/
public final long insertAndReturnId(T entity) {
final SupportSQLiteStatement stmt = acquire();
try {
bind(stmt, entity);
return stmt.executeInsert();
} finally {
release(stmt);
}
}
/**
* Inserts the given entities into the database and returns the row ids.
*
* @param entities Entities to insert
* @return The SQLite row ids, for entities that are not inserted the row id returned will be -1
*/
public final long[] insertAndReturnIdsArray(Collection<? extends T> entities) {
final SupportSQLiteStatement stmt = acquire();
try {
final long[] result = new long[entities.size()];
int index = 0;
for (T entity : entities) {
bind(stmt, entity);
result[index] = stmt.executeInsert();
index++;
}
return result;
} finally {
release(stmt);
}
}
/**
* Inserts the given entities into the database and returns the row ids.
*
* @param entities Entities to insert
* @return The SQLite row ids, for entities that are not inserted the row id returned will be -1
*/
public final long[] insertAndReturnIdsArray(T[] entities) {
final SupportSQLiteStatement stmt = acquire();
try {
final long[] result = new long[entities.length];
int index = 0;
for (T entity : entities) {
bind(stmt, entity);
result[index] = stmt.executeInsert();
index++;
}
return result;
} finally {
release(stmt);
}
}
/**
* Inserts the given entities into the database and returns the row ids.
*
* @param entities Entities to insert
* @return The SQLite row ids, for entities that are not inserted the row id returned will be -1
*/
public final Long[] insertAndReturnIdsArrayBox(Collection<? extends T> entities) {
final SupportSQLiteStatement stmt = acquire();
try {
final Long[] result = new Long[entities.size()];
int index = 0;
for (T entity : entities) {
bind(stmt, entity);
result[index] = stmt.executeInsert();
index++;
}
return result;
} finally {
release(stmt);
}
}
/**
* Inserts the given entities into the database and returns the row ids.
*
* @param entities Entities to insert
* @return The SQLite row ids, for entities that are not inserted the row id returned will be -1
*/
public final Long[] insertAndReturnIdsArrayBox(T[] entities) {
final SupportSQLiteStatement stmt = acquire();
try {
final Long[] result = new Long[entities.length];
int index = 0;
for (T entity : entities) {
bind(stmt, entity);
result[index] = stmt.executeInsert();
index++;
}
return result;
} finally {
release(stmt);
}
}
/**
* Inserts the given entities into the database and returns the row ids.
*
* @param entities Entities to insert
* @return The SQLite row ids, for entities that are not inserted the row id returned will be -1
*/
public final List<Long> insertAndReturnIdsList(T[] entities) {
final SupportSQLiteStatement stmt = acquire();
try {
final List<Long> result = new ArrayList<>(entities.length);
int index = 0;
for (T entity : entities) {
bind(stmt, entity);
result.add(index, stmt.executeInsert());
index++;
}
return result;
} finally {
release(stmt);
}
}
/**
* Inserts the given entities into the database and returns the row ids.
*
* @param entities Entities to insert
* @return The SQLite row ids, for entities that are not inserted the row id returned will be -1
*/
public final List<Long> insertAndReturnIdsList(Collection<? extends T> entities) {
final SupportSQLiteStatement stmt = acquire();
try {
final List<Long> result = new ArrayList<>(entities.size());
int index = 0;
for (T entity : entities) {
bind(stmt, entity);
result.add(index, stmt.executeInsert());
index++;
}
return result;
} finally {
release(stmt);
}
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.room;
import androidx.annotation.VisibleForTesting;
import androidx.lifecycle.LiveData;
import java.util.Collections;
import java.util.IdentityHashMap;
import java.util.Set;
import java.util.concurrent.Callable;
/**
* A helper class that maintains {@link RoomTrackingLiveData} instances for an
* {@link InvalidationTracker}.
* <p>
* We keep a strong reference to active LiveData instances to avoid garbage collection in case
* developer does not hold onto the returned LiveData.
*/
class InvalidationLiveDataContainer {
@SuppressWarnings("WeakerAccess")
@VisibleForTesting
final Set<LiveData> mLiveDataSet = Collections.newSetFromMap(
new IdentityHashMap<LiveData, Boolean>()
);
private final RoomDatabase mDatabase;
InvalidationLiveDataContainer(RoomDatabase database) {
mDatabase = database;
}
<T> LiveData<T> create(String[] tableNames, boolean inTransaction,
Callable<T> computeFunction) {
return new RoomTrackingLiveData<>(mDatabase, this, inTransaction, computeFunction,
tableNames);
}
void onActive(LiveData liveData) {
mLiveDataSet.add(liveData);
}
void onInactive(LiveData liveData) {
mLiveDataSet.remove(liveData);
}
}

View File

@@ -0,0 +1,853 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.room;
import android.annotation.SuppressLint;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
import androidx.arch.core.internal.SafeIterableMap;
import androidx.lifecycle.LiveData;
import androidx.sqlite.db.SimpleSQLiteQuery;
import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.sqlite.db.SupportSQLiteStatement;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
/**
* InvalidationTracker keeps a list of tables modified by queries and notifies its callbacks about
* these tables.
*/
// Some details on how the InvalidationTracker works:
// * An in memory table is created with (table_id, invalidated) table_id is a hardcoded int from
// initialization, while invalidated is a boolean bit to indicate if the table has been invalidated.
// * ObservedTableTracker tracks list of tables we should be watching (e.g. adding triggers for).
// * Before each beginTransaction, RoomDatabase invokes InvalidationTracker to sync trigger states.
// * After each endTransaction, RoomDatabase invokes InvalidationTracker to refresh invalidated
// tables.
// * Each update (write operation) on one of the observed tables triggers an update into the
// memory table table, flipping the invalidated flag ON.
// * When multi-instance invalidation is turned on, MultiInstanceInvalidationClient will be created.
// It works as an Observer, and notifies other instances of table invalidation.
public class InvalidationTracker {
private static final String[] TRIGGERS = new String[]{"UPDATE", "DELETE", "INSERT"};
private static final String UPDATE_TABLE_NAME = "room_table_modification_log";
private static final String TABLE_ID_COLUMN_NAME = "table_id";
private static final String INVALIDATED_COLUMN_NAME = "invalidated";
private static final String CREATE_TRACKING_TABLE_SQL = "CREATE TEMP TABLE " + UPDATE_TABLE_NAME
+ "(" + TABLE_ID_COLUMN_NAME + " INTEGER PRIMARY KEY, "
+ INVALIDATED_COLUMN_NAME + " INTEGER NOT NULL DEFAULT 0)";
@VisibleForTesting
static final String RESET_UPDATED_TABLES_SQL = "UPDATE " + UPDATE_TABLE_NAME
+ " SET " + INVALIDATED_COLUMN_NAME + " = 0 WHERE " + INVALIDATED_COLUMN_NAME + " = 1 ";
@VisibleForTesting
static final String SELECT_UPDATED_TABLES_SQL = "SELECT * FROM " + UPDATE_TABLE_NAME
+ " WHERE " + INVALIDATED_COLUMN_NAME + " = 1;";
@NonNull
@VisibleForTesting
final HashMap<String, Integer> mTableIdLookup;
final String[] mTableNames;
@NonNull
private Map<String, Set<String>> mViewTables;
@SuppressWarnings("WeakerAccess") /* synthetic access */
final RoomDatabase mDatabase;
AtomicBoolean mPendingRefresh = new AtomicBoolean(false);
private volatile boolean mInitialized = false;
@SuppressWarnings("WeakerAccess") /* synthetic access */
volatile SupportSQLiteStatement mCleanupStatement;
private ObservedTableTracker mObservedTableTracker;
private final InvalidationLiveDataContainer mInvalidationLiveDataContainer;
// should be accessed with synchronization only.
@VisibleForTesting
@SuppressLint("RestrictedApi")
final SafeIterableMap<Observer, ObserverWrapper> mObserverMap = new SafeIterableMap<>();
private MultiInstanceInvalidationClient mMultiInstanceInvalidationClient;
/**
* Used by the generated code.
*
* @hide
*/
@SuppressWarnings("WeakerAccess")
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public InvalidationTracker(RoomDatabase database, String... tableNames) {
this(database, new HashMap<String, String>(), Collections.<String, Set<String>>emptyMap(),
tableNames);
}
/**
* Used by the generated code.
*
* @hide
*/
@SuppressWarnings("WeakerAccess")
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public InvalidationTracker(RoomDatabase database, Map<String, String> shadowTablesMap,
Map<String, Set<String>> viewTables, String... tableNames) {
mDatabase = database;
mObservedTableTracker = new ObservedTableTracker(tableNames.length);
mTableIdLookup = new HashMap<>();
mViewTables = viewTables;
mInvalidationLiveDataContainer = new InvalidationLiveDataContainer(mDatabase);
final int size = tableNames.length;
mTableNames = new String[size];
for (int id = 0; id < size; id++) {
final String tableName = tableNames[id].toLowerCase(Locale.US);
mTableIdLookup.put(tableName, id);
String shadowTableName = shadowTablesMap.get(tableNames[id]);
if (shadowTableName != null) {
mTableNames[id] = shadowTableName.toLowerCase(Locale.US);
} else {
mTableNames[id] = tableName;
}
}
// Adjust table id lookup for those tables whose shadow table is another already mapped
// table (e.g. external content fts tables).
for (Map.Entry<String, String> shadowTableEntry : shadowTablesMap.entrySet()) {
String shadowTableName = shadowTableEntry.getValue().toLowerCase(Locale.US);
if (mTableIdLookup.containsKey(shadowTableName)) {
String tableName = shadowTableEntry.getKey().toLowerCase(Locale.US);
mTableIdLookup.put(tableName, mTableIdLookup.get(shadowTableName));
}
}
}
/**
* Internal method to initialize table tracking.
* <p>
* You should never call this method, it is called by the generated code.
*/
void internalInit(SupportSQLiteDatabase database) {
synchronized (this) {
if (mInitialized) {
Log.e(Room.LOG_TAG, "Invalidation tracker is initialized twice :/.");
return;
}
// These actions are not in a transaction because temp_store is not allowed to be
// performed on a transaction, and recursive_triggers is not affected by transactions.
database.execSQL("PRAGMA temp_store = MEMORY;");
database.execSQL("PRAGMA recursive_triggers='ON';");
database.execSQL(CREATE_TRACKING_TABLE_SQL);
syncTriggers(database);
mCleanupStatement = database.compileStatement(RESET_UPDATED_TABLES_SQL);
mInitialized = true;
}
}
void startMultiInstanceInvalidation(Context context, String name) {
mMultiInstanceInvalidationClient = new MultiInstanceInvalidationClient(context, name, this,
mDatabase.getQueryExecutor());
}
void stopMultiInstanceInvalidation() {
if (mMultiInstanceInvalidationClient != null) {
mMultiInstanceInvalidationClient.stop();
mMultiInstanceInvalidationClient = null;
}
}
private static void appendTriggerName(StringBuilder builder, String tableName,
String triggerType) {
builder.append("`")
.append("room_table_modification_trigger_")
.append(tableName)
.append("_")
.append(triggerType)
.append("`");
}
private void stopTrackingTable(SupportSQLiteDatabase writableDb, int tableId) {
final String tableName = mTableNames[tableId];
StringBuilder stringBuilder = new StringBuilder();
for (String trigger : TRIGGERS) {
stringBuilder.setLength(0);
stringBuilder.append("DROP TRIGGER IF EXISTS ");
appendTriggerName(stringBuilder, tableName, trigger);
writableDb.execSQL(stringBuilder.toString());
}
}
private void startTrackingTable(SupportSQLiteDatabase writableDb, int tableId) {
writableDb.execSQL(
"INSERT OR IGNORE INTO " + UPDATE_TABLE_NAME + " VALUES(" + tableId + ", 0)");
final String tableName = mTableNames[tableId];
StringBuilder stringBuilder = new StringBuilder();
for (String trigger : TRIGGERS) {
stringBuilder.setLength(0);
stringBuilder.append("CREATE TEMP TRIGGER IF NOT EXISTS ");
appendTriggerName(stringBuilder, tableName, trigger);
stringBuilder.append(" AFTER ")
.append(trigger)
.append(" ON `")
.append(tableName)
.append("` BEGIN UPDATE ")
.append(UPDATE_TABLE_NAME)
.append(" SET ").append(INVALIDATED_COLUMN_NAME).append(" = 1")
.append(" WHERE ").append(TABLE_ID_COLUMN_NAME).append(" = ").append(tableId)
.append(" AND ").append(INVALIDATED_COLUMN_NAME).append(" = 0")
.append("; END");
writableDb.execSQL(stringBuilder.toString());
}
}
/**
* Adds the given observer to the observers list and it will be notified if any table it
* observes changes.
* <p>
* Database changes are pulled on another thread so in some race conditions, the observer might
* be invoked for changes that were done before it is added.
* <p>
* If the observer already exists, this is a no-op call.
* <p>
* If one of the tables in the Observer does not exist in the database, this method throws an
* {@link IllegalArgumentException}.
*
* @param observer The observer which listens the database for changes.
*/
@SuppressLint("RestrictedApi")
@WorkerThread
public void addObserver(@NonNull Observer observer) {
final String[] tableNames = resolveViews(observer.mTables);
int[] tableIds = new int[tableNames.length];
final int size = tableNames.length;
for (int i = 0; i < size; i++) {
Integer tableId = mTableIdLookup.get(tableNames[i].toLowerCase(Locale.US));
if (tableId == null) {
throw new IllegalArgumentException("There is no table with name " + tableNames[i]);
}
tableIds[i] = tableId;
}
ObserverWrapper wrapper = new ObserverWrapper(observer, tableIds, tableNames);
ObserverWrapper currentObserver;
synchronized (mObserverMap) {
currentObserver = mObserverMap.putIfAbsent(observer, wrapper);
}
if (currentObserver == null && mObservedTableTracker.onAdded(tableIds)) {
syncTriggers();
}
}
private String[] validateAndResolveTableNames(String[] tableNames) {
String[] resolved = resolveViews(tableNames);
for (String tableName : resolved) {
if (!mTableIdLookup.containsKey(tableName.toLowerCase(Locale.US))) {
throw new IllegalArgumentException("There is no table with name " + tableName);
}
}
return resolved;
}
/**
* Resolves the list of tables and views into a list of unique tables that are underlying them.
*
* @param names The names of tables or views.
* @return The names of the underlying tables.
*/
private String[] resolveViews(String[] names) {
Set<String> tables = new HashSet<>();
for (String name : names) {
final String lowercase = name.toLowerCase(Locale.US);
if (mViewTables.containsKey(lowercase)) {
tables.addAll(mViewTables.get(lowercase));
} else {
tables.add(name);
}
}
return tables.toArray(new String[tables.size()]);
}
/**
* Adds an observer but keeps a weak reference back to it.
* <p>
* Note that you cannot remove this observer once added. It will be automatically removed
* when the observer is GC'ed.
*
* @param observer The observer to which InvalidationTracker will keep a weak reference.
* @hide
*/
@SuppressWarnings("unused")
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public void addWeakObserver(Observer observer) {
addObserver(new WeakObserver(this, observer));
}
/**
* Removes the observer from the observers list.
*
* @param observer The observer to remove.
*/
@SuppressLint("RestrictedApi")
@SuppressWarnings("WeakerAccess")
@WorkerThread
public void removeObserver(@NonNull final Observer observer) {
ObserverWrapper wrapper;
synchronized (mObserverMap) {
wrapper = mObserverMap.remove(observer);
}
if (wrapper != null && mObservedTableTracker.onRemoved(wrapper.mTableIds)) {
syncTriggers();
}
}
@SuppressWarnings("WeakerAccess") /* synthetic access */
boolean ensureInitialization() {
if (!mDatabase.isOpen()) {
return false;
}
if (!mInitialized) {
// trigger initialization
mDatabase.getOpenHelper().getWritableDatabase();
}
if (!mInitialized) {
Log.e(Room.LOG_TAG, "database is not initialized even though it is open");
return false;
}
return true;
}
@VisibleForTesting
Runnable mRefreshRunnable = new Runnable() {
@Override
public void run() {
final Lock closeLock = mDatabase.getCloseLock();
Set<Integer> invalidatedTableIds = null;
try {
closeLock.lock();
if (!ensureInitialization()) {
return;
}
if (!mPendingRefresh.compareAndSet(true, false)) {
// no pending refresh
return;
}
if (mDatabase.inTransaction()) {
// current thread is in a transaction. when it ends, it will invoke
// refreshRunnable again. mPendingRefresh is left as false on purpose
// so that the last transaction can flip it on again.
return;
}
if (mDatabase.mWriteAheadLoggingEnabled) {
// This transaction has to be on the underlying DB rather than the RoomDatabase
// in order to avoid a recursive loop after endTransaction.
SupportSQLiteDatabase db = mDatabase.getOpenHelper().getWritableDatabase();
db.beginTransaction();
try {
invalidatedTableIds = checkUpdatedTable();
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
} else {
invalidatedTableIds = checkUpdatedTable();
}
} catch (IllegalStateException | SQLiteException exception) {
// may happen if db is closed. just log.
Log.e(Room.LOG_TAG, "Cannot run invalidation tracker. Is the db closed?",
exception);
} finally {
closeLock.unlock();
}
if (invalidatedTableIds != null && !invalidatedTableIds.isEmpty()) {
synchronized (mObserverMap) {
for (Map.Entry<Observer, ObserverWrapper> entry : mObserverMap) {
entry.getValue().notifyByTableInvalidStatus(invalidatedTableIds);
}
}
}
}
private Set<Integer> checkUpdatedTable() {
HashSet<Integer> invalidatedTableIds = new HashSet<>();
Cursor cursor = mDatabase.query(new SimpleSQLiteQuery(SELECT_UPDATED_TABLES_SQL));
//noinspection TryFinallyCanBeTryWithResources
try {
while (cursor.moveToNext()) {
final int tableId = cursor.getInt(0);
invalidatedTableIds.add(tableId);
}
} finally {
cursor.close();
}
if (!invalidatedTableIds.isEmpty()) {
mCleanupStatement.executeUpdateDelete();
}
return invalidatedTableIds;
}
};
/**
* Enqueues a task to refresh the list of updated tables.
* <p>
* This method is automatically called when {@link RoomDatabase#endTransaction()} is called but
* if you have another connection to the database or directly use {@link
* SupportSQLiteDatabase}, you may need to call this manually.
*/
@SuppressWarnings("WeakerAccess")
public void refreshVersionsAsync() {
// TODO we should consider doing this sync instead of async.
if (mPendingRefresh.compareAndSet(false, true)) {
mDatabase.getQueryExecutor().execute(mRefreshRunnable);
}
}
/**
* Check versions for tables, and run observers synchronously if tables have been updated.
*
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
@WorkerThread
public void refreshVersionsSync() {
syncTriggers();
mRefreshRunnable.run();
}
/**
* Notifies all the registered {@link Observer}s of table changes.
* <p>
* This can be used for notifying invalidation that cannot be detected by this
* {@link InvalidationTracker}, for example, invalidation from another process.
*
* @param tables The invalidated tables.
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY)
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
public void notifyObserversByTableNames(String... tables) {
synchronized (mObserverMap) {
for (Map.Entry<Observer, ObserverWrapper> entry : mObserverMap) {
if (!entry.getKey().isRemote()) {
entry.getValue().notifyByTableNames(tables);
}
}
}
}
void syncTriggers(SupportSQLiteDatabase database) {
if (database.inTransaction()) {
// we won't run this inside another transaction.
return;
}
try {
// This method runs in a while loop because while changes are synced to db, another
// runnable may be skipped. If we cause it to skip, we need to do its work.
while (true) {
Lock closeLock = mDatabase.getCloseLock();
closeLock.lock();
try {
// there is a potential race condition where another mSyncTriggers runnable
// can start running right after we get the tables list to sync.
final int[] tablesToSync = mObservedTableTracker.getTablesToSync();
if (tablesToSync == null) {
return;
}
final int limit = tablesToSync.length;
database.beginTransaction();
try {
for (int tableId = 0; tableId < limit; tableId++) {
switch (tablesToSync[tableId]) {
case ObservedTableTracker.ADD:
startTrackingTable(database, tableId);
break;
case ObservedTableTracker.REMOVE:
stopTrackingTable(database, tableId);
break;
}
}
database.setTransactionSuccessful();
} finally {
database.endTransaction();
}
mObservedTableTracker.onSyncCompleted();
} finally {
closeLock.unlock();
}
}
} catch (IllegalStateException | SQLiteException exception) {
// may happen if db is closed. just log.
Log.e(Room.LOG_TAG, "Cannot run invalidation tracker. Is the db closed?",
exception);
}
}
/**
* Called by RoomDatabase before each beginTransaction call.
* <p>
* It is important that pending trigger changes are applied to the database before any query
* runs. Otherwise, we may miss some changes.
* <p>
* This api should eventually be public.
*/
void syncTriggers() {
if (!mDatabase.isOpen()) {
return;
}
syncTriggers(mDatabase.getOpenHelper().getWritableDatabase());
}
/**
* Creates a LiveData that computes the given function once and for every other invalidation
* of the database.
* <p>
* Holds a strong reference to the created LiveData as long as it is active.
*
* @deprecated Use {@link #createLiveData(String[], boolean, Callable)}
*
* @param computeFunction The function that calculates the value
* @param tableNames The list of tables to observe
* @param <T> The return type
* @return A new LiveData that computes the given function when the given list of tables
* invalidates.
* @hide
*/
@Deprecated
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public <T> LiveData<T> createLiveData(String[] tableNames, Callable<T> computeFunction) {
return createLiveData(tableNames, false, computeFunction);
}
/**
* Creates a LiveData that computes the given function once and for every other invalidation
* of the database.
* <p>
* Holds a strong reference to the created LiveData as long as it is active.
*
* @param tableNames The list of tables to observe
* @param inTransaction True if the computeFunction will be done in a transaction, false
* otherwise.
* @param computeFunction The function that calculates the value
* @param <T> The return type
* @return A new LiveData that computes the given function when the given list of tables
* invalidates.
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public <T> LiveData<T> createLiveData(String[] tableNames, boolean inTransaction,
Callable<T> computeFunction) {
return mInvalidationLiveDataContainer.create(
validateAndResolveTableNames(tableNames), inTransaction, computeFunction);
}
/**
* Wraps an observer and keeps the table information.
* <p>
* Internally table ids are used which may change from database to database so the table
* related information is kept here rather than in the Observer.
*/
@SuppressWarnings("WeakerAccess")
static class ObserverWrapper {
final int[] mTableIds;
private final String[] mTableNames;
final Observer mObserver;
private final Set<String> mSingleTableSet;
ObserverWrapper(Observer observer, int[] tableIds, String[] tableNames) {
mObserver = observer;
mTableIds = tableIds;
mTableNames = tableNames;
if (tableIds.length == 1) {
HashSet<String> set = new HashSet<>();
set.add(mTableNames[0]);
mSingleTableSet = Collections.unmodifiableSet(set);
} else {
mSingleTableSet = null;
}
}
/**
* Notifies the underlying {@link #mObserver} if any of the observed tables are invalidated
* based on the given invalid status set.
*
* @param invalidatedTablesIds The table ids of the tables that are invalidated.
*/
void notifyByTableInvalidStatus(Set<Integer> invalidatedTablesIds) {
Set<String> invalidatedTables = null;
final int size = mTableIds.length;
for (int index = 0; index < size; index++) {
final int tableId = mTableIds[index];
if (invalidatedTablesIds.contains(tableId)) {
if (size == 1) {
// Optimization for a single-table observer
invalidatedTables = mSingleTableSet;
} else {
if (invalidatedTables == null) {
invalidatedTables = new HashSet<>(size);
}
invalidatedTables.add(mTableNames[index]);
}
}
}
if (invalidatedTables != null) {
mObserver.onInvalidated(invalidatedTables);
}
}
/**
* Notifies the underlying {@link #mObserver} if it observes any of the specified
* {@code tables}.
*
* @param tables The invalidated table names.
*/
void notifyByTableNames(String[] tables) {
Set<String> invalidatedTables = null;
if (mTableNames.length == 1) {
for (String table : tables) {
if (table.equalsIgnoreCase(mTableNames[0])) {
// Optimization for a single-table observer
invalidatedTables = mSingleTableSet;
break;
}
}
} else {
HashSet<String> set = new HashSet<>();
for (String table : tables) {
for (String ourTable : mTableNames) {
if (ourTable.equalsIgnoreCase(table)) {
set.add(ourTable);
break;
}
}
}
if (set.size() > 0) {
invalidatedTables = set;
}
}
if (invalidatedTables != null) {
mObserver.onInvalidated(invalidatedTables);
}
}
}
/**
* An observer that can listen for changes in the database.
*/
public abstract static class Observer {
final String[] mTables;
/**
* Observes the given list of tables and views.
*
* @param firstTable The name of the table or view.
* @param rest More names of tables or views.
*/
@SuppressWarnings("unused")
protected Observer(@NonNull String firstTable, String... rest) {
mTables = Arrays.copyOf(rest, rest.length + 1);
mTables[rest.length] = firstTable;
}
/**
* Observes the given list of tables and views.
*
* @param tables The list of tables or views to observe for changes.
*/
public Observer(@NonNull String[] tables) {
// copy tables in case user modifies them afterwards
mTables = Arrays.copyOf(tables, tables.length);
}
/**
* Called when one of the observed tables is invalidated in the database.
*
* @param tables A set of invalidated tables. This is useful when the observer targets
* multiple tables and you want to know which table is invalidated. This will
* be names of underlying tables when you are observing views.
*/
public abstract void onInvalidated(@NonNull Set<String> tables);
boolean isRemote() {
return false;
}
}
/**
* Keeps a list of tables we should observe. Invalidation tracker lazily syncs this list w/
* triggers in the database.
* <p>
* This class is thread safe
*/
static class ObservedTableTracker {
static final int NO_OP = 0; // don't change trigger state for this table
static final int ADD = 1; // add triggers for this table
static final int REMOVE = 2; // remove triggers for this table
// number of observers per table
final long[] mTableObservers;
// trigger state for each table at last sync
// this field is updated when syncAndGet is called.
final boolean[] mTriggerStates;
// when sync is called, this field is returned. It includes actions as ADD, REMOVE, NO_OP
final int[] mTriggerStateChanges;
boolean mNeedsSync;
/**
* After we return non-null value from getTablesToSync, we expect a onSyncCompleted before
* returning any non-null value from getTablesToSync.
* This allows us to workaround any multi-threaded state syncing issues.
*/
boolean mPendingSync;
ObservedTableTracker(int tableCount) {
mTableObservers = new long[tableCount];
mTriggerStates = new boolean[tableCount];
mTriggerStateChanges = new int[tableCount];
Arrays.fill(mTableObservers, 0);
Arrays.fill(mTriggerStates, false);
}
/**
* @return true if # of triggers is affected.
*/
boolean onAdded(int... tableIds) {
boolean needTriggerSync = false;
synchronized (this) {
for (int tableId : tableIds) {
final long prevObserverCount = mTableObservers[tableId];
mTableObservers[tableId] = prevObserverCount + 1;
if (prevObserverCount == 0) {
mNeedsSync = true;
needTriggerSync = true;
}
}
}
return needTriggerSync;
}
/**
* @return true if # of triggers is affected.
*/
boolean onRemoved(int... tableIds) {
boolean needTriggerSync = false;
synchronized (this) {
for (int tableId : tableIds) {
final long prevObserverCount = mTableObservers[tableId];
mTableObservers[tableId] = prevObserverCount - 1;
if (prevObserverCount == 1) {
mNeedsSync = true;
needTriggerSync = true;
}
}
}
return needTriggerSync;
}
/**
* If this returns non-null, you must call onSyncCompleted.
*
* @return int[] An int array where the index for each tableId has the action for that
* table.
*/
@Nullable
int[] getTablesToSync() {
synchronized (this) {
if (!mNeedsSync || mPendingSync) {
return null;
}
final int tableCount = mTableObservers.length;
for (int i = 0; i < tableCount; i++) {
final boolean newState = mTableObservers[i] > 0;
if (newState != mTriggerStates[i]) {
mTriggerStateChanges[i] = newState ? ADD : REMOVE;
} else {
mTriggerStateChanges[i] = NO_OP;
}
mTriggerStates[i] = newState;
}
mPendingSync = true;
mNeedsSync = false;
return mTriggerStateChanges;
}
}
/**
* if getTablesToSync returned non-null, the called should call onSyncCompleted once it
* is done.
*/
void onSyncCompleted() {
synchronized (this) {
mPendingSync = false;
}
}
}
/**
* An Observer wrapper that keeps a weak reference to the given object.
* <p>
* This class will automatically unsubscribe when the wrapped observer goes out of memory.
*/
static class WeakObserver extends Observer {
final InvalidationTracker mTracker;
final WeakReference<Observer> mDelegateRef;
WeakObserver(InvalidationTracker tracker, Observer delegate) {
super(delegate.mTables);
mTracker = tracker;
mDelegateRef = new WeakReference<>(delegate);
}
@Override
public void onInvalidated(@NonNull Set<String> tables) {
final Observer observer = mDelegateRef.get();
if (observer == null) {
mTracker.removeObserver(this);
} else {
observer.onInvalidated(tables);
}
}
}
}

View File

@@ -0,0 +1,200 @@
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.room;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Handles all the communication from {@link RoomDatabase} and {@link InvalidationTracker} to
* {@link MultiInstanceInvalidationService}.
*/
class MultiInstanceInvalidationClient {
/**
* The application context.
*/
// synthetic access
@SuppressWarnings("WeakerAccess")
final Context mAppContext;
/**
* The name of the database file.
*/
// synthetic access
@SuppressWarnings("WeakerAccess")
final String mName;
/**
* The client ID assigned by {@link MultiInstanceInvalidationService}.
*/
// synthetic access
@SuppressWarnings("WeakerAccess")
int mClientId;
// synthetic access
@SuppressWarnings("WeakerAccess")
final InvalidationTracker mInvalidationTracker;
// synthetic access
@SuppressWarnings("WeakerAccess")
final InvalidationTracker.Observer mObserver;
// synthetic access
@SuppressWarnings("WeakerAccess")
@Nullable
IMultiInstanceInvalidationService mService;
// synthetic access
@SuppressWarnings("WeakerAccess")
final Executor mExecutor;
// synthetic access
@SuppressWarnings("WeakerAccess")
final IMultiInstanceInvalidationCallback mCallback =
new IMultiInstanceInvalidationCallback.Stub() {
@Override
public void onInvalidation(final String[] tables) {
mExecutor.execute(new Runnable() {
@Override
public void run() {
mInvalidationTracker.notifyObserversByTableNames(tables);
}
});
}
};
// synthetic access
@SuppressWarnings("WeakerAccess")
final AtomicBoolean mStopped = new AtomicBoolean(false);
// synthetic access
@SuppressWarnings("WeakerAccess")
final ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = IMultiInstanceInvalidationService.Stub.asInterface(service);
mExecutor.execute(mSetUpRunnable);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mExecutor.execute(mRemoveObserverRunnable);
mService = null;
}
};
// synthetic access
@SuppressWarnings("WeakerAccess")
final Runnable mSetUpRunnable = new Runnable() {
@Override
public void run() {
try {
final IMultiInstanceInvalidationService service = mService;
if (service != null) {
mClientId = service.registerCallback(mCallback, mName);
mInvalidationTracker.addObserver(mObserver);
}
} catch (RemoteException e) {
Log.w(Room.LOG_TAG, "Cannot register multi-instance invalidation callback", e);
}
}
};
// synthetic access
@SuppressWarnings("WeakerAccess")
final Runnable mRemoveObserverRunnable = new Runnable() {
@Override
public void run() {
mInvalidationTracker.removeObserver(mObserver);
}
};
private final Runnable mTearDownRunnable = new Runnable() {
@Override
public void run() {
mInvalidationTracker.removeObserver(mObserver);
try {
final IMultiInstanceInvalidationService service = mService;
if (service != null) {
service.unregisterCallback(mCallback, mClientId);
}
} catch (RemoteException e) {
Log.w(Room.LOG_TAG, "Cannot unregister multi-instance invalidation callback", e);
}
mAppContext.unbindService(mServiceConnection);
}
};
/**
* @param context The Context to be used for binding
* {@link IMultiInstanceInvalidationService}.
* @param name The name of the database file.
* @param invalidationTracker The {@link InvalidationTracker}
* @param executor The background executor.
*/
MultiInstanceInvalidationClient(Context context, String name,
InvalidationTracker invalidationTracker, Executor executor) {
mAppContext = context.getApplicationContext();
mName = name;
mInvalidationTracker = invalidationTracker;
mExecutor = executor;
mObserver = new InvalidationTracker.Observer(invalidationTracker.mTableNames) {
@Override
public void onInvalidated(@NonNull Set<String> tables) {
if (mStopped.get()) {
return;
}
try {
final IMultiInstanceInvalidationService service = mService;
if (service != null) {
service.broadcastInvalidation(mClientId, tables.toArray(new String[0]));
}
} catch (RemoteException e) {
Log.w(Room.LOG_TAG, "Cannot broadcast invalidation", e);
}
}
@Override
boolean isRemote() {
return true;
}
};
Intent intent = new Intent(mAppContext, MultiInstanceInvalidationService.class);
mAppContext.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
}
void stop() {
if (mStopped.compareAndSet(false, true)) {
mExecutor.execute(mTearDownRunnable);
}
}
}

View File

@@ -0,0 +1,134 @@
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.room;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import java.util.HashMap;
/**
* A {@link Service} for remote invalidation among multiple {@link InvalidationTracker} instances.
* This service runs in the main app process. All the instances of {@link InvalidationTracker}
* (potentially in other processes) has to connect to this service.
*
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public class MultiInstanceInvalidationService extends Service {
// synthetic access
@SuppressWarnings("WeakerAccess")
int mMaxClientId = 0;
// synthetic access
@SuppressWarnings("WeakerAccess")
final HashMap<Integer, String> mClientNames = new HashMap<>();
// synthetic access
@SuppressWarnings("WeakerAccess")
final RemoteCallbackList<IMultiInstanceInvalidationCallback> mCallbackList =
new RemoteCallbackList<IMultiInstanceInvalidationCallback>() {
@Override
public void onCallbackDied(IMultiInstanceInvalidationCallback callback,
Object cookie) {
mClientNames.remove((int) cookie);
}
};
private final IMultiInstanceInvalidationService.Stub mBinder =
new IMultiInstanceInvalidationService.Stub() {
// Assigns a client ID to the client.
@Override
public int registerCallback(IMultiInstanceInvalidationCallback callback,
String name) {
if (name == null) {
return 0;
}
synchronized (mCallbackList) {
int clientId = ++mMaxClientId;
// Use the client ID as the RemoteCallbackList cookie.
if (mCallbackList.register(callback, clientId)) {
mClientNames.put(clientId, name);
return clientId;
} else {
--mMaxClientId;
return 0;
}
}
}
// Explicitly removes the client.
// The client can die without calling this. In that case, mCallbackList
// .onCallbackDied() can take care of removal.
@Override
public void unregisterCallback(IMultiInstanceInvalidationCallback callback,
int clientId) {
synchronized (mCallbackList) {
mCallbackList.unregister(callback);
mClientNames.remove(clientId);
}
}
// Broadcasts table invalidation to other instances of the same database file.
// The broadcast is not sent to the caller itself.
@Override
public void broadcastInvalidation(int clientId, String[] tables) {
synchronized (mCallbackList) {
String name = mClientNames.get(clientId);
if (name == null) {
Log.w(Room.LOG_TAG, "Remote invalidation client ID not registered");
return;
}
int count = mCallbackList.beginBroadcast();
try {
for (int i = 0; i < count; i++) {
int targetClientId = (int) mCallbackList.getBroadcastCookie(i);
String targetName = mClientNames.get(targetClientId);
if (clientId == targetClientId // This is the caller itself.
|| !name.equals(targetName)) { // Not the same file.
continue;
}
try {
IMultiInstanceInvalidationCallback callback =
mCallbackList.getBroadcastItem(i);
callback.onInvalidation(tables);
} catch (RemoteException e) {
Log.w(Room.LOG_TAG, "Error invoking a remote callback", e);
}
}
} finally {
mCallbackList.finishBroadcast();
}
}
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.room;
import android.content.Context;
import androidx.annotation.NonNull;
/**
* Utility class for Room.
*/
@SuppressWarnings("unused")
public class Room {
static final String LOG_TAG = "ROOM";
/**
* The master table where room keeps its metadata information.
*/
public static final String MASTER_TABLE_NAME = RoomMasterTable.TABLE_NAME;
private static final String CURSOR_CONV_SUFFIX = "_CursorConverter";
/**
* Creates a RoomDatabase.Builder for a persistent database. Once a database is built, you
* should keep a reference to it and re-use it.
*
* @param context The context for the database. This is usually the Application context.
* @param klass The abstract class which is annotated with {@link Database} and extends
* {@link RoomDatabase}.
* @param name The name of the database file.
* @param <T> The type of the database class.
* @return A {@code RoomDatabaseBuilder<T>} which you can use to create the database.
*/
@SuppressWarnings("WeakerAccess")
@NonNull
public static <T extends RoomDatabase> RoomDatabase.Builder<T> databaseBuilder(
@NonNull Context context, @NonNull Class<T> klass, @NonNull String name) {
//noinspection ConstantConditions
if (name == null || name.trim().length() == 0) {
throw new IllegalArgumentException("Cannot build a database with null or empty name."
+ " If you are trying to create an in memory database, use Room"
+ ".inMemoryDatabaseBuilder");
}
return new RoomDatabase.Builder<>(context, klass, name);
}
/**
* Creates a RoomDatabase.Builder for an in memory database. Information stored in an in memory
* database disappears when the process is killed.
* Once a database is built, you should keep a reference to it and re-use it.
*
* @param context The context for the database. This is usually the Application context.
* @param klass The abstract class which is annotated with {@link Database} and extends
* {@link RoomDatabase}.
* @param <T> The type of the database class.
* @return A {@code RoomDatabaseBuilder<T>} which you can use to create the database.
*/
@NonNull
public static <T extends RoomDatabase> RoomDatabase.Builder<T> inMemoryDatabaseBuilder(
@NonNull Context context, @NonNull Class<T> klass) {
return new RoomDatabase.Builder<>(context, klass, null);
}
@SuppressWarnings({"TypeParameterUnusedInFormals", "ClassNewInstance"})
@NonNull
static <T, C> T getGeneratedImplementation(Class<C> klass, String suffix) {
final String fullPackage = klass.getPackage().getName();
String name = klass.getCanonicalName();
final String postPackageName = fullPackage.isEmpty()
? name
: (name.substring(fullPackage.length() + 1));
final String implName = postPackageName.replace('.', '_') + suffix;
//noinspection TryWithIdenticalCatches
try {
@SuppressWarnings("unchecked")
final Class<T> aClass = (Class<T>) Class.forName(
fullPackage.isEmpty() ? implName : fullPackage + "." + implName);
return aClass.newInstance();
} catch (ClassNotFoundException e) {
throw new RuntimeException("cannot find implementation for "
+ klass.getCanonicalName() + ". " + implName + " does not exist");
} catch (IllegalAccessException e) {
throw new RuntimeException("Cannot access the constructor"
+ klass.getCanonicalName());
} catch (InstantiationException e) {
throw new RuntimeException("Failed to create an instance of "
+ klass.getCanonicalName());
}
}
/** @deprecated This type should not be instantiated as it contains only static methods. */
@Deprecated
@SuppressWarnings("PrivateConstructorForUtilityClass")
public Room() {
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,277 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.room;
import android.database.Cursor;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.room.migration.Migration;
import androidx.sqlite.db.SimpleSQLiteQuery;
import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.sqlite.db.SupportSQLiteOpenHelper;
import java.util.List;
/**
* An open helper that holds a reference to the configuration until the database is opened.
*
* @hide
*/
@SuppressWarnings("unused")
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public class RoomOpenHelper extends SupportSQLiteOpenHelper.Callback {
@Nullable
private DatabaseConfiguration mConfiguration;
@NonNull
private final Delegate mDelegate;
@NonNull
private final String mIdentityHash;
/**
* Room v1 had a bug where the hash was not consistent if fields are reordered.
* The new has fixes it but we still need to accept the legacy hash.
*/
@NonNull // b/64290754
private final String mLegacyHash;
public RoomOpenHelper(@NonNull DatabaseConfiguration configuration, @NonNull Delegate delegate,
@NonNull String identityHash, @NonNull String legacyHash) {
super(delegate.version);
mConfiguration = configuration;
mDelegate = delegate;
mIdentityHash = identityHash;
mLegacyHash = legacyHash;
}
public RoomOpenHelper(@NonNull DatabaseConfiguration configuration, @NonNull Delegate delegate,
@NonNull String legacyHash) {
this(configuration, delegate, "", legacyHash);
}
@Override
public void onConfigure(SupportSQLiteDatabase db) {
super.onConfigure(db);
}
@Override
public void onCreate(SupportSQLiteDatabase db) {
boolean isEmptyDatabase = hasEmptySchema(db);
mDelegate.createAllTables(db);
if (!isEmptyDatabase) {
// A 0 version pre-populated database goes through the create path because the
// framework's SQLiteOpenHelper thinks the database was just created from scratch. If we
// find the database not to be empty, then it is a pre-populated, we must validate it to
// see if its suitable for usage.
ValidationResult result = mDelegate.onValidateSchema(db);
if (!result.isValid) {
throw new IllegalStateException("Pre-packaged database has an invalid schema: "
+ result.expectedFoundMsg);
}
}
updateIdentity(db);
mDelegate.onCreate(db);
}
@Override
public void onUpgrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
boolean migrated = false;
if (mConfiguration != null) {
List<Migration> migrations = mConfiguration.migrationContainer.findMigrationPath(
oldVersion, newVersion);
if (migrations != null) {
mDelegate.onPreMigrate(db);
for (Migration migration : migrations) {
migration.migrate(db);
}
ValidationResult result = mDelegate.onValidateSchema(db);
if (!result.isValid) {
throw new IllegalStateException("Migration didn't properly handle: "
+ result.expectedFoundMsg);
}
mDelegate.onPostMigrate(db);
updateIdentity(db);
migrated = true;
}
}
if (!migrated) {
if (mConfiguration != null
&& !mConfiguration.isMigrationRequired(oldVersion, newVersion)) {
mDelegate.dropAllTables(db);
mDelegate.createAllTables(db);
} else {
throw new IllegalStateException("A migration from " + oldVersion + " to "
+ newVersion + " was required but not found. Please provide the "
+ "necessary Migration path via "
+ "RoomDatabase.Builder.addMigration(Migration ...) or allow for "
+ "destructive migrations via one of the "
+ "RoomDatabase.Builder.fallbackToDestructiveMigration* methods.");
}
}
}
@Override
public void onDowngrade(SupportSQLiteDatabase db, int oldVersion, int newVersion) {
onUpgrade(db, oldVersion, newVersion);
}
@Override
public void onOpen(SupportSQLiteDatabase db) {
super.onOpen(db);
checkIdentity(db);
mDelegate.onOpen(db);
// there might be too many configurations etc, just clear it.
mConfiguration = null;
}
private void checkIdentity(SupportSQLiteDatabase db) {
if (hasRoomMasterTable(db)) {
String identityHash = null;
Cursor cursor = db.query(new SimpleSQLiteQuery(RoomMasterTable.READ_QUERY));
//noinspection TryFinallyCanBeTryWithResources
try {
if (cursor.moveToFirst()) {
identityHash = cursor.getString(0);
}
} finally {
cursor.close();
}
if (!mIdentityHash.equals(identityHash) && !mLegacyHash.equals(identityHash)) {
throw new IllegalStateException("Room cannot verify the data integrity. Looks like"
+ " you've changed schema but forgot to update the version number. You can"
+ " simply fix this by increasing the version number.");
}
} else {
// No room_master_table, this might an a pre-populated DB, we must validate to see if
// its suitable for usage.
ValidationResult result = mDelegate.onValidateSchema(db);
if (!result.isValid) {
throw new IllegalStateException("Pre-packaged database has an invalid schema: "
+ result.expectedFoundMsg);
}
mDelegate.onPostMigrate(db);
updateIdentity(db);
}
}
private void updateIdentity(SupportSQLiteDatabase db) {
createMasterTableIfNotExists(db);
db.execSQL(RoomMasterTable.createInsertQuery(mIdentityHash));
}
private void createMasterTableIfNotExists(SupportSQLiteDatabase db) {
db.execSQL(RoomMasterTable.CREATE_QUERY);
}
private static boolean hasRoomMasterTable(SupportSQLiteDatabase db) {
Cursor cursor = db.query("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name='"
+ RoomMasterTable.TABLE_NAME + "'");
//noinspection TryFinallyCanBeTryWithResources
try {
return cursor.moveToFirst() && cursor.getInt(0) != 0;
} finally {
cursor.close();
}
}
private static boolean hasEmptySchema(SupportSQLiteDatabase db) {
Cursor cursor = db.query(
"SELECT count(*) FROM sqlite_master WHERE name != 'android_metadata'");
//noinspection TryFinallyCanBeTryWithResources
try {
return cursor.moveToFirst() && cursor.getInt(0) == 0;
} finally {
cursor.close();
}
}
/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public abstract static class Delegate {
public final int version;
public Delegate(int version) {
this.version = version;
}
protected abstract void dropAllTables(SupportSQLiteDatabase database);
protected abstract void createAllTables(SupportSQLiteDatabase database);
protected abstract void onOpen(SupportSQLiteDatabase database);
protected abstract void onCreate(SupportSQLiteDatabase database);
/**
* Called after a migration run to validate database integrity.
*
* @param db The SQLite database.
*
* @deprecated Use {@link #onValidateSchema(SupportSQLiteDatabase)}
*/
@Deprecated
protected void validateMigration(SupportSQLiteDatabase db) {
throw new UnsupportedOperationException("validateMigration is deprecated");
}
/**
* Called after a migration run or pre-package database copy to validate database integrity.
*
* @param db The SQLite database.
*/
@SuppressWarnings("deprecation")
@NonNull
protected ValidationResult onValidateSchema(@NonNull SupportSQLiteDatabase db) {
validateMigration(db);
return new ValidationResult(true, null);
}
/**
* Called before migrations execute to perform preliminary work.
* @param database The SQLite database.
*/
protected void onPreMigrate(SupportSQLiteDatabase database) {
}
/**
* Called after migrations execute to perform additional work.
* @param database The SQLite database.
*/
protected void onPostMigrate(SupportSQLiteDatabase database) {
}
}
/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public static class ValidationResult {
public final boolean isValid;
@Nullable
public final String expectedFoundMsg;
public ValidationResult(boolean isValid, @Nullable String expectedFoundMsg) {
this.isValid = isValid;
this.expectedFoundMsg = expectedFoundMsg;
}
}
}

View File

@@ -0,0 +1,299 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.room;
import androidx.annotation.IntDef;
import androidx.annotation.RestrictTo;
import androidx.annotation.VisibleForTesting;
import androidx.sqlite.db.SupportSQLiteProgram;
import androidx.sqlite.db.SupportSQLiteQuery;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
/**
* This class is used as an intermediate place to keep binding arguments so that we can run
* Cursor queries with correct types rather than passing everything as a string.
* <p>
* Because it is relatively a big object, they are pooled and must be released after each use.
*
* @hide
*/
@SuppressWarnings("unused")
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public class RoomSQLiteQuery implements SupportSQLiteQuery, SupportSQLiteProgram {
@SuppressWarnings("WeakerAccess")
@VisibleForTesting
// Maximum number of queries we'll keep cached.
static final int POOL_LIMIT = 15;
@SuppressWarnings("WeakerAccess")
@VisibleForTesting
// Once we hit POOL_LIMIT, we'll bring the pool size back to the desired number. We always
// clear the bigger queries (# of arguments).
static final int DESIRED_POOL_SIZE = 10;
private volatile String mQuery;
@SuppressWarnings("WeakerAccess")
@VisibleForTesting
final long[] mLongBindings;
@SuppressWarnings("WeakerAccess")
@VisibleForTesting
final double[] mDoubleBindings;
@SuppressWarnings("WeakerAccess")
@VisibleForTesting
final String[] mStringBindings;
@SuppressWarnings("WeakerAccess")
@VisibleForTesting
final byte[][] mBlobBindings;
@Binding
private final int[] mBindingTypes;
@SuppressWarnings("WeakerAccess")
@VisibleForTesting
final int mCapacity;
// number of arguments in the query
@SuppressWarnings("WeakerAccess")
@VisibleForTesting
int mArgCount;
@SuppressWarnings("WeakerAccess")
@VisibleForTesting
static final TreeMap<Integer, RoomSQLiteQuery> sQueryPool = new TreeMap<>();
/**
* Copies the given SupportSQLiteQuery and converts it into RoomSQLiteQuery.
*
* @param supportSQLiteQuery The query to copy from
* @return A new query copied from the provided one.
*/
public static RoomSQLiteQuery copyFrom(SupportSQLiteQuery supportSQLiteQuery) {
final RoomSQLiteQuery query = RoomSQLiteQuery.acquire(
supportSQLiteQuery.getSql(),
supportSQLiteQuery.getArgCount());
supportSQLiteQuery.bindTo(new SupportSQLiteProgram() {
@Override
public void bindNull(int index) {
query.bindNull(index);
}
@Override
public void bindLong(int index, long value) {
query.bindLong(index, value);
}
@Override
public void bindDouble(int index, double value) {
query.bindDouble(index, value);
}
@Override
public void bindString(int index, String value) {
query.bindString(index, value);
}
@Override
public void bindBlob(int index, byte[] value) {
query.bindBlob(index, value);
}
@Override
public void clearBindings() {
query.clearBindings();
}
@Override
public void close() {
// ignored.
}
});
return query;
}
/**
* Returns a new RoomSQLiteQuery that can accept the given number of arguments and holds the
* given query.
*
* @param query The query to prepare
* @param argumentCount The number of query arguments
* @return A RoomSQLiteQuery that holds the given query and has space for the given number of
* arguments.
*/
@SuppressWarnings("WeakerAccess")
public static RoomSQLiteQuery acquire(String query, int argumentCount) {
synchronized (sQueryPool) {
final Map.Entry<Integer, RoomSQLiteQuery> entry =
sQueryPool.ceilingEntry(argumentCount);
if (entry != null) {
sQueryPool.remove(entry.getKey());
final RoomSQLiteQuery sqliteQuery = entry.getValue();
sqliteQuery.init(query, argumentCount);
return sqliteQuery;
}
}
RoomSQLiteQuery sqLiteQuery = new RoomSQLiteQuery(argumentCount);
sqLiteQuery.init(query, argumentCount);
return sqLiteQuery;
}
private RoomSQLiteQuery(int capacity) {
mCapacity = capacity;
// because, 1 based indices... we don't want to offsets everything with 1 all the time.
int limit = capacity + 1;
//noinspection WrongConstant
mBindingTypes = new int[limit];
mLongBindings = new long[limit];
mDoubleBindings = new double[limit];
mStringBindings = new String[limit];
mBlobBindings = new byte[limit][];
}
@SuppressWarnings("WeakerAccess")
void init(String query, int argCount) {
mQuery = query;
mArgCount = argCount;
}
/**
* Releases the query back to the pool.
* <p>
* After released, the statement might be returned when {@link #acquire(String, int)} is called
* so you should never re-use it after releasing.
*/
@SuppressWarnings("WeakerAccess")
public void release() {
synchronized (sQueryPool) {
sQueryPool.put(mCapacity, this);
prunePoolLocked();
}
}
private static void prunePoolLocked() {
if (sQueryPool.size() > POOL_LIMIT) {
int toBeRemoved = sQueryPool.size() - DESIRED_POOL_SIZE;
final Iterator<Integer> iterator = sQueryPool.descendingKeySet().iterator();
while (toBeRemoved-- > 0) {
iterator.next();
iterator.remove();
}
}
}
@Override
public String getSql() {
return mQuery;
}
@Override
public int getArgCount() {
return mArgCount;
}
@Override
public void bindTo(SupportSQLiteProgram program) {
for (int index = 1; index <= mArgCount; index++) {
switch (mBindingTypes[index]) {
case NULL:
program.bindNull(index);
break;
case LONG:
program.bindLong(index, mLongBindings[index]);
break;
case DOUBLE:
program.bindDouble(index, mDoubleBindings[index]);
break;
case STRING:
program.bindString(index, mStringBindings[index]);
break;
case BLOB:
program.bindBlob(index, mBlobBindings[index]);
break;
}
}
}
@Override
public void bindNull(int index) {
mBindingTypes[index] = NULL;
}
@Override
public void bindLong(int index, long value) {
mBindingTypes[index] = LONG;
mLongBindings[index] = value;
}
@Override
public void bindDouble(int index, double value) {
mBindingTypes[index] = DOUBLE;
mDoubleBindings[index] = value;
}
@Override
public void bindString(int index, String value) {
mBindingTypes[index] = STRING;
mStringBindings[index] = value;
}
@Override
public void bindBlob(int index, byte[] value) {
mBindingTypes[index] = BLOB;
mBlobBindings[index] = value;
}
@Override
public void close() {
// no-op. not calling release because it is internal API.
}
/**
* Copies arguments from another RoomSQLiteQuery into this query.
*
* @param other The other query, which holds the arguments to be copied.
*/
public void copyArgumentsFrom(RoomSQLiteQuery other) {
int argCount = other.getArgCount() + 1; // +1 for the binding offsets
System.arraycopy(other.mBindingTypes, 0, mBindingTypes, 0, argCount);
System.arraycopy(other.mLongBindings, 0, mLongBindings, 0, argCount);
System.arraycopy(other.mStringBindings, 0, mStringBindings, 0, argCount);
System.arraycopy(other.mBlobBindings, 0, mBlobBindings, 0, argCount);
System.arraycopy(other.mDoubleBindings, 0, mDoubleBindings, 0, argCount);
}
@Override
public void clearBindings() {
Arrays.fill(mBindingTypes, NULL);
Arrays.fill(mStringBindings, null);
Arrays.fill(mBlobBindings, null);
mQuery = null;
// no need to clear others
}
private static final int NULL = 1;
private static final int LONG = 2;
private static final int DOUBLE = 3;
private static final int STRING = 4;
private static final int BLOB = 5;
@Retention(RetentionPolicy.SOURCE)
@IntDef({NULL, LONG, DOUBLE, STRING, BLOB})
@interface Binding {
}
}

View File

@@ -0,0 +1,175 @@
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.room;
import android.annotation.SuppressLint;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.WorkerThread;
import androidx.arch.core.executor.ArchTaskExecutor;
import androidx.lifecycle.LiveData;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* A LiveData implementation that closely works with {@link InvalidationTracker} to implement
* database drive {@link androidx.lifecycle.LiveData} queries that are strongly hold as long
* as they are active.
* <p>
* We need this extra handling for {@link androidx.lifecycle.LiveData} because when they are
* observed forever, there is no {@link androidx.lifecycle.Lifecycle} that will keep them in
* memory but they should stay. We cannot add-remove observer in {@link LiveData#onActive()},
* {@link LiveData#onInactive()} because that would mean missing changes in between or doing an
* extra query on every UI rotation.
* <p>
* This {@link LiveData} keeps a weak observer to the {@link InvalidationTracker} but it is hold
* strongly by the {@link InvalidationTracker} as long as it is active.
*/
class RoomTrackingLiveData<T> extends LiveData<T> {
@SuppressWarnings("WeakerAccess")
final RoomDatabase mDatabase;
@SuppressWarnings("WeakerAccess")
final boolean mInTransaction;
@SuppressWarnings("WeakerAccess")
final Callable<T> mComputeFunction;
private final InvalidationLiveDataContainer mContainer;
@SuppressWarnings("WeakerAccess")
final InvalidationTracker.Observer mObserver;
@SuppressWarnings("WeakerAccess")
final AtomicBoolean mInvalid = new AtomicBoolean(true);
@SuppressWarnings("WeakerAccess")
final AtomicBoolean mComputing = new AtomicBoolean(false);
@SuppressWarnings("WeakerAccess")
final AtomicBoolean mRegisteredObserver = new AtomicBoolean(false);
@SuppressWarnings("WeakerAccess")
final Runnable mRefreshRunnable = new Runnable() {
@WorkerThread
@Override
public void run() {
if (mRegisteredObserver.compareAndSet(false, true)) {
mDatabase.getInvalidationTracker().addWeakObserver(mObserver);
}
boolean computed;
do {
computed = false;
// compute can happen only in 1 thread but no reason to lock others.
if (mComputing.compareAndSet(false, true)) {
// as long as it is invalid, keep computing.
try {
T value = null;
int retry = 0;
while (mInvalid.compareAndSet(true, false) && !computed) {
try {
value = mComputeFunction.call();
computed = true;
} catch (Exception e) {
if (++retry > 3)
throw new RuntimeException(
"Exception while computing database live data.", e);
eu.faircode.email.Log.w(e);
try {
Thread.sleep(3000L);
} catch (InterruptedException ignored) {
}
}
}
if (computed) {
postValue(value);
}
} finally {
// release compute lock
mComputing.set(false);
}
}
// check invalid after releasing compute lock to avoid the following scenario.
// Thread A runs compute()
// Thread A checks invalid, it is false
// Main thread sets invalid to true
// Thread B runs, fails to acquire compute lock and skips
// Thread A releases compute lock
// We've left invalid in set state. The check below recovers.
} while (computed && mInvalid.get());
}
};
@SuppressWarnings("WeakerAccess")
final Runnable mInvalidationRunnable = new Runnable() {
@MainThread
@Override
public void run() {
boolean isActive = hasActiveObservers();
if (mInvalid.compareAndSet(false, true)) {
if (isActive) {
getQueryExecutor().execute(mRefreshRunnable);
}
}
}
};
@SuppressLint("RestrictedApi")
RoomTrackingLiveData(
RoomDatabase database,
InvalidationLiveDataContainer container,
boolean inTransaction,
Callable<T> computeFunction,
String[] tableNames) {
mDatabase = database;
mInTransaction = inTransaction;
mComputeFunction = computeFunction;
mContainer = container;
mObserver = new InvalidationTracker.Observer(tableNames) {
@Override
public void onInvalidated(@NonNull Set<String> tables) {
ArchTaskExecutor.getInstance().executeOnMainThread(mInvalidationRunnable);
}
};
}
@Override
protected void onActive() {
super.onActive();
mContainer.onActive(this);
getQueryExecutor().execute(mRefreshRunnable);
}
@Override
protected void onInactive() {
super.onInactive();
mContainer.onInactive(this);
}
Executor getQueryExecutor() {
if (mInTransaction) {
return mDatabase.getTransactionExecutor();
} else {
return mDatabase.getQueryExecutor();
}
}
}

View File

@@ -0,0 +1,205 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.room;
import android.content.Context;
import android.os.Build;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;
import androidx.room.util.CopyLock;
import androidx.room.util.DBUtil;
import androidx.room.util.FileUtil;
import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.sqlite.db.SupportSQLiteOpenHelper;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.Channels;
import java.nio.channels.FileChannel;
import java.nio.channels.ReadableByteChannel;
/**
* An open helper that will copy & open a pre-populated database if it doesn't exists in internal
* storage.
*/
class SQLiteCopyOpenHelper implements SupportSQLiteOpenHelper {
@NonNull
private final Context mContext;
@Nullable
private final String mCopyFromAssetPath;
@Nullable
private final File mCopyFromFile;
private final int mDatabaseVersion;
@NonNull
private final SupportSQLiteOpenHelper mDelegate;
@Nullable
private DatabaseConfiguration mDatabaseConfiguration;
private boolean mVerified;
SQLiteCopyOpenHelper(
@NonNull Context context,
@Nullable String copyFromAssetPath,
@Nullable File copyFromFile,
int databaseVersion,
@NonNull SupportSQLiteOpenHelper supportSQLiteOpenHelper) {
mContext = context;
mCopyFromAssetPath = copyFromAssetPath;
mCopyFromFile = copyFromFile;
mDatabaseVersion = databaseVersion;
mDelegate = supportSQLiteOpenHelper;
}
@Override
public String getDatabaseName() {
return mDelegate.getDatabaseName();
}
@Override
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
public void setWriteAheadLoggingEnabled(boolean enabled) {
mDelegate.setWriteAheadLoggingEnabled(enabled);
}
@Override
public synchronized SupportSQLiteDatabase getWritableDatabase() {
if (!mVerified) {
verifyDatabaseFile();
mVerified = true;
}
return mDelegate.getWritableDatabase();
}
@Override
public synchronized SupportSQLiteDatabase getReadableDatabase() {
if (!mVerified) {
verifyDatabaseFile();
mVerified = true;
}
return mDelegate.getReadableDatabase();
}
@Override
public synchronized void close() {
mDelegate.close();
mVerified = false;
}
// Can't be constructor param because the factory is needed by the database builder which in
// turn is the one that actually builds the configuration.
void setDatabaseConfiguration(@Nullable DatabaseConfiguration databaseConfiguration) {
mDatabaseConfiguration = databaseConfiguration;
}
private void verifyDatabaseFile() {
String databaseName = getDatabaseName();
File databaseFile = mContext.getDatabasePath(databaseName);
boolean processLevelLock = mDatabaseConfiguration == null
|| mDatabaseConfiguration.multiInstanceInvalidation;
CopyLock copyLock = new CopyLock(databaseName, mContext.getFilesDir(), processLevelLock);
try {
// Acquire a copy lock, this lock works across threads and processes, preventing
// concurrent copy attempts from occurring.
copyLock.lock();
if (!databaseFile.exists()) {
try {
// No database file found, copy and be done.
copyDatabaseFile(databaseFile);
return;
} catch (IOException e) {
throw new RuntimeException("Unable to copy database file.", e);
}
}
if (mDatabaseConfiguration == null) {
return;
}
// A database file is present, check if we need to re-copy it.
int currentVersion;
try {
currentVersion = DBUtil.readVersion(databaseFile);
} catch (IOException e) {
Log.w(Room.LOG_TAG, "Unable to read database version.", e);
return;
}
if (currentVersion == mDatabaseVersion) {
return;
}
if (mDatabaseConfiguration.isMigrationRequired(currentVersion, mDatabaseVersion)) {
// From the current version to the desired version a migration is required, i.e.
// we won't be performing a copy destructive migration.
return;
}
if (mContext.deleteDatabase(databaseName)) {
try {
copyDatabaseFile(databaseFile);
} catch (IOException e) {
// We are more forgiving copying a database on a destructive migration since
// there is already a database file that can be opened.
Log.w(Room.LOG_TAG, "Unable to copy database file.", e);
}
} else {
Log.w(Room.LOG_TAG, "Failed to delete database file ("
+ databaseName + ") for a copy destructive migration.");
}
} finally {
copyLock.unlock();
}
}
private void copyDatabaseFile(File destinationFile) throws IOException {
ReadableByteChannel input;
if (mCopyFromAssetPath != null) {
input = Channels.newChannel(mContext.getAssets().open(mCopyFromAssetPath));
} else if (mCopyFromFile != null) {
input = new FileInputStream(mCopyFromFile).getChannel();
} else {
throw new IllegalStateException("copyFromAssetPath and copyFromFile == null!");
}
// An intermediate file is used so that we never end up with a half-copied database file
// in the internal directory.
File intermediateFile = File.createTempFile(
"room-copy-helper", ".tmp", mContext.getCacheDir());
intermediateFile.deleteOnExit();
FileChannel output = new FileOutputStream(intermediateFile).getChannel();
FileUtil.copy(input, output);
File parent = destinationFile.getParentFile();
if (parent != null && !parent.exists() && !parent.mkdirs()) {
throw new IOException("Failed to create directories for "
+ destinationFile.getAbsolutePath());
}
if (!intermediateFile.renameTo(destinationFile)) {
throw new IOException("Failed to move intermediate file ("
+ intermediateFile.getAbsolutePath() + ") to destination ("
+ destinationFile.getAbsolutePath() + ").");
}
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.room;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.sqlite.db.SupportSQLiteOpenHelper;
import java.io.File;
/**
* Implementation of {@link SupportSQLiteOpenHelper.Factory} that creates
* {@link SQLiteCopyOpenHelper}.
*/
class SQLiteCopyOpenHelperFactory implements SupportSQLiteOpenHelper.Factory {
@Nullable
private final String mCopyFromAssetPath;
@Nullable
private final File mCopyFromFile;
@NonNull
private final SupportSQLiteOpenHelper.Factory mDelegate;
SQLiteCopyOpenHelperFactory(
@Nullable String copyFromAssetPath,
@Nullable File copyFromFile,
@NonNull SupportSQLiteOpenHelper.Factory factory) {
mCopyFromAssetPath = copyFromAssetPath;
mCopyFromFile = copyFromFile;
mDelegate = factory;
}
@Override
public SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration configuration) {
return new SQLiteCopyOpenHelper(
configuration.context,
mCopyFromAssetPath,
mCopyFromFile,
configuration.callback.version,
mDelegate.create(configuration));
}
}

View File

@@ -0,0 +1,100 @@
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.room;
import androidx.annotation.RestrictTo;
import androidx.sqlite.db.SupportSQLiteStatement;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Represents a prepared SQLite state that can be re-used multiple times.
* <p>
* This class is used by generated code. After it is used, {@code release} must be called so that
* it can be used by other threads.
* <p>
* To avoid re-entry even within the same thread, this class allows only 1 time access to the shared
* statement until it is released.
*
* @hide
*/
@SuppressWarnings({"WeakerAccess", "unused"})
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP_PREFIX)
public abstract class SharedSQLiteStatement {
private final AtomicBoolean mLock = new AtomicBoolean(false);
private final RoomDatabase mDatabase;
private volatile SupportSQLiteStatement mStmt;
/**
* Creates an SQLite prepared statement that can be re-used across threads. If it is in use,
* it automatically creates a new one.
*
* @param database The database to create the statement in.
*/
public SharedSQLiteStatement(RoomDatabase database) {
mDatabase = database;
}
/**
* Create the query.
*
* @return The SQL query to prepare.
*/
protected abstract String createQuery();
protected void assertNotMainThread() {
mDatabase.assertNotMainThread();
}
private SupportSQLiteStatement createNewStatement() {
String query = createQuery();
return mDatabase.compileStatement(query);
}
private SupportSQLiteStatement getStmt(boolean canUseCached) {
final SupportSQLiteStatement stmt;
if (canUseCached) {
if (mStmt == null) {
mStmt = createNewStatement();
}
stmt = mStmt;
} else {
// it is in use, create a one off statement
stmt = createNewStatement();
}
return stmt;
}
/**
* Call this to get the statement. Must call {@link #release(SupportSQLiteStatement)} once done.
*/
public SupportSQLiteStatement acquire() {
assertNotMainThread();
return getStmt(mLock.compareAndSet(false, true));
}
/**
* Must call this when statement will not be used anymore.
*
* @param statement The statement that was returned from acquire.
*/
public void release(SupportSQLiteStatement statement) {
if (statement == mStmt) {
mLock.set(false);
}
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.room;
import androidx.annotation.NonNull;
import java.util.ArrayDeque;
import java.util.concurrent.Executor;
/**
* Executor wrapper for performing database transactions serially.
* <p>
* Since database transactions are exclusive, this executor ensures that transactions are performed
* in-order and one at a time, preventing threads from blocking each other when multiple concurrent
* transactions are attempted.
*/
class TransactionExecutor implements Executor {
private final Executor mExecutor;
private final ArrayDeque<Runnable> mTasks = new ArrayDeque<>();
private Runnable mActive;
TransactionExecutor(@NonNull Executor executor) {
mExecutor = executor;
}
public synchronized void execute(final Runnable command) {
mTasks.offer(new Runnable() {
public void run() {
try {
command.run();
} finally {
scheduleNext();
}
}
});
if (mActive == null) {
scheduleNext();
}
}
@SuppressWarnings("WeakerAccess")
synchronized void scheduleNext() {
if ((mActive = mTasks.poll()) != null) {
mExecutor.execute(mActive);
}
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.room.migration;
import androidx.annotation.NonNull;
import androidx.sqlite.db.SupportSQLiteDatabase;
/**
* Base class for a database migration.
* <p>
* Each migration can move between 2 versions that are defined by {@link #startVersion} and
* {@link #endVersion}.
* <p>
* A migration can handle more than 1 version (e.g. if you have a faster path to choose when
* going version 3 to 5 without going to version 4). If Room opens a database at version
* 3 and latest version is &gt;= 5, Room will use the migration object that can migrate from
* 3 to 5 instead of 3 to 4 and 4 to 5.
* <p>
* If there are not enough migrations provided to move from the current version to the latest
* version, Room will clear the database and recreate so even if you have no changes between 2
* versions, you should still provide a Migration object to the builder.
*/
public abstract class Migration {
public final int startVersion;
public final int endVersion;
/**
* Creates a new migration between {@code startVersion} and {@code endVersion}.
*
* @param startVersion The start version of the database.
* @param endVersion The end version of the database after this migration is applied.
*/
public Migration(int startVersion, int endVersion) {
this.startVersion = startVersion;
this.endVersion = endVersion;
}
/**
* Should run the necessary migrations.
* <p>
* This class cannot access any generated Dao in this method.
* <p>
* This method is already called inside a transaction and that transaction might actually be a
* composite transaction of all necessary {@code Migration}s.
*
* @param database The database instance
*/
public abstract void migrate(@NonNull SupportSQLiteDatabase database);
}

Some files were not shown because too many files have changed in this diff Show More