Compare commits

...

162 Commits

Author SHA1 Message Date
3b6891c84a App was a little slow with livedata. 2018-10-13 10:24:58 +02:00
4901e7174c Still trying to fix the build issue. 2018-10-13 04:52:55 +02:00
8d70e68fe2 Trying to fix build issue. 2018-10-12 22:50:43 +02:00
d3e1527b70 AS changes gradle version to one that does not exist. 2018-10-12 22:29:33 +02:00
0c201301f2 Replaced reservoir by room. 2018-10-12 22:04:47 +02:00
6090590f24 Imports cleaning. Libraries update. 2018-10-12 21:01:39 +02:00
06b88c783d Auto migration to android x. 2018-10-12 20:36:18 +02:00
bb75ebf635 Merge branch 'master' of github.com:aminecmi/ReaderforSelfoss 2018-10-07 22:30:30 +02:00
7d7d0014be Fixes #83. The issue wasn't selfoss related at all... 2018-10-07 22:30:07 +02:00
b3f8d44794 New Crowdin translations (#224)
* New translations strings.xml (Korean)

* New translations strings.xml (Korean)

* New translations strings.xml (Korean)
2018-10-03 14:35:03 +02:00
29d1e38340 Fixes #223. Fixes internal reader issue on 4.2.2 2018-09-30 09:37:22 +02:00
2be872e61e Fixed version update. 2018-09-25 20:56:25 +02:00
377c5518f7 Minimal versions updates. 2018-09-25 20:47:38 +02:00
21be7357b5 Revert "Updated to androidx."
This reverts commit d47ba2c820.
2018-09-25 20:27:27 +02:00
d47ba2c820 Updated to androidx. 2018-09-25 20:08:51 +02:00
a64b14614a New Crowdin translations (#222)
* New translations strings.xml (Italian)

* New translations strings.xml (Italian)

* New translations strings.xml (Italian)
2018-09-24 08:06:00 +02:00
6a88192e77 Fixes #221 2018-09-21 21:39:37 +02:00
aa7c630818 erge branch 'master' of github.com:aminecmi/ReaderforSelfoss 2018-09-15 16:26:22 +02:00
7fb54f14c7 Fixed bug 2018-09-15 16:24:29 +02:00
3d709c02b7 New Crowdin translations (#219)
* New translations strings.xml (French)

* New translations strings.xml (French)

* New translations strings.xml (Spanish)

* New translations strings.xml (Galician)
2018-09-12 13:16:07 +02:00
339d384561 Changelog. 2018-09-10 20:29:22 +02:00
50338d51af New Crowdin translations (#218)
* New translations strings.xml (French)

* New translations strings.xml (French)
2018-09-10 15:17:16 +02:00
92dbabf899 New Crowdin translations (#217)
* New translations strings.xml (Catalan)

* New translations strings.xml (Japanese)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Swedish)

* New translations strings.xml (Spanish)

* New translations strings.xml (Serbian (Cyrillic))

* New translations strings.xml (Russian)

* New translations strings.xml (Romanian)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Portuguese)

* New translations strings.xml (Polish)

* New translations strings.xml (Norwegian)

* New translations strings.xml (Korean)

* New translations strings.xml (Italian)

* New translations strings.xml (Afrikaans)

* New translations strings.xml (Indonesian)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Hebrew)

* New translations strings.xml (Greek)

* New translations strings.xml (German)

* New translations strings.xml (French)

* New translations strings.xml (Finnish)

* New translations strings.xml (Dutch)

* New translations strings.xml (Danish)

* New translations strings.xml (Czech)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Arabic)

* New translations strings.xml (Galician)
2018-09-09 20:59:34 +02:00
0043021390 Merge branch 'master' of github.com:aminecmi/ReaderforSelfoss 2018-09-09 20:40:13 +02:00
70ba9b20da Items marked as read on open. Closes #208. 2018-09-09 20:39:27 +02:00
7fda0a04a1 Fixes #215. 2018-09-09 19:45:49 +02:00
3db3157dc9 Trying to fix a strange issue with the loading when hiding items.. 2018-09-09 19:44:01 +02:00
2089fe60ca New Crowdin translations (#214)
* New translations strings.xml (Galician)

* New translations strings.xml (French)

* New translations strings.xml (Spanish)
2018-08-06 15:49:50 +02:00
9606d36670 Hiding hidden tags from the "normal" tags section. 2018-08-03 09:49:49 +02:00
869cf64c54 New Crowdin translations (#213)
* New translations strings.xml (Catalan)

* New translations strings.xml (Japanese)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Swedish)

* New translations strings.xml (Spanish)

* New translations strings.xml (Serbian (Cyrillic))

* New translations strings.xml (Russian)

* New translations strings.xml (Romanian)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Portuguese)

* New translations strings.xml (Polish)

* New translations strings.xml (Norwegian)

* New translations strings.xml (Korean)

* New translations strings.xml (Italian)

* New translations strings.xml (Afrikaans)

* New translations strings.xml (Indonesian)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Hebrew)

* New translations strings.xml (Greek)

* New translations strings.xml (German)

* New translations strings.xml (French)

* New translations strings.xml (Finnish)

* New translations strings.xml (Dutch)

* New translations strings.xml (Danish)

* New translations strings.xml (Czech)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Arabic)

* New translations strings.xml (Galician)
2018-08-02 22:33:58 +02:00
f57ec1f6c0 Add hidden tags option (#212)
* Add filter for hidden tags. (#207)

This commit adds the option to configure hidden tags. Articles tagged
with these hidden tags won't appear in the list of articles by default.
To see these articles the user must explicitly filter by those tags.

* Closes #211. Handling hidden tags in the lateral panel.

* Changelog.
2018-08-02 22:04:15 +02:00
361eea9a06 New Crowdin translations (#209)
* New translations strings.xml (Galician)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Portuguese, Brazilian)
2018-08-01 20:34:39 +02:00
838b4056ac Add store logos, put badges on the same line (#210)
* Add store logos, put badges on the same line

* fix link

* Changed Jenkins url.
2018-08-01 20:32:21 +02:00
0c0a98510b New translations strings.xml (Galician) (#206) 2018-07-17 19:21:42 +02:00
be642ed06f New Crowdin translations (#205)
* New translations strings.xml (Galician)

* New translations strings.xml (Galician)

* New translations strings.xml (Galician)
2018-07-17 17:47:45 +02:00
fd77f38e95 New Crowdin translations (#204)
* New translations strings.xml (Spanish)

* New translations strings.xml (Spanish)

* New translations strings.xml (Galician)

* New translations strings.xml (Galician)
2018-07-17 17:12:01 +02:00
c9baab7267 New Crowdin translations (#203)
* New translations strings.xml (Spanish)

* New translations strings.xml (Spanish)

* New translations strings.xml (Spanish)

* New translations strings.xml (Spanish)
2018-07-17 11:01:54 +02:00
86985cfd5b Update README.md 2018-07-16 11:24:56 +02:00
1327a4e069 Merge branch 'master' of github.com:aminecmi/ReaderforSelfoss 2018-07-07 13:52:53 +02:00
c46acbc579 Dependencies update to solve fdroid issues. 2018-07-07 13:50:32 +02:00
4c6a403fae New Crowdin translations (#202)
* New translations strings.xml (Catalan)

* New translations strings.xml (Catalan)
2018-06-15 13:45:01 +02:00
78920022bd Update 2018-06-04 19:22:40 +02:00
7b16c41e82 Added the alpha steps. 2018-06-04 19:22:05 +02:00
3389f8bd09 New Crowdin translations (#200)
* New translations strings.xml (Catalan)

* New translations strings.xml (Catalan)

* New translations strings.xml (Catalan)

* New translations strings.xml (Catalan)
2018-06-03 17:20:36 +02:00
8dc25c527d Trying to fix issues with Glide, fragment, and destroyed activity. 2018-05-31 22:41:23 +02:00
46d6bd57c1 Changelog. 2018-05-31 12:46:45 +02:00
db014fe13d Fixed issue on pre lolipop devices. 2018-05-31 12:42:06 +02:00
6c293f4cac Changing the tag handling. 2018-05-26 10:51:11 +02:00
91e5d3736f Cleaning build script. 2018-05-25 22:47:36 +02:00
e11dee220f Adde dHTTP version check for fdroid. 2018-05-25 22:22:32 +02:00
fcebf916d2 New translations strings.xml (French) (#199) 2018-05-25 20:28:15 +02:00
73cc1a7297 Remove any email from the reports, as there seems to be no way to secure the data. 2018-05-25 20:20:47 +02:00
798f112498 Added the ability to force push and force tag creation. 2018-05-24 22:36:20 +02:00
38b5e7dc65 New (new, new, new, new) way for handling versions. Will work with jenkins and fdroid. 2018-05-24 22:32:34 +02:00
2799a48f2b Fixed build issue. 2018-05-24 21:43:27 +02:00
ad5edae6cd Fixed jenkins build issue. 2018-05-23 20:56:30 +02:00
9cb02f0272 Changelog. 2018-05-23 20:44:47 +02:00
6d24fd9336 Removed every config string added at build time. 2018-05-23 20:39:50 +02:00
a3a7b78c96 Removed direct call to mercury api. 2018-05-23 20:00:46 +02:00
e995286068 New Crowdin translations (#196)
* New translations strings.xml (Catalan)

* New translations strings.xml (Japanese)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Swedish)

* New translations strings.xml (Spanish)

* New translations strings.xml (Serbian (Cyrillic))

* New translations strings.xml (Russian)

* New translations strings.xml (Romanian)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Portuguese)

* New translations strings.xml (Polish)

* New translations strings.xml (Norwegian)

* New translations strings.xml (Korean)

* New translations strings.xml (Italian)

* New translations strings.xml (Afrikaans)

* New translations strings.xml (Indonesian)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Hebrew)

* New translations strings.xml (Greek)

* New translations strings.xml (German)

* New translations strings.xml (French)

* New translations strings.xml (Finnish)

* New translations strings.xml (Dutch)

* New translations strings.xml (Danish)

* New translations strings.xml (Czech)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Arabic)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (French)

* New translations strings.xml (French)
2018-05-22 21:20:27 +02:00
65fb6d9b7e Fixes #59. 2018-05-22 21:14:10 +02:00
eb02d1efad Settings for silent debug. 2018-05-22 20:21:31 +02:00
f8d3e1eefb New Crowdin translations (#195)
* New translations strings.xml (Catalan)

* New translations strings.xml (Japanese)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Swedish)

* New translations strings.xml (Spanish)

* New translations strings.xml (Serbian (Cyrillic))

* New translations strings.xml (Russian)

* New translations strings.xml (Romanian)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Portuguese)

* New translations strings.xml (Polish)

* New translations strings.xml (Norwegian)

* New translations strings.xml (Korean)

* New translations strings.xml (Italian)

* New translations strings.xml (Afrikaans)

* New translations strings.xml (Indonesian)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Hebrew)

* New translations strings.xml (Greek)

* New translations strings.xml (German)

* New translations strings.xml (French)

* New translations strings.xml (Finnish)

* New translations strings.xml (Dutch)

* New translations strings.xml (Danish)

* New translations strings.xml (Czech)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Arabic)

* New translations strings.xml (Vietnamese)
2018-05-22 13:21:03 +02:00
218b8fa843 Replace with ACRA bug reporter. 2018-05-21 21:39:23 +02:00
9f94af6239 Removed firebase and crashlytics 2018-05-20 10:19:46 +02:00
d3584ac40e New Crowdin translations (#194)
* New translations strings.xml (Catalan)

* New translations strings.xml (Catalan)

* New translations strings.xml (Catalan)

* New translations strings.xml (Catalan)

* New translations strings.xml (Catalan)

* New translations strings.xml (Catalan)

* New translations strings.xml (Japanese)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Swedish)

* New translations strings.xml (Spanish)

* New translations strings.xml (Serbian (Cyrillic))

* New translations strings.xml (Russian)

* New translations strings.xml (Romanian)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Portuguese)

* New translations strings.xml (Polish)

* New translations strings.xml (Norwegian)

* New translations strings.xml (Korean)

* New translations strings.xml (Italian)

* New translations strings.xml (Afrikaans)

* New translations strings.xml (Indonesian)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Hebrew)

* New translations strings.xml (Greek)

* New translations strings.xml (German)

* New translations strings.xml (French)

* New translations strings.xml (Finnish)

* New translations strings.xml (Dutch)

* New translations strings.xml (Danish)

* New translations strings.xml (Czech)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Arabic)

* New translations strings.xml (Vietnamese)
2018-05-14 22:36:36 +02:00
90bdb289d0 Fixed build and version issue. 2018-05-14 22:36:12 +02:00
78a08750a2 CHANGELOG. 2018-05-14 22:07:50 +02:00
baba851e97 Strings cleaning and versions updates. 2018-05-14 22:02:57 +02:00
2a03783623 Trying to fix some swipe and reload items issues. 2018-05-10 12:57:57 +02:00
9f2a4438b1 Trying to debug and fix a swipe to hide issue. 2018-05-08 10:23:12 +02:00
5ee5287ffa changelog. 2018-05-07 11:28:24 +02:00
29547c2c94 Changed the email subject. 2018-05-07 11:27:16 +02:00
4846c870fa Fixed dark theme issue. 2018-05-07 10:16:02 +02:00
c17980a032 Versionning changes. 2018-04-15 21:32:34 +02:00
a929e419d9 Version names. 2018-04-15 15:46:42 +02:00
487d484bae More styling. 2018-04-15 11:33:37 +02:00
0ca4c04c61 Add to sources. 2018-04-11 22:25:51 +02:00
c857cf2d67 No theme handling for the login page. 2018-04-11 22:25:51 +02:00
acb502028b Article fragment color. 2018-04-11 22:25:51 +02:00
533636f3a1 Link colors in cards and list items. 2018-04-11 22:25:51 +02:00
eb5672901b AS update. 2018-04-11 22:25:51 +02:00
53a8716b51 New translations strings.xml (Spanish) (#190) 2018-04-09 11:06:40 +02:00
3aaff612af WIP. All activities dynamic themes. 2018-04-08 14:16:16 +02:00
fdcd8c6c6a Dynamic theme handling. Works only on the homeactivity for now. 2018-04-08 13:08:42 +02:00
bafd478604 Keeping the source even when fetching details from mercury. 2018-04-07 19:55:55 +02:00
987513a88b Version for alpha deploy. 2018-04-07 19:31:50 +02:00
a450ab2a3b Handling items with multiple tags. 2018-04-07 18:51:39 +02:00
db89fe5aad Updating lateral panel tags without network call. 2018-04-07 18:19:24 +02:00
67a30b92f6 Cleaning. 2018-04-07 17:19:41 +02:00
c397de8c3e New Crowdin translations (#189)
* New translations strings.xml (German)

* New translations strings.xml (Japanese)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Swedish)

* New translations strings.xml (Spanish)

* New translations strings.xml (Serbian (Cyrillic))

* New translations strings.xml (Russian)

* New translations strings.xml (Romanian)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Portuguese)

* New translations strings.xml (Polish)

* New translations strings.xml (Norwegian)

* New translations strings.xml (Korean)

* New translations strings.xml (Italian)

* New translations strings.xml (Afrikaans)

* New translations strings.xml (Indonesian)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Hebrew)

* New translations strings.xml (Greek)

* New translations strings.xml (French)

* New translations strings.xml (Finnish)

* New translations strings.xml (Dutch)

* New translations strings.xml (Danish)

* New translations strings.xml (Czech)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Catalan)

* New translations strings.xml (Arabic)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (French)

* New translations strings.xml (Portuguese)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Spanish)

* New translations strings.xml (Chinese Simplified)
2018-04-07 10:56:13 +02:00
b4db532c45 Fixes #184. Needs translations. 2018-04-07 10:40:27 +02:00
ebecc9c80a Fixes #188. 2018-04-02 09:08:58 +02:00
4f8556fca8 Change version code generation. 2018-03-31 18:25:14 +02:00
68892fb41b Fixed play store update. 2018-03-31 08:22:57 +02:00
6d6f6c72ac Fixes #187. 2018-03-31 07:40:11 +02:00
df5556b945 Build. 2018-03-30 22:17:47 +02:00
d6c74049c3 Fixes #185. 2018-03-30 22:14:49 +02:00
18946464a2 Do nothing if ids are empty. More logging. 2018-03-30 19:54:42 +02:00
edb5eabee7 Fixing NPE. And obfuscation. 2018-03-29 19:59:07 +02:00
99a305f3e2 Cache url as parameter. 2018-03-28 21:11:25 +02:00
68dc5a6acf Debugging read all items. WILL ONLY WORK IF THE OPTION IS ENABLED IN SETTINGS. 2018-03-28 19:26:59 +02:00
6816461502 D8 enabling and gradle cache debug. 2018-03-28 19:13:47 +02:00
15b93bbd9e Updated AS. Enabled build cache. 2018-03-27 20:00:02 +02:00
cd61e140f6 New Crowdin translations (#182)
* New translations strings.xml (German)

* New translations strings.xml (German)

* New translations strings.xml (French)

* New translations strings.xml (German)

* New translations strings.xml (French)

* New translations strings.xml (Portuguese, Brazilian)
2018-03-26 16:08:18 +02:00
4d861a84e6 Fixing permissions added by firebase update
See https://www.reddit.com/r/androiddev/comments/86c02l/google_play_services_1200_released/dw4ehln/?context=0 for more details.
2018-03-26 12:00:57 +02:00
f24de68618 Code cleaning and versions updates. 2018-03-25 21:11:47 +02:00
3bcffff444 Fixing crashlytics issues. 2018-03-25 20:54:33 +02:00
75e9031fa5 Changelog. 2018-03-19 07:53:46 +01:00
3b77e24399 Removed english indonesian translation. 2018-03-19 07:48:08 +01:00
0a738e895f Version updates. 2018-03-03 16:18:29 +01:00
242e5ba035 New Crowdin translations (#181)
* New translations strings.xml (Spanish)

* New translations strings.xml (Spanish)

* New translations strings.xml (Indonesian)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Swedish)

* New translations strings.xml (Serbian (Cyrillic))

* New translations strings.xml (Russian)

* New translations strings.xml (Romanian)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Portuguese)

* New translations strings.xml (Polish)

* New translations strings.xml (Norwegian)

* New translations strings.xml (Korean)

* New translations strings.xml (Japanese)

* New translations strings.xml (Italian)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Afrikaans)

* New translations strings.xml (Hebrew)

* New translations strings.xml (Greek)

* New translations strings.xml (German)

* New translations strings.xml (French)

* New translations strings.xml (Finnish)

* New translations strings.xml (English, Indonesia)

* New translations strings.xml (Dutch)

* New translations strings.xml (Danish)

* New translations strings.xml (Czech)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Catalan)

* New translations strings.xml (Arabic)

* New translations strings.xml (Vietnamese)
2018-03-03 16:10:02 +01:00
c94612106c Changelog. 2018-03-03 16:03:07 +01:00
320924b4ed Fixes #180. 2018-03-03 16:00:56 +01:00
403ecc4521 Trying to understand #83. 2018-02-07 21:30:39 +01:00
6a50b37364 Added a padding to the recyclerview. 2018-02-07 21:22:08 +01:00
d9d341ac5d Versions updates. 2018-02-07 21:16:51 +01:00
e9805b731e New translations strings.xml (Portuguese, Brazilian) (#177) 2018-02-06 11:35:31 +01:00
c6d4337cd1 New Crowdin translations (#176)
* New translations strings.xml (English, Indonesia)

* New translations strings.xml (English, Indonesia)

* New translations strings.xml (English, Indonesia)

* New translations strings.xml (English, Indonesia)

* New translations strings.xml (English, Indonesia)
2018-02-01 17:55:54 +01:00
173f4b2ff7 Build. 2018-01-27 17:23:31 +01:00
3b9436264c Still fixing small fonts issues. 2018-01-27 17:17:43 +01:00
35fe87d79d New Crowdin translations (#175)
* New translations strings.xml (Afrikaans)

* New translations strings.xml (Italian)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Swedish)

* New translations strings.xml (Spanish)

* New translations strings.xml (Serbian (Cyrillic))

* New translations strings.xml (Russian)

* New translations strings.xml (Romanian)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Portuguese)

* New translations strings.xml (Polish)

* New translations strings.xml (Norwegian)

* New translations strings.xml (Korean)

* New translations strings.xml (Japanese)

* New translations strings.xml (Indonesian)

* New translations strings.xml (Arabic)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Hebrew)

* New translations strings.xml (Greek)

* New translations strings.xml (German)

* New translations strings.xml (French)

* New translations strings.xml (Finnish)

* New translations strings.xml (English, Indonesia)

* New translations strings.xml (Dutch)

* New translations strings.xml (Danish)

* New translations strings.xml (Czech)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Catalan)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (French)
2018-01-27 12:39:55 +01:00
f1bb7ba9ad Should fix #174. Added the ability to set the font size of the content of the article reader. 2018-01-27 12:29:14 +01:00
279f229166 Trying jekkyl. 2018-01-21 18:53:00 +01:00
be1794e27b Github page. 2018-01-21 18:23:47 +01:00
4d4a2039c8 Reformated code according to official kotling style. 2018-01-21 18:08:39 +01:00
3013ae4f35 Set theme jekyll-theme-cayman 2018-01-21 17:56:58 +01:00
bb3f7d3786 Page. 2018-01-21 17:54:24 +01:00
f7cc305e44 Changed suffix back to normal. 2018-01-16 22:13:00 +01:00
da17f89148 Building. 2018-01-12 06:26:39 +01:00
ec71ab3c6f Fix issue with list adapter. 2018-01-12 06:24:06 +01:00
0d007f1492 Added logging for StringIndexOutOfBound. 2018-01-12 05:19:49 +01:00
96f8663b8f Simpler (kind of) way to have multiple builds a day) 2018-01-12 05:15:28 +01:00
1a4bc1b301 Refactoring to use #158. 2018-01-12 04:58:43 +01:00
b51ae58a97 A lot of logs to clean after solving an issue. 2018-01-11 21:21:40 +01:00
b126fc32da Fixing the same issue as #158. 2018-01-11 21:10:53 +01:00
b8d234c415 Removed sonar. 2018-01-11 20:53:17 +01:00
2c8902d404 Working on #169.
List isn't reloaded each time.
When new articles arrive, the list is still updated, but the list stays at the same (old) position.
Not sure if I keep this behavior, or try to keep the old position and scroll to it.
2018-01-06 15:37:12 +01:00
80ad65b196 New Crowdin translations (#171)
* New translations strings.xml (French)

* New translations strings.xml (French)
2018-01-06 03:52:23 +01:00
744d9ba72b Contributing to translations. 2018-01-06 03:38:08 +01:00
0c1d708588 Merge pull request #170 from aancel/fix/values-fr-rFR
Update values-fr-rFR/strings.xml
2018-01-06 03:14:21 +01:00
95e79e7c5d Update values-fr-rFR/strings.xml
Fix typos
2018-01-06 01:49:07 +01:00
3ce3260d20 Fixes #165. 2018-01-05 10:51:19 +01:00
641f4f34d3 Fixes #168. 2018-01-04 20:45:25 +01:00
99620cb1c5 Update README.md 2018-01-04 13:32:21 +01:00
8f5f33f5d2 Crowdin fixing Android Studio indentation issue.
* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Italian)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Swedish)

* New translations strings.xml (Spanish)

* New translations strings.xml (Serbian (Cyrillic))

* New translations strings.xml (Russian)

* New translations strings.xml (Romanian)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Portuguese)

* New translations strings.xml (Polish)

* New translations strings.xml (Norwegian)

* New translations strings.xml (Japanese)

* New translations strings.xml (Indonesian)

* New translations strings.xml (Afrikaans)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Hebrew)

* New translations strings.xml (Greek)

* New translations strings.xml (German)

* New translations strings.xml (French)

* New translations strings.xml (Finnish)

* New translations strings.xml (English, Indonesia)

* New translations strings.xml (Dutch)

* New translations strings.xml (Danish)

* New translations strings.xml (Czech)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Arabic)

* New translations strings.xml (Vietnamese)
2018-01-03 19:19:56 +01:00
78e9230b82 Fixes #161. 2018-01-03 19:07:27 +01:00
78aa44c007 Changed from gitter to slack. 2018-01-01 15:04:52 +01:00
53fd944f00 Update README.md 2018-01-01 15:00:04 +01:00
9e6cb4ee3d Fixes #164. Page indicator position. 2018-01-01 07:55:57 +01:00
87ad6f2826 (Again) fixed auto version number. 2018-01-01 07:27:10 +01:00
9050f5a56f Changelog. 2018-01-01 07:03:11 +01:00
3437004082 Multiple crash fixes. 2018-01-01 07:02:19 +01:00
dcf620af87 New translations strings.xml (Chinese Simplified) (#163) 2017-12-27 05:44:58 +01:00
128085a02e New Crowdin translations (#159)
* New translations strings.xml (Afrikaans)

* New translations strings.xml (Korean)

* New translations strings.xml (English, Indonesia)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Swedish)

* New translations strings.xml (Spanish)

* New translations strings.xml (Serbian (Cyrillic))

* New translations strings.xml (Russian)

* New translations strings.xml (Romanian)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Portuguese)

* New translations strings.xml (Polish)

* New translations strings.xml (Norwegian)

* New translations strings.xml (Japanese)

* New translations strings.xml (Arabic)

* New translations strings.xml (Italian)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Hebrew)

* New translations strings.xml (Greek)

* New translations strings.xml (German)

* New translations strings.xml (French)

* New translations strings.xml (Finnish)

* New translations strings.xml (Dutch)

* New translations strings.xml (Danish)

* New translations strings.xml (Czech)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Catalan)

* New translations strings.xml (Indonesian)

* New translations strings.xml (French)
2017-12-20 06:39:27 +01:00
302040ec25 Fixed #160. 2017-12-20 06:33:10 +01:00
e177c22032 Fixes #157. 2017-12-17 21:30:42 +01:00
a11007113a Maybe fixing IllegalStateException. 2017-12-17 20:59:16 +01:00
5e7897bcf4 Fixes #152. 2017-12-17 20:40:14 +01:00
9559af3637 Closes #154. 2017-12-17 19:51:20 +01:00
4c499abcdb Issue 155 - fix StringIndexOutOfBoundException 2017-12-17 17:12:54 +01:00
0055a503b3 New Crowdin translations (#156)
* New translations strings.xml (Afrikaans)

* New translations strings.xml (Korean)

* New translations strings.xml (English, Indonesia)

* New translations strings.xml (Vietnamese)

* New translations strings.xml (Ukrainian)

* New translations strings.xml (Turkish)

* New translations strings.xml (Swedish)

* New translations strings.xml (Spanish)

* New translations strings.xml (Serbian (Cyrillic))

* New translations strings.xml (Russian)

* New translations strings.xml (Romanian)

* New translations strings.xml (Portuguese, Brazilian)

* New translations strings.xml (Portuguese)

* New translations strings.xml (Polish)

* New translations strings.xml (Norwegian)

* New translations strings.xml (Japanese)

* New translations strings.xml (Arabic)

* New translations strings.xml (Italian)

* New translations strings.xml (Hungarian)

* New translations strings.xml (Hebrew)

* New translations strings.xml (Greek)

* New translations strings.xml (German)

* New translations strings.xml (French)

* New translations strings.xml (Finnish)

* New translations strings.xml (Dutch)

* New translations strings.xml (Danish)

* New translations strings.xml (Czech)

* New translations strings.xml (Chinese Traditional)

* New translations strings.xml (Chinese Simplified)

* New translations strings.xml (Catalan)

* New translations strings.xml (Indonesian)
2017-12-13 10:48:56 +01:00
141 changed files with 4206 additions and 3318 deletions

View File

@ -8,7 +8,7 @@ Please read the guidelines before contributing, and follow them (or try to) when
### What you can do to help. ### What you can do to help.
There are many ways to contribute to this project, you could report bugs, request missing features, suggest enhancements and changes to existing ones. You also can improve the README with useful tips that could help the other users. There are many ways to contribute to this project, you could [translate the app](https://crowdin.com/project/readerforselfoss), report bugs, request missing features, suggest enhancements and changes to existing ones. You also can improve the README with useful tips that could help the other users.
You can fork the repository, and [help me solve some issues](https://github.com/aminecmi/ReaderforSelfoss/issues?q=is%3Aissue+is%3Aopen+label%3A%22Up+For+Grabs%22) or [develop new things](https://github.com/aminecmi/ReaderforSelfoss/issues) You can fork the repository, and [help me solve some issues](https://github.com/aminecmi/ReaderforSelfoss/issues?q=is%3Aissue+is%3Aopen+label%3A%22Up+For+Grabs%22) or [develop new things](https://github.com/aminecmi/ReaderforSelfoss/issues)
@ -28,6 +28,7 @@ Always check if the web version of your instance is working.
### Pull requests ### Pull requests
* Don't create a PR for translations. See [here](https://github.com/aminecmi/ReaderforSelfoss/pull/170#issuecomment-355715654) for an explanation why.
* Please ask before starting to work on an issue. I may be working on it, or someone else could be doing so. * Please ask before starting to work on an issue. I may be working on it, or someone else could be doing so.
* Each pull request should implement **ONE** feature or bugfix. Keep in mind that you can submit as many PR as you want. * Each pull request should implement **ONE** feature or bugfix. Keep in mind that you can submit as many PR as you want.
* Your code must be simple and clear enough to avoid using comments to explain what it does. * Your code must be simple and clear enough to avoid using comments to explain what it does.
@ -46,15 +47,8 @@ You can directly import this project into IntellIJ/Android Studio.
You'll have to: You'll have to:
- Configure fabric and add your `apiKey` and `apiSecret` in the `fabric.properties` file.
- Create a firebase project and add the `google-services.json` to the `app/` folder.
- Define some parameters either in `~/.gradle/gradle.properties` or as gradle parameters (see the examples) - Define some parameters either in `~/.gradle/gradle.properties` or as gradle parameters (see the examples)
- mercuryApiKey: A [Mercury](https://mercury.postlight.com/web-parser/) web parser api key for the internal browser
- feedbackEmail: An email to receive users feedback.
- sourceUrl: an url to the source code, used in the settings. **It can be empty.**
- trackerUrl: an url to the tracker, used in the settings. **It can be empty.**
- githubToken: a github token used to report issues from within the app. [Details here](https://github.com/heinrichreimer/android-issue-reporter#how-to-create-a-bot-key). **It can be empty.**
- appLoginUrl, appLoginUsername and appLoginPassword: url, username and password of a selfoss instance. **These are only used for tests. They can be empty if you don't test API calls.** - appLoginUrl, appLoginUsername and appLoginPassword: url, username and password of a selfoss instance. **These are only used for tests. They can be empty if you don't test API calls.**
### Examples: ### Examples:
@ -64,15 +58,10 @@ You'll have to:
appLoginUrl="URL" # It can be empty. appLoginUrl="URL" # It can be empty.
appLoginUsername="LOGIN" # It can be empty. appLoginUsername="LOGIN" # It can be empty.
appLoginPassword="PASS" # It can be empty. appLoginPassword="PASS" # It can be empty.
mercuryApiKey="LONGAPIKEY"
feedbackEmail="EMAIL"
sourceUrl="URLSOURCE" # It can be empty.
trackerUrl="URLTRACKER" # It can be empty.
githubToken="GITHUBTOKEN" # It can be empty or use https://github.com/heinrichreimer/android-issue-reporter#how-to-create-a-bot-key to generate one
``` ```
#### As gradle parameters #### As gradle parameters
``` ```
./gradlew .... -P appLoginUrl="URL" -P appLoginUsername="LOGIN" -P appLoginPassword="PASS" -P mercuryApiKey="LONGAPIKEY" -P feedbackEmail="EMAIL" -P sourceUrl="URLSOURCE" -P trackerUrl="URLTRACKER" -P githubToken="GITHUBTOKEN" ./gradlew .... -P appLoginUrl="URL" -P appLoginUsername="LOGIN" -P appLoginPassword="PASS"
``` ```

View File

@ -5,6 +5,7 @@
- [ ] I have updated the documentation accordingly. - [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes. - [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed. - [ ] All new and existing tests passed.
- [ ] This is **NOT** translation related. (See [here](https://github.com/aminecmi/ReaderforSelfoss/pull/170#issuecomment-355715654))
This closes issue #XXX This closes issue #XXX

4
.gitignore vendored
View File

@ -216,4 +216,6 @@ gradle-app.setting
release/ release/
crowdin.properties crowdin.properties
publish-version.sh

View File

@ -1,8 +1,52 @@
**1.6.x**
- Handling hidden tags.
- Fixed pre-lolipop issue with automatic theme changes.
- Removed all Build config things.
- Removed firebase and fabric.
- Added Acra for optional crash reporting and error logging.
- Dynamic themes !
- Strings cleaning.
- Versions updates.
- Fixes #215, #208.
**1.5.7.x** **1.5.7.x**
- Added confirmation to the mark as read and update menues.
- Add to favorites from article viewer.
- Added an option to use a webview in the article viewer (see #149) - Added an option to use a webview in the article viewer (see #149)
- Fixes (#151) - Fixes (#151 #152 #155 #157 #160 #174) and more.
- New year fixes !!!
- Changed page indicator position as it was overlaping content.
- Now using slack instead of gitter.
- Moved completely to a webview to fix #161.
- Fixed typos in French ( Thanks @aancel )
- Updated the Contribution guide about translations.
- Better handling for articles update. (See #169)
- Ability to change the article viewer content font size (see #153)
- Versions updates * 2.
- Added padding to the recyclerview.
**1.5.5.x (didn't last long) AND 1.5.6.x** **1.5.5.x (didn't last long) AND 1.5.6.x**
@ -20,6 +64,8 @@
- Changed versions handling. - Changed versions handling.
- Removed indonesian english as it was causing issues with the english version of the app.
**1.5.4.22** **1.5.4.22**
- You can now scroll through the loaded articles ! - You can now scroll through the loaded articles !

View File

@ -1,17 +1,20 @@
# ReaderForSelfoss # ReaderForSelfoss
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/readerforselfoss/localized.svg)](https://crowdin.com/project/readerforselfoss) [![Gitter chat](https://badges.gitter.im/amine-bou/ReaderForSelfoss.png)](https://gitter.im/amine-bou/ReaderForSelfoss) [![Slack Channel](https://img.shields.io/badge/chat-slack-green.svg)](https://join.slack.com/t/readerforselfoss/shared_invite/enQtMjkyNzc3NjM2Mjc1LTUzZTZhOGM5YjQ1MTI5MWZiODRjMjE1ZDBmMzQxZmQ3NWZhYTNhMTBjNGEwNmE2ZGFjODU5NjUxZjBkMWJmMDQ) [![Build Status](https://jenkins.amine-bou.fr/job/ReaderForSelfoss/badge/icon)](https://jenkins.amine-bou.fr/job/ReaderForSelfoss/) [![Code Triagers Badge](https://www.codetriage.com/aminecmi/readerforselfoss/badges/users.svg)](https://www.codetriage.com/aminecmi/readerforselfoss) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/readerforselfoss/localized.svg)](https://crowdin.com/project/readerforselfoss)
[![Build Status](http://jenkins.amine-bou.fr/job/ReaderForSelfoss/badge/icon)](http://jenkins.amine-bou.fr/job/ReaderForSelfoss/)
[![Code Triagers Badge](https://www.codetriage.com/aminecmi/readerforselfoss/badges/users.svg)](https://www.codetriage.com/aminecmi/readerforselfoss)
This is the repo of [Reader For Selfoss](https://play.google.com/store/apps/details?id=apps.amine.bou.readerforselfoss&hl=en).
It's an RSS Reader for Android, that **only** works with [Selfoss](https://selfoss.aditu.de/) It's an RSS Reader for Android, that **only** works with [Selfoss](https://selfoss.aditu.de/)
The last APK built from source is available [here](https://jenkins.amine-bou.fr/job/ReaderForSelfoss/lastSuccessfulBuild/artifact/SignApksBuilder-out/selfoss-key/selfoss/app-githubConfig-release-unsigned.apk/app-githubConfig-release.apk). <a href='https://play.google.com/store/apps/details?id=apps.amine.bou.readerforselfoss'><img alt='Get it on Google Play' src='https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png' height="100"/></a> <a href="https://f-droid.org/packages/apps.amine.bou.readerforselfoss"><img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="100"></a>
Also, the last APK built from source is available [here](https://jenkins.amine-bou.fr/job/ReaderForSelfoss/lastSuccessfulBuild/artifact/SignApksBuilder-out/selfoss-key/selfoss/app-githubConfig-release-unsigned.apk/app-githubConfig-release.apk).
## Join the alpha channel
**Keep in mind, it could be instable, but you'll have the new updates faster**
- First, join the google [group](https://groups.google.com/d/forum/reader-for-selfoss-alpha-testing).
- Then, join the [alpha channel](https://play.google.com/apps/testing/apps.amine.bou.readerforselfoss) of the app.
- You'll be able to update the app for the current alpha version.
## Want to help ? ## Want to help ?
@ -23,4 +26,4 @@ Check the [Contribution guide](https://github.com/aminecmi/ReaderforSelfoss/blob
- [See what I'm doing](https://github.com/aminecmi/ReaderforSelfoss/projects/1) - [See what I'm doing](https://github.com/aminecmi/ReaderforSelfoss/projects/1)
- [Create an issue, or request a new feature](https://github.com/aminecmi/ReaderforSelfoss/issues) - [Create an issue, or request a new feature](https://github.com/aminecmi/ReaderforSelfoss/issues)
- [Help translation the app](https://crowdin.com/project/readerforselfoss) - [Help translation the app](https://crowdin.com/project/readerforselfoss)
- [Ask for help](https://gitter.im/amine-bou/ReaderForSelfoss) - [Ask for help](https://join.slack.com/t/readerforselfoss/shared_invite/enQtMjkyNzc3NjM2Mjc1LTUzZTZhOGM5YjQ1MTI5MWZiODRjMjE1ZDBmMzQxZmQ3NWZhYTNhMTBjNGEwNmE2ZGFjODU5NjUxZjBkMWJmMDQ)

View File

@ -1,17 +1,12 @@
buildscript { buildscript {
repositories {
maven { url 'https://maven.fabric.io/public' }
}
dependencies {
classpath 'io.fabric.tools:gradle:1.+'
}
} }
ext { ext {
configuration = [ configuration = [
buildDate: new Date() buildDate: new Date()
] ]
// This will make me able to build multiple times a day. May break thinks. I may forget it.
todaysBuilds = "1"
} }
def gitVersion() { def gitVersion() {
@ -20,40 +15,34 @@ def gitVersion() {
} }
def versionCodeFromGit() { def versionCodeFromGit() {
def versionCode = gitVersion() + (ext.configuration.buildDate.format("yyMMD")).toInteger() println "version code " + gitVersion()
println "version code " + versionCode return gitVersion().toInteger()
return versionCode.toInteger()
} }
def versionNameFromGit() { def versionNameFromGit() {
def versionName = gitVersion() + ext.configuration.buildDate.format('yyyyMMddHHmm') println "version name " + gitVersion()
println "version name " + versionName return gitVersion()
return versionName
} }
apply plugin: 'org.sonarqube' apply plugin: 'kotlin-kapt'
apply plugin: 'com.android.application' apply plugin: 'com.android.application'
apply plugin: 'io.fabric'
apply plugin: 'kotlin-android' apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android-extensions'
repositories {
maven {
url 'https://maven.fabric.io/public'
}
}
android { android {
compileSdkVersion 27 compileOptions {
buildToolsVersion '27.0.2' sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
compileSdkVersion 28
buildToolsVersion '28.0.3'
defaultConfig { defaultConfig {
applicationId "apps.amine.bou.readerforselfoss" applicationId "apps.amine.bou.readerforselfoss"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 27 targetSdkVersion 28
versionCode versionCodeFromGit() versionCode versionCodeFromGit()
versionName versionNameFromGit() versionName versionNameFromGit()
@ -66,14 +55,7 @@ android {
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
// tests // tests
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
buildConfigField "String", "MERCURY_KEY", mercuryApiKey
buildConfigField "String", "FEEDBACK_EMAIL", feedbackEmail
buildConfigField "String", "SOURCE_URL", sourceUrl
buildConfigField "String", "TRACKER_URL", trackerUrl
buildConfigField "String", "TRANSLATION_URL", translationUrl
buildConfigField "String", "GITHUB_TOKEN", githubToken
} }
buildTypes { buildTypes {
release { release {
@ -86,6 +68,7 @@ android {
buildConfigField "String", "LOGIN_URL", appLoginUrl buildConfigField "String", "LOGIN_URL", appLoginUrl
buildConfigField "String", "LOGIN_USERNAME", appLoginUsername buildConfigField "String", "LOGIN_USERNAME", appLoginUsername
buildConfigField "String", "LOGIN_PASSWORD", appLoginPassword buildConfigField "String", "LOGIN_PASSWORD", appLoginPassword
applicationIdSuffix ".dev"
} }
} }
flavorDimensions "build" flavorDimensions "build"
@ -93,112 +76,91 @@ android {
githubConfig { githubConfig {
versionNameSuffix '-github' versionNameSuffix '-github'
dimension "build" dimension "build"
buildConfigField "boolean", "GITHUB_VERSION", "true"
} }
storeConfig { storeConfig {
// As jenkins publishes to alpha first, this is the default suffix now.
versionNameSuffix '-store' versionNameSuffix '-store'
dimension "build" dimension "build"
buildConfigField "boolean", "GITHUB_VERSION", "false"
} }
} }
} }
dependencies { dependencies {
// Testing // Testing
androidTestCompile 'com.android.support.test.espresso:espresso-core:3.0.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-beta02'
androidTestCompile 'com.android.support.test:runner:1.0.1' androidTestImplementation 'androidx.test:runner:1.1.0-beta02'
// Espresso-contrib for DatePicker, RecyclerView, Drawer actions, Accessibility checks, CountingIdlingResource // Espresso-contrib for DatePicker, RecyclerView, Drawer actions, Accessibility checks, CountingIdlingResource
androidTestCompile 'com.android.support.test.espresso:espresso-contrib:3.0.1' androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.1.0-beta02'
// Espresso-intents for validation and stubbing of Intents // Espresso-intents for validation and stubbing of Intents
androidTestCompile 'com.android.support.test.espresso:espresso-intents:3.0.1' androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.0-beta02'
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
compile fileTree(dir: 'libs', include: ['*.jar'])
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
// Android Support // Android Support
compile 'com.android.support:appcompat-v7:27.0.2' implementation "androidx.appcompat:appcompat:$android_version"
compile 'com.android.support:design:27.0.2' implementation "com.google.android.material:material:$android_version"
compile 'com.android.support:recyclerview-v7:27.0.2' implementation "androidx.recyclerview:recyclerview:$android_version"
compile 'com.android.support:support-v4:27.0.2' implementation "androidx.legacy:legacy-support-v4:$android_version"
compile 'com.android.support:support-vector-drawable:27.0.2' implementation "androidx.vectordrawable:vectordrawable:$android_version"
compile 'com.android.support:customtabs:27.0.2' implementation "androidx.browser:browser:$android_version"
compile 'com.android.support:cardview-v7:27.0.2' implementation "androidx.cardview:cardview:$android_version"
compile 'com.android.support.constraint:constraint-layout:1.0.2' implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2'
// Firebase + crashlytics
compile 'com.google.firebase:firebase-core:11.6.2'
compile 'com.google.firebase:firebase-config:11.6.2'
compile 'com.google.firebase:firebase-invites:11.6.2'
compile('com.crashlytics.sdk.android:crashlytics:2.8.0@aar') {
transitive = true;
}
//multidex //multidex
compile 'com.android.support:multidex:1.0.2' implementation 'androidx.multidex:multidex:2.0.0'
// Intro // Intro
compile 'agency.tango.android:material-intro-screen:0.0.5' implementation 'agency.tango.android:material-intro-screen:0.0.5'
// About // About
compile('com.mikepenz:aboutlibraries:6.0.0@aar') { implementation('com.mikepenz:aboutlibraries:6.2.0@aar') {
transitive = true transitive = true
} }
// Retrofit + http logging + okhttp // Retrofit + http logging + okhttp
compile 'com.squareup.retrofit2:retrofit:2.3.0' implementation 'com.squareup.retrofit2:retrofit:2.3.0'
compile 'com.squareup.okhttp3:logging-interceptor:3.9.0' implementation 'com.squareup.okhttp3:logging-interceptor:3.9.0'
compile 'com.squareup.retrofit2:converter-gson:2.3.0' implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
compile 'com.burgstaller:okhttp-digest:1.12' implementation 'com.burgstaller:okhttp-digest:1.12'
// Material-ish things // Material-ish things
compile 'com.ashokvarma.android:bottom-navigation-bar:2.0.3' implementation 'com.ashokvarma.android:bottom-navigation-bar:2.0.5'
compile 'com.github.jd-alexander:LikeButton:0.2.1' implementation 'com.github.jd-alexander:LikeButton:0.2.3'
compile 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1' implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
compile 'org.sufficientlysecure:html-textview:3.5'
// glide // glide
compile 'com.github.bumptech.glide:glide:4.1.1' implementation 'com.github.bumptech.glide:glide:4.1.1'
compile 'com.github.bumptech.glide:okhttp3-integration:4.1.1' implementation 'com.github.bumptech.glide:okhttp3-integration:4.1.1'
// Asking politely users to rate the app // Asking politely users to rate the app
compile 'com.github.stkent:amplify:2.1.0' implementation 'com.github.stkent:amplify:2.2.0'
// Drawer // Drawer
implementation 'co.zsmb:materialdrawer-kt:1.2.1' implementation 'co.zsmb:materialdrawer-kt:2.0.1'
compile 'com.anupcowkur:reservoir:3.1.0'
// Themes // Themes
compile 'com.52inc:scoops:1.0.0' implementation 'com.52inc:scoops:1.0.0'
implementation 'com.jaredrummler:colorpicker:1.0.2'
// Github issues reporter implementation 'com.github.rubensousa:floatingtoolbar:1.5.1'
compile 'com.heinrichreimersoftware:android-issue-reporter:1.3.1'
compile 'com.github.rubensousa:floatingtoolbar:1.5.1'
// Pager // Pager
compile 'me.relex:circleindicator:1.2.2@aar' implementation 'me.relex:circleindicator:2.0.0@aar'
implementation 'androidx.core:core-ktx:1.0.0'
// Crash
implementation 'ch.acra:acra-http:5.1.3'
implementation 'ch.acra:acra-dialog:5.1.3'
implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
} }
apply plugin: 'com.google.gms.google-services'
afterEvaluate { afterEvaluate {
initFabricPropertiesIfNeeded()
initAppLoginPropertiesIfNeeded() initAppLoginPropertiesIfNeeded()
initAppForSecretPropertiesIfNeeded()
}
def initFabricPropertiesIfNeeded() {
def propertiesFile = file('fabric.properties')
if (!propertiesFile.exists()) {
def commentMessage = "This is autogenerated fabric property from system environment to prevent key to be committed to source control."
ant.propertyfile(file: "fabric.properties", comment: commentMessage) {
entry(key: "apiSecret", value: crashlyticsdemoApisecret)
entry(key: "apiKey", value: crashlyticsdemoApikey)
}
}
} }
def initAppLoginPropertiesIfNeeded() { def initAppLoginPropertiesIfNeeded() {
@ -211,19 +173,4 @@ def initAppLoginPropertiesIfNeeded() {
entry(key: "appLoginPassword", value: System.getProperty("appLoginPassword")) entry(key: "appLoginPassword", value: System.getProperty("appLoginPassword"))
} }
} }
} }
def initAppForSecretPropertiesIfNeeded() {
def propertiesFile = file(System.getProperty("user.home") + '/.gradle/gradle.properties')
if (!propertiesFile.exists()) {
def commentMessage = "This is autogenerated local property from system environment to prevent key to be committed to source control."
ant.propertyfile(file: System.getProperty("user.home") + "/.gradle/gradle.properties", comment: commentMessage) {
entry(key: "mercuryApiKey", value: System.getProperty("mercuryApiKey"))
entry(key: "feedbackEmail", value: System.getProperty("feedbackEmail"))
entry(key: "sourceUrl", value: System.getProperty("sourceUrl"))
entry(key: "trackerUrl", value: System.getProperty("trackerUrl"))
entry(key: "translationUrl", value: System.getProperty("translationUrl"))
entry(key: "githubToken", value: System.getProperty("githubToken"))
}
}
}

View File

@ -48,7 +48,11 @@
#} #}
-dontwarn okio.** -dontwarn okio.**
-dontwarn retrofit2.Platform$Java8 -dontwarn retrofit2.Platform$Java8
-keepattributes Signature -keep class retrofit.** { *; }
-keepclasseswithmembers class * {
@retrofit.http.* <methods>;
}
-keepattributes *Annotation*,Signature
-keepattributes Exceptions -keepattributes Exceptions
-dontwarn okio.** -dontwarn okio.**
-dontwarn javax.annotation.Nullable -dontwarn javax.annotation.Nullable

View File

@ -2,30 +2,29 @@ package apps.amine.bou.readerforselfoss
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.support.test.InstrumentationRegistry import androidx.test.InstrumentationRegistry
import android.support.test.espresso.Espresso.onView import androidx.test.espresso.Espresso.onView
import android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
import android.support.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.click
import android.support.test.espresso.action.ViewActions.closeSoftKeyboard import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
import android.support.test.espresso.action.ViewActions.pressBack import androidx.test.espresso.action.ViewActions.pressBack
import android.support.test.espresso.action.ViewActions.pressKey import androidx.test.espresso.action.ViewActions.pressKey
import android.support.test.espresso.action.ViewActions.typeText import androidx.test.espresso.action.ViewActions.typeText
import android.support.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.assertion.ViewAssertions.matches
import android.support.test.espresso.contrib.DrawerActions import androidx.test.espresso.contrib.DrawerActions
import android.support.test.espresso.intent.Intents import androidx.test.espresso.intent.Intents
import android.support.test.espresso.intent.Intents.intended import androidx.test.espresso.intent.Intents.intended
import android.support.test.espresso.intent.Intents.times import androidx.test.espresso.intent.Intents.times
import android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
import android.support.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import android.support.test.espresso.matcher.ViewMatchers.isRoot import androidx.test.espresso.matcher.ViewMatchers.isRoot
import android.support.test.espresso.matcher.ViewMatchers.withContentDescription import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
import android.support.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withId
import android.support.test.espresso.matcher.ViewMatchers.withText import androidx.test.espresso.matcher.ViewMatchers.withText
import android.support.test.rule.ActivityTestRule import androidx.test.rule.ActivityTestRule
import android.support.test.runner.AndroidJUnit4 import androidx.test.runner.AndroidJUnit4
import android.view.KeyEvent import android.view.KeyEvent
import apps.amine.bou.readerforselfoss.utils.Config import apps.amine.bou.readerforselfoss.utils.Config
import com.heinrichreimersoftware.androidissuereporter.IssueReporterLauncher
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Rule import org.junit.Rule
@ -92,25 +91,6 @@ class HomeActivityEspressoTest {
intended(hasComponent(LoginActivity::class.java.name), times(1)) intended(hasComponent(LoginActivity::class.java.name), times(1))
} }
@Test
fun drawerTesting() {
rule.launchActivity(Intent())
onView(withId(R.id.material_drawer_layout)).perform(DrawerActions.open())
onView(withText(R.string.drawer_report_bug)).perform(click())
intended(hasComponent(IssueReporterLauncher.Activity::class.java.name))
onView(isRoot()).perform(pressBack())
onView(isRoot()).perform(pressBack())
intended(hasComponent(HomeActivity::class.java.name))
onView(withId(R.id.material_drawer_layout)).perform(DrawerActions.open())
onView(withId(R.id.material_drawer_layout)).perform(DrawerActions.open())
onView(withText(R.string.drawer_action_clear)).perform(click())
}
// TODO: test articles opening and actions for cards and lists // TODO: test articles opening and actions for cards and lists
@After @After

View File

@ -2,19 +2,19 @@ package apps.amine.bou.readerforselfoss
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.support.test.InstrumentationRegistry.getInstrumentation import androidx.test.InstrumentationRegistry.getInstrumentation
import android.support.test.espresso.Espresso.onView import androidx.test.espresso.Espresso.onView
import android.support.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.click
import android.support.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.assertion.ViewAssertions.matches
import android.support.test.espresso.intent.Intents import androidx.test.espresso.intent.Intents
import android.support.test.espresso.intent.Intents.intended import androidx.test.espresso.intent.Intents.intended
import android.support.test.espresso.intent.Intents.times import androidx.test.espresso.intent.Intents.times
import android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
import android.support.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import android.support.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withId
import android.support.test.espresso.matcher.ViewMatchers.withText import androidx.test.espresso.matcher.ViewMatchers.withText
import android.support.test.rule.ActivityTestRule import androidx.test.rule.ActivityTestRule
import android.support.test.runner.AndroidJUnit4 import androidx.test.runner.AndroidJUnit4
import apps.amine.bou.readerforselfoss.utils.Config import apps.amine.bou.readerforselfoss.utils.Config
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before

View File

@ -2,25 +2,25 @@ package apps.amine.bou.readerforselfoss
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.support.test.InstrumentationRegistry import androidx.test.InstrumentationRegistry
import android.support.test.espresso.Espresso.onView import androidx.test.espresso.Espresso.onView
import android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
import android.support.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.click
import android.support.test.espresso.action.ViewActions.closeSoftKeyboard import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
import android.support.test.espresso.action.ViewActions.pressBack import androidx.test.espresso.action.ViewActions.pressBack
import android.support.test.espresso.action.ViewActions.typeText import androidx.test.espresso.action.ViewActions.typeText
import android.support.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.assertion.ViewAssertions.matches
import android.support.test.espresso.intent.Intents import androidx.test.espresso.intent.Intents
import android.support.test.espresso.intent.Intents.intended import androidx.test.espresso.intent.Intents.intended
import android.support.test.espresso.intent.Intents.times import androidx.test.espresso.intent.Intents.times
import android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
import android.support.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers
import android.support.test.espresso.matcher.ViewMatchers.isRoot import androidx.test.espresso.matcher.ViewMatchers.isRoot
import android.support.test.espresso.matcher.ViewMatchers.withEffectiveVisibility import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
import android.support.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withId
import android.support.test.espresso.matcher.ViewMatchers.withText import androidx.test.espresso.matcher.ViewMatchers.withText
import android.support.test.rule.ActivityTestRule import androidx.test.rule.ActivityTestRule
import android.support.test.runner.AndroidJUnit4 import androidx.test.runner.AndroidJUnit4
import apps.amine.bou.readerforselfoss.utils.Config import apps.amine.bou.readerforselfoss.utils.Config
import com.mikepenz.aboutlibraries.ui.LibsActivity import com.mikepenz.aboutlibraries.ui.LibsActivity
import org.junit.After import org.junit.After

View File

@ -3,13 +3,13 @@ package apps.amine.bou.readerforselfoss
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.preference.PreferenceManager import android.preference.PreferenceManager
import android.support.test.InstrumentationRegistry.getInstrumentation import androidx.test.InstrumentationRegistry.getInstrumentation
import android.support.test.espresso.intent.Intents import androidx.test.espresso.intent.Intents
import android.support.test.espresso.intent.Intents.intended import androidx.test.espresso.intent.Intents.intended
import android.support.test.espresso.intent.Intents.times import androidx.test.espresso.intent.Intents.times
import android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
import android.support.test.rule.ActivityTestRule import androidx.test.rule.ActivityTestRule
import android.support.test.runner.AndroidJUnit4 import androidx.test.runner.AndroidJUnit4
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before

View File

@ -1,7 +1,7 @@
package apps.amine.bou.readerforselfoss package apps.amine.bou.readerforselfoss
import android.support.design.widget.TextInputLayout import com.google.android.material.textfield.TextInputLayout
import android.support.test.espresso.matcher.ViewMatchers import androidx.test.espresso.matcher.ViewMatchers
import android.view.View import android.view.View
import org.hamcrest.Description import org.hamcrest.Description
import org.hamcrest.Matcher import org.hamcrest.Matcher

View File

@ -1,13 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="apps.amine.bou.readerforselfoss"> package="apps.amine.bou.readerforselfoss"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<!-- For firebase only -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application <application
android:name=".MyApp" android:name=".MyApp"
android:allowBackup="true" android:allowBackup="true"
@ -77,6 +74,8 @@
<meta-data android:name="android.webkit.WebView.EnableSafeBrowsing" <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
android:value="true" /> android:value="true" />
<meta-data android:name="android.max_aspect" android:value="2.1" />
</application> </application>
</manifest> </manifest>

View File

@ -1,10 +1,11 @@
package apps.amine.bou.readerforselfoss package apps.amine.bou.readerforselfoss
import android.content.Intent import android.content.Intent
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.preference.PreferenceManager import android.preference.PreferenceManager
import android.support.constraint.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import android.support.v7.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import android.view.View import android.view.View
import android.widget.AdapterView import android.widget.AdapterView
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
@ -16,6 +17,8 @@ import android.widget.Toast
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
import apps.amine.bou.readerforselfoss.api.selfoss.Spout import apps.amine.bou.readerforselfoss.api.selfoss.Spout
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
import apps.amine.bou.readerforselfoss.themes.AppColors
import apps.amine.bou.readerforselfoss.themes.Toppings
import apps.amine.bou.readerforselfoss.utils.Config import apps.amine.bou.readerforselfoss.utils.Config
import apps.amine.bou.readerforselfoss.utils.isBaseUrlValid import apps.amine.bou.readerforselfoss.utils.isBaseUrlValid
import com.ftinc.scoop.Scoop import com.ftinc.scoop.Scoop
@ -23,29 +26,70 @@ import kotlinx.android.synthetic.main.activity_add_source.*
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import android.graphics.PorterDuff
class AddSourceActivity : AppCompatActivity() { class AddSourceActivity : AppCompatActivity() {
private var mSpoutsValue: String? = null private var mSpoutsValue: String? = null
private lateinit var api: SelfossApi
private lateinit var appColors: AppColors
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
appColors = AppColors(this@AddSourceActivity)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
Scoop.getInstance().apply(this)
setContentView(R.layout.activity_add_source) setContentView(R.layout.activity_add_source)
val scoop = Scoop.getInstance()
scoop.bind(this, Toppings.PRIMARY.value, toolbar)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
}
val drawable = nameInput.background
drawable.setColorFilter(appColors.colorAccent, PorterDuff.Mode.SRC_ATOP)
// TODO: clean
if(Build.VERSION.SDK_INT > 16) {
nameInput.background = drawable
} else{
nameInput.setBackgroundDrawable(drawable)
}
val drawable1 = sourceUri.background
drawable1.setColorFilter(appColors.colorAccent, PorterDuff.Mode.SRC_ATOP)
if(Build.VERSION.SDK_INT > 16) {
sourceUri.background = drawable1
} else{
sourceUri.setBackgroundDrawable(drawable1)
}
val drawable2 = tags.background
drawable2.setColorFilter(appColors.colorAccent, PorterDuff.Mode.SRC_ATOP)
if(Build.VERSION.SDK_INT > 16) {
tags.background = drawable2
} else{
tags.setBackgroundDrawable(drawable2)
}
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true)
var api: SelfossApi? = null
try { try {
val prefs = PreferenceManager.getDefaultSharedPreferences(this) val prefs = PreferenceManager.getDefaultSharedPreferences(this)
api = SelfossApi( api = SelfossApi(
this, this,
this@AddSourceActivity, this@AddSourceActivity,
prefs.getBoolean("isSelfSignedCert", false), prefs.getBoolean("isSelfSignedCert", false),
prefs.getBoolean("should_log_everything", false) prefs.getBoolean("should_log_everything", false)
) )
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
mustLoginToAddSource() mustLoginToAddSource()
@ -53,10 +97,15 @@ class AddSourceActivity : AppCompatActivity() {
maybeGetDetailsFromIntentSharing(intent, sourceUri, nameInput) maybeGetDetailsFromIntentSharing(intent, sourceUri, nameInput)
saveBtn.setTextColor(appColors.colorAccent)
saveBtn.setOnClickListener { saveBtn.setOnClickListener {
handleSaveSource(tags, nameInput.text.toString(), sourceUri.text.toString(), api!!) handleSaveSource(tags, nameInput.text.toString(), sourceUri.text.toString(), api!!)
} }
}
override fun onResume() {
super.onResume()
val config = Config(this) val config = Config(this)
if (config.baseUrl.isEmpty() || !config.baseUrl.isBaseUrlValid()) { if (config.baseUrl.isEmpty() || !config.baseUrl.isBaseUrlValid()) {
@ -67,16 +116,18 @@ class AddSourceActivity : AppCompatActivity() {
} }
private fun handleSpoutsSpinner( private fun handleSpoutsSpinner(
spoutsSpinner: Spinner, spoutsSpinner: Spinner,
api: SelfossApi?, api: SelfossApi?,
mProgress: ProgressBar, mProgress: ProgressBar,
formContainer: ConstraintLayout formContainer: ConstraintLayout
) { ) {
val spoutsKV = HashMap<String, String>() val spoutsKV = HashMap<String, String>()
spoutsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { spoutsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(adapterView: AdapterView<*>, view: View, i: Int, l: Long) { override fun onItemSelected(adapterView: AdapterView<*>, view: View?, i: Int, l: Long) {
val spoutName = (view as TextView).text.toString() if (view != null) {
mSpoutsValue = spoutsKV[spoutName] val spoutName = (view as TextView).text.toString()
mSpoutsValue = spoutsKV[spoutName]
}
} }
override fun onNothingSelected(adapterView: AdapterView<*>) { override fun onNothingSelected(adapterView: AdapterView<*>) {
@ -87,26 +138,26 @@ class AddSourceActivity : AppCompatActivity() {
var items: Map<String, Spout> var items: Map<String, Spout>
api!!.spouts().enqueue(object : Callback<Map<String, Spout>> { api!!.spouts().enqueue(object : Callback<Map<String, Spout>> {
override fun onResponse( override fun onResponse(
call: Call<Map<String, Spout>>, call: Call<Map<String, Spout>>,
response: Response<Map<String, Spout>> response: Response<Map<String, Spout>>
) { ) {
if (response.body() != null) { if (response.body() != null) {
items = response.body()!! items = response.body()!!
val itemsStrings = items.map { it.value.name } val itemsStrings = items.map { it.value.name }
for ((key, value) in items) { for ((key, value) in items) {
spoutsKV.put(value.name, key) spoutsKV[value.name] = key
} }
mProgress.visibility = View.GONE mProgress.visibility = View.GONE
formContainer.visibility = View.VISIBLE formContainer.visibility = View.VISIBLE
val spinnerArrayAdapter = val spinnerArrayAdapter =
ArrayAdapter( ArrayAdapter(
this@AddSourceActivity, this@AddSourceActivity,
android.R.layout.simple_spinner_item, android.R.layout.simple_spinner_item,
itemsStrings itemsStrings
) )
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spoutsSpinner.adapter = spinnerArrayAdapter spoutsSpinner.adapter = spinnerArrayAdapter
} else { } else {
@ -120,9 +171,9 @@ class AddSourceActivity : AppCompatActivity() {
private fun handleProblemWithSpouts() { private fun handleProblemWithSpouts() {
Toast.makeText( Toast.makeText(
this@AddSourceActivity, this@AddSourceActivity,
R.string.cant_get_spouts, R.string.cant_get_spouts,
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
mProgress.visibility = View.GONE mProgress.visibility = View.GONE
} }
@ -130,9 +181,9 @@ class AddSourceActivity : AppCompatActivity() {
} }
private fun maybeGetDetailsFromIntentSharing( private fun maybeGetDetailsFromIntentSharing(
intent: Intent, intent: Intent,
sourceUri: EditText, sourceUri: EditText,
nameInput: EditText nameInput: EditText
) { ) {
if (Intent.ACTION_SEND == intent.action && "text/plain" == intent.type) { if (Intent.ACTION_SEND == intent.action && "text/plain" == intent.type) {
sourceUri.setText(intent.getStringExtra(Intent.EXTRA_TEXT)) sourceUri.setText(intent.getStringExtra(Intent.EXTRA_TEXT))
@ -149,38 +200,39 @@ class AddSourceActivity : AppCompatActivity() {
private fun handleSaveSource(tags: EditText, title: String, url: String, api: SelfossApi) { private fun handleSaveSource(tags: EditText, title: String, url: String, api: SelfossApi) {
val sourceDetailsAvailable = title.isEmpty() || url.isEmpty() || mSpoutsValue == null || mSpoutsValue!!.isEmpty() val sourceDetailsAvailable =
title.isEmpty() || url.isEmpty() || mSpoutsValue == null || mSpoutsValue!!.isEmpty()
if (sourceDetailsAvailable) { if (sourceDetailsAvailable) {
Toast.makeText(this, R.string.form_not_complete, Toast.LENGTH_SHORT).show() Toast.makeText(this, R.string.form_not_complete, Toast.LENGTH_SHORT).show()
} else { } else {
api.createSource( api.createSource(
title, title,
url, url,
mSpoutsValue!!, mSpoutsValue!!,
tags.text.toString(), tags.text.toString(),
"" ""
).enqueue(object : Callback<SuccessResponse> { ).enqueue(object : Callback<SuccessResponse> {
override fun onResponse( override fun onResponse(
call: Call<SuccessResponse>, call: Call<SuccessResponse>,
response: Response<SuccessResponse> response: Response<SuccessResponse>
) { ) {
if (response.body() != null && response.body()!!.isSuccess) { if (response.body() != null && response.body()!!.isSuccess) {
finish() finish()
} else { } else {
Toast.makeText( Toast.makeText(
this@AddSourceActivity, this@AddSourceActivity,
R.string.cant_create_source, R.string.cant_create_source,
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
} }
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) { override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
Toast.makeText( Toast.makeText(
this@AddSourceActivity, this@AddSourceActivity,
R.string.cant_create_source, R.string.cant_create_source,
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
}) })

View File

@ -7,7 +7,7 @@ import android.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.preference.PreferenceManager import android.preference.PreferenceManager
import android.support.v7.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import android.view.View import android.view.View
class IntroActivity : MaterialIntroActivity() { class IntroActivity : MaterialIntroActivity() {
@ -18,42 +18,42 @@ class IntroActivity : MaterialIntroActivity() {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
addSlide( addSlide(
SlideFragmentBuilder() SlideFragmentBuilder()
.backgroundColor(R.color.colorPrimary) .backgroundColor(R.color.colorPrimary)
.buttonsColor(R.color.colorAccent) .buttonsColor(R.color.colorAccent)
.image(R.drawable.web_hi_res_512) .image(R.drawable.web_hi_res_512)
.title(getString(R.string.intro_hello_title)) .title(getString(R.string.intro_hello_title))
.description(getString(R.string.intro_hello_message)) .description(getString(R.string.intro_hello_message))
.build() .build()
) )
addSlide( addSlide(
SlideFragmentBuilder() SlideFragmentBuilder()
.backgroundColor(R.color.colorAccent) .backgroundColor(R.color.colorAccent)
.buttonsColor(R.color.colorPrimary) .buttonsColor(R.color.colorPrimary)
.image(R.drawable.ic_info_outline_white_48px) .image(R.drawable.ic_info_outline_white_48px)
.title(getString(R.string.intro_needs_selfoss_title)) .title(getString(R.string.intro_needs_selfoss_title))
.description(getString(R.string.intro_needs_selfoss_message)) .description(getString(R.string.intro_needs_selfoss_message))
.build(), .build(),
MessageButtonBehaviour( MessageButtonBehaviour(
View.OnClickListener { View.OnClickListener {
val browserIntent = Intent( val browserIntent = Intent(
Intent.ACTION_VIEW, Intent.ACTION_VIEW,
Uri.parse("https://selfoss.aditu.de") Uri.parse("https://selfoss.aditu.de")
) )
startActivity(browserIntent) startActivity(browserIntent)
}, getString(R.string.intro_needs_selfoss_link) }, getString(R.string.intro_needs_selfoss_link)
) )
) )
addSlide( addSlide(
SlideFragmentBuilder() SlideFragmentBuilder()
.backgroundColor(R.color.colorPrimaryDark) .backgroundColor(R.color.colorPrimaryDark)
.buttonsColor(R.color.colorAccentDark) .buttonsColor(R.color.colorAccentDark)
.image(R.drawable.ic_thumb_up_white_48px) .image(R.drawable.ic_thumb_up_white_48px)
.title(getString(R.string.intro_all_set_title)) .title(getString(R.string.intro_all_set_title))
.description(getString(R.string.intro_all_set_message)) .description(getString(R.string.intro_all_set_message))
.build() .build()
) )
} }

View File

@ -6,8 +6,8 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.os.Bundle import android.os.Bundle
import android.support.v7.app.AlertDialog import androidx.appcompat.app.AlertDialog
import android.support.v7.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import android.text.TextUtils import android.text.TextUtils
import android.view.Menu import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
@ -17,14 +17,14 @@ import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
import apps.amine.bou.readerforselfoss.themes.AppColors
import apps.amine.bou.readerforselfoss.utils.Config import apps.amine.bou.readerforselfoss.utils.Config
import apps.amine.bou.readerforselfoss.utils.isBaseUrlValid import apps.amine.bou.readerforselfoss.utils.isBaseUrlValid
import com.crashlytics.android.Crashlytics import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
import com.ftinc.scoop.Scoop
import com.google.firebase.analytics.FirebaseAnalytics
import com.mikepenz.aboutlibraries.Libs import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.LibsBuilder import com.mikepenz.aboutlibraries.LibsBuilder
import kotlinx.android.synthetic.main.activity_login.* import kotlinx.android.synthetic.main.activity_login.*
import org.acra.ACRA
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
@ -38,13 +38,15 @@ class LoginActivity : AppCompatActivity() {
private lateinit var settings: SharedPreferences private lateinit var settings: SharedPreferences
private lateinit var editor: SharedPreferences.Editor private lateinit var editor: SharedPreferences.Editor
private lateinit var firebaseAnalytics: FirebaseAnalytics
private lateinit var userIdentifier: String private lateinit var userIdentifier: String
private var logErrors: Boolean = false private var logErrors: Boolean = false
private lateinit var appColors: AppColors
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
appColors = AppColors(this@LoginActivity)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
Scoop.getInstance().apply(this)
setContentView(R.layout.activity_login) setContentView(R.layout.activity_login)
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
@ -62,8 +64,6 @@ class LoginActivity : AppCompatActivity() {
goToMain() goToMain()
} }
firebaseAnalytics = FirebaseAnalytics.getInstance(this)
handleActions() handleActions()
} }
@ -77,13 +77,13 @@ class LoginActivity : AppCompatActivity() {
} }
passwordView.setOnEditorActionListener( passwordView.setOnEditorActionListener(
TextView.OnEditorActionListener { _, id, _ -> TextView.OnEditorActionListener { _, id, _ ->
if (id == R.id.loginView || id == EditorInfo.IME_NULL) { if (id == R.id.loginView || id == EditorInfo.IME_NULL) {
attemptLogin() attemptLogin()
return@OnEditorActionListener true return@OnEditorActionListener true
}
false
} }
false
}
) )
signInButton.setOnClickListener { attemptLogin() } signInButton.setOnClickListener { attemptLogin() }
@ -111,9 +111,9 @@ class LoginActivity : AppCompatActivity() {
alertDialog.setTitle(getString(R.string.warning_wrong_url)) alertDialog.setTitle(getString(R.string.warning_wrong_url))
alertDialog.setMessage(getString(R.string.base_url_error)) alertDialog.setMessage(getString(R.string.base_url_error))
alertDialog.setButton( alertDialog.setButton(
AlertDialog.BUTTON_NEUTRAL, AlertDialog.BUTTON_NEUTRAL,
"OK", "OK",
{ dialog, _ -> dialog.dismiss() } { dialog, _ -> dialog.dismiss() }
) )
alertDialog.show() alertDialog.show()
} }
@ -154,9 +154,9 @@ class LoginActivity : AppCompatActivity() {
alertDialog.setTitle(getString(R.string.warning_wrong_url)) alertDialog.setTitle(getString(R.string.warning_wrong_url))
alertDialog.setMessage(getString(R.string.text_wrong_url)) alertDialog.setMessage(getString(R.string.text_wrong_url))
alertDialog.setButton( alertDialog.setButton(
AlertDialog.BUTTON_NEUTRAL, AlertDialog.BUTTON_NEUTRAL,
"OK", "OK",
{ dialog, _ -> dialog.dismiss() } { dialog, _ -> dialog.dismiss() }
) )
alertDialog.show() alertDialog.show()
inValidCount = 0 inValidCount = 0
@ -191,10 +191,10 @@ class LoginActivity : AppCompatActivity() {
editor.apply() editor.apply()
val api = SelfossApi( val api = SelfossApi(
this, this,
this@LoginActivity, this@LoginActivity,
isWithSelfSignedCert, isWithSelfSignedCert,
isWithSelfSignedCert isWithSelfSignedCert
) )
api.login().enqueue(object : Callback<SuccessResponse> { api.login().enqueue(object : Callback<SuccessResponse> {
private fun preferenceError(t: Throwable) { private fun preferenceError(t: Throwable) {
@ -210,24 +210,21 @@ class LoginActivity : AppCompatActivity() {
httpLoginView.error = getString(R.string.wrong_infos) httpLoginView.error = getString(R.string.wrong_infos)
httpPasswordView.error = getString(R.string.wrong_infos) httpPasswordView.error = getString(R.string.wrong_infos)
if (logErrors) { if (logErrors) {
Crashlytics.setUserIdentifier(userIdentifier) ACRA.getErrorReporter().maybeHandleSilentException(t, this@LoginActivity)
Crashlytics.log(100, "LOGIN_DEBUG_ERRROR", t.message)
Crashlytics.logException(t)
Toast.makeText( Toast.makeText(
this@LoginActivity, this@LoginActivity,
t.message, t.message,
Toast.LENGTH_LONG Toast.LENGTH_LONG
).show() ).show()
} }
showProgress(false) showProgress(false)
} }
override fun onResponse( override fun onResponse(
call: Call<SuccessResponse>, call: Call<SuccessResponse>,
response: Response<SuccessResponse> response: Response<SuccessResponse>
) { ) {
if (response.body() != null && response.body()!!.isSuccess) { if (response.body() != null && response.body()!!.isSuccess) {
firebaseAnalytics.logEvent(FirebaseAnalytics.Event.LOGIN, Bundle())
goToMain() goToMain()
} else { } else {
preferenceError(Exception("No response body...")) preferenceError(Exception("No response body..."))
@ -246,28 +243,28 @@ class LoginActivity : AppCompatActivity() {
loginForm.visibility = if (show) View.GONE else View.VISIBLE loginForm.visibility = if (show) View.GONE else View.VISIBLE
loginForm loginForm
.animate() .animate()
.setDuration(shortAnimTime.toLong()) .setDuration(shortAnimTime.toLong())
.alpha( .alpha(
if (show) 0F else 1F if (show) 0F else 1F
).setListener(object : AnimatorListenerAdapter() { ).setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) { override fun onAnimationEnd(animation: Animator) {
loginForm.visibility = if (show) View.GONE else View.VISIBLE loginForm.visibility = if (show) View.GONE else View.VISIBLE
} }
} }
) )
loginProgress.visibility = if (show) View.VISIBLE else View.GONE loginProgress.visibility = if (show) View.VISIBLE else View.GONE
loginProgress loginProgress
.animate() .animate()
.setDuration(shortAnimTime.toLong()) .setDuration(shortAnimTime.toLong())
.alpha( .alpha(
if (show) 1F else 0F if (show) 1F else 0F
).setListener(object : AnimatorListenerAdapter() { ).setListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator) { override fun onAnimationEnd(animation: Animator) {
loginProgress.visibility = if (show) View.VISIBLE else View.GONE loginProgress.visibility = if (show) View.VISIBLE else View.GONE
} }
} }
) )
} }
@ -281,10 +278,10 @@ class LoginActivity : AppCompatActivity() {
when (item.itemId) { when (item.itemId) {
R.id.about -> { R.id.about -> {
LibsBuilder() LibsBuilder()
.withActivityStyle(Libs.ActivityStyle.LIGHT_DARK_TOOLBAR) .withActivityStyle(Libs.ActivityStyle.LIGHT_DARK_TOOLBAR)
.withAboutIconShown(true) .withAboutIconShown(true)
.withAboutVersionShown(true) .withAboutVersionShown(true)
.start(this) .start(this)
return true return true
} }
R.id.login_debug -> { R.id.login_debug -> {

View File

@ -3,7 +3,7 @@ package apps.amine.bou.readerforselfoss
import android.content.Intent import android.content.Intent
import android.os.Bundle import android.os.Bundle
import android.preference.PreferenceManager import android.preference.PreferenceManager
import android.support.v7.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
@ -14,7 +14,7 @@ class MainActivity : AppCompatActivity() {
if (PreferenceManager.getDefaultSharedPreferences(baseContext).getBoolean( if (PreferenceManager.getDefaultSharedPreferences(baseContext).getBoolean(
"firstStart", "firstStart",
true true
)) { )) {
val i = Intent(this@MainActivity, IntroActivity::class.java) val i = Intent(this@MainActivity, IntroActivity::class.java)
startActivity(i) startActivity(i)
} else { } else {

View File

@ -4,33 +4,49 @@ import android.content.Context
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.net.Uri import android.net.Uri
import android.preference.PreferenceManager import android.preference.PreferenceManager
import android.support.multidex.MultiDexApplication import androidx.multidex.MultiDexApplication
import android.widget.ImageView import android.widget.ImageView
import apps.amine.bou.readerforselfoss.utils.Config import apps.amine.bou.readerforselfoss.utils.Config
import com.anupcowkur.reservoir.Reservoir
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
import com.crashlytics.android.Crashlytics
import com.ftinc.scoop.Scoop import com.ftinc.scoop.Scoop
import com.github.stkent.amplify.feedback.DefaultEmailFeedbackCollector import com.github.stkent.amplify.feedback.DefaultEmailFeedbackCollector
import com.github.stkent.amplify.feedback.GooglePlayStoreFeedbackCollector import com.github.stkent.amplify.feedback.GooglePlayStoreFeedbackCollector
import com.github.stkent.amplify.tracking.Amplify import com.github.stkent.amplify.tracking.Amplify
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader
import com.mikepenz.materialdrawer.util.DrawerImageLoader import com.mikepenz.materialdrawer.util.DrawerImageLoader
import io.fabric.sdk.android.Fabric import org.acra.ACRA
import org.acra.ReportField
import org.acra.annotation.AcraCore
import org.acra.annotation.AcraDialog
import org.acra.annotation.AcraHttpSender
import org.acra.sender.HttpSender
import java.io.IOException import java.io.IOException
import java.util.UUID.randomUUID import java.util.UUID.randomUUID
@AcraHttpSender(uri = "http://amine-bou.fr:5984/acra-selfoss/_design/acra-storage/_update/report",
basicAuthLogin = "selfoss",
basicAuthPassword = "selfoss",
httpMethod = HttpSender.Method.PUT)
@AcraDialog(resText = R.string.crash_dialog_text,
resCommentPrompt = R.string.crash_dialog_comment,
resTheme = android.R.style.Theme_DeviceDefault_Dialog)
@AcraCore(reportContent = [ReportField.REPORT_ID, ReportField.INSTALLATION_ID,
ReportField.APP_VERSION_CODE, ReportField.APP_VERSION_NAME,
ReportField.BUILD, ReportField.ANDROID_VERSION, ReportField.BRAND, ReportField.PHONE_MODEL,
ReportField.AVAILABLE_MEM_SIZE, ReportField.TOTAL_MEM_SIZE,
ReportField.STACK_TRACE, ReportField.APPLICATION_LOG, ReportField.LOGCAT,
ReportField.INITIAL_CONFIGURATION, ReportField.CRASH_CONFIGURATION, ReportField.IS_SILENT,
ReportField.USER_APP_START_DATE, ReportField.USER_COMMENT, ReportField.USER_CRASH_DATE, ReportField.USER_EMAIL, ReportField.CUSTOM_DATA],
buildConfigClass = BuildConfig::class)
class MyApp : MultiDexApplication() { class MyApp : MultiDexApplication() {
override fun onCreate() { override fun onCreate() {
super.onCreate() super.onCreate()
Fabric.with(this, Crashlytics())
initAmplify() initAmplify()
initCache()
val prefs = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE) val prefs = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
if (prefs.getString("unique_id", "").isEmpty()) { if (prefs.getString("unique_id", "").isEmpty()) {
val editor = prefs.edit() val editor = prefs.edit()
@ -45,33 +61,33 @@ class MyApp : MultiDexApplication() {
tryToHandleBug() tryToHandleBug()
} }
private fun initAmplify() { override fun attachBaseContext(base: Context?) {
Amplify.initSharedInstance(this) super.attachBaseContext(base)
.setPositiveFeedbackCollectors(GooglePlayStoreFeedbackCollector()) val prefs = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
.setCriticalFeedbackCollectors(DefaultEmailFeedbackCollector(BuildConfig.FEEDBACK_EMAIL)) ACRA.init(this)
.applyAllDefaultRules() ACRA.getErrorReporter().putCustomData("unique_id", prefs.getString("unique_id", ""))
} }
private fun initCache() { private fun initAmplify() {
try { Amplify.initSharedInstance(this)
Reservoir.init(this, 8192) //in bytes .setPositiveFeedbackCollectors(GooglePlayStoreFeedbackCollector())
} catch (e: IOException) { .setCriticalFeedbackCollectors(DefaultEmailFeedbackCollector(Config.feedbackEmail))
//failure .applyAllDefaultRules()
}
} }
private fun initDrawerImageLoader() { private fun initDrawerImageLoader() {
DrawerImageLoader.init(object : AbstractDrawerImageLoader() { DrawerImageLoader.init(object : AbstractDrawerImageLoader() {
override fun set( override fun set(
imageView: ImageView?, imageView: ImageView?,
uri: Uri?, uri: Uri?,
placeholder: Drawable?, placeholder: Drawable?,
tag: String? tag: String?
) { ) {
Glide.with(imageView?.context) Glide.with(imageView?.context)
.load(uri) .load(uri)
.apply(RequestOptions.fitCenterTransform().placeholder(placeholder)) .apply(RequestOptions.fitCenterTransform().placeholder(placeholder))
.into(imageView) .into(imageView)
} }
override fun cancel(imageView: ImageView?) { override fun cancel(imageView: ImageView?) {
@ -86,22 +102,10 @@ class MyApp : MultiDexApplication() {
private fun initTheme() { private fun initTheme() {
Scoop.waffleCone() Scoop.waffleCone()
.addFlavor(getString(R.string.default_theme), R.style.NoBar, true) .addFlavor(getString(R.string.default_theme), R.style.NoBar, true)
.addFlavor(getString(R.string.default_dark_theme), R.style.NoBarDark) .addFlavor(getString(R.string.default_dark_theme), R.style.NoBarDark, false)
.addFlavor(getString(R.string.teal_orange_theme), R.style.NoBarTealOrange) .setSharedPreferences(PreferenceManager.getDefaultSharedPreferences(this))
.addFlavor(getString(R.string.teal_orange_dark_theme), R.style.NoBarTealOrangeDark) .initialize()
.addFlavor(getString(R.string.cyan_pink_theme), R.style.NoBarCyanPink)
.addFlavor(getString(R.string.cyan_pink_dark_theme), R.style.NoBarCyanPinkDark)
.addFlavor(getString(R.string.grey_orange_theme), R.style.NoBarGreyOrange)
.addFlavor(getString(R.string.grey_orange_dark_theme), R.style.NoBarGreyOrangeDark)
.addFlavor(getString(R.string.blue_amber_theme), R.style.NoBarBlueAmber)
.addFlavor(getString(R.string.blue_amber_dark_theme), R.style.NoBarBlueAmberDark)
.addFlavor(getString(R.string.indigo_pink_theme), R.style.NoBarIndigoPink)
.addFlavor(getString(R.string.indigo_pink_dark_theme), R.style.NoBarIndigoPinkDark)
.addFlavor(getString(R.string.red_teal_theme), R.style.NoBarRedTeal)
.addFlavor(getString(R.string.red_teal_dark_theme), R.style.NoBarRedTealDark)
.setSharedPreferences(PreferenceManager.getDefaultSharedPreferences(this))
.initialize()
} }
private fun tryToHandleBug() { private fun tryToHandleBug() {
@ -109,8 +113,8 @@ class MyApp : MultiDexApplication() {
Thread.setDefaultUncaughtExceptionHandler { thread, e -> Thread.setDefaultUncaughtExceptionHandler { thread, e ->
if (e is java.lang.NoClassDefFoundError && e.stackTrace.asList().any { if (e is java.lang.NoClassDefFoundError && e.stackTrace.asList().any {
it.toString().contains("android.view.ViewDebug") it.toString().contains("android.view.ViewDebug")
}) { }) {
Unit Unit
} else { } else {
oldHandler.uncaughtException(thread, e) oldHandler.uncaughtException(thread, e)

View File

@ -1,24 +1,32 @@
package apps.amine.bou.readerforselfoss package apps.amine.bou.readerforselfoss
import android.content.Context import android.graphics.drawable.ColorDrawable
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.preference.PreferenceManager import android.preference.PreferenceManager
import android.support.v4.app.FragmentManager import androidx.fragment.app.FragmentManager
import android.support.v4.app.FragmentStatePagerAdapter import androidx.fragment.app.FragmentStatePagerAdapter
import android.support.v4.view.ViewPager import androidx.core.content.ContextCompat
import android.support.v7.app.AppCompatActivity import androidx.viewpager.widget.ViewPager
import androidx.appcompat.app.AppCompatActivity
import android.view.Menu
import android.view.MenuItem import android.view.MenuItem
import android.view.ViewGroup
import android.widget.Toast
import apps.amine.bou.readerforselfoss.api.selfoss.Item import apps.amine.bou.readerforselfoss.api.selfoss.Item
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
import apps.amine.bou.readerforselfoss.fragments.ArticleFragment import apps.amine.bou.readerforselfoss.fragments.ArticleFragment
import apps.amine.bou.readerforselfoss.themes.AppColors
import apps.amine.bou.readerforselfoss.themes.Toppings
import apps.amine.bou.readerforselfoss.transformers.DepthPageTransformer import apps.amine.bou.readerforselfoss.transformers.DepthPageTransformer
import apps.amine.bou.readerforselfoss.utils.Config import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
import apps.amine.bou.readerforselfoss.utils.succeeded import apps.amine.bou.readerforselfoss.utils.succeeded
import com.crashlytics.android.Crashlytics import apps.amine.bou.readerforselfoss.utils.toggleStar
import com.ftinc.scoop.Scoop import com.ftinc.scoop.Scoop
import kotlinx.android.synthetic.main.activity_reader.* import kotlinx.android.synthetic.main.activity_reader.*
import me.relex.circleindicator.CircleIndicator import me.relex.circleindicator.CircleIndicator
import org.acra.ACRA
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
@ -26,114 +34,130 @@ import retrofit2.Response
class ReaderActivity : AppCompatActivity() { class ReaderActivity : AppCompatActivity() {
private var markOnScroll: Boolean = false private var markOnScroll: Boolean = false
private var debugReadingItems: Boolean = false
private var currentItem: Int = 0
private lateinit var userIdentifier: String
private var useWebview: Boolean = false private lateinit var api: SelfossApi
private lateinit var toolbarMenu: Menu
private fun showMenuItem(willAddToFavorite: Boolean) {
toolbarMenu.findItem(R.id.save).isVisible = willAddToFavorite
toolbarMenu.findItem(R.id.unsave).isVisible = !willAddToFavorite
}
private fun canFavorite() {
showMenuItem(true)
}
private fun canRemoveFromFavorite() {
showMenuItem(false)
}
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
Scoop.getInstance().apply(this)
setContentView(R.layout.activity_reader) setContentView(R.layout.activity_reader)
val scoop = Scoop.getInstance()
scoop.bind(this, Toppings.PRIMARY.value, toolBar)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
}
setSupportActionBar(toolBar) setSupportActionBar(toolBar)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true)
val settings = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE) val prefs = PreferenceManager.getDefaultSharedPreferences(this)
val sharedPref = PreferenceManager.getDefaultSharedPreferences(this)
val debugReadingItems = sharedPref.getBoolean("read_debug", false) debugReadingItems = prefs.getBoolean("read_debug", false)
val userIdentifier = sharedPref.getString("unique_id", "") userIdentifier = prefs.getString("unique_id", "")
markOnScroll = sharedPref.getBoolean("mark_on_scroll", false) markOnScroll = prefs.getBoolean("mark_on_scroll", false)
useWebview = sharedPref.getBoolean("prefer_webview_in_article_viewer", false)
api = SelfossApi(
this,
this@ReaderActivity,
prefs.getBoolean("isSelfSignedCert", false),
prefs.getBoolean("should_log_everything", false)
)
if (allItems.isEmpty()) { if (allItems.isEmpty()) {
Crashlytics.setUserIdentifier(userIdentifier)
Crashlytics.log(
100,
"READER_ITEMS_EMPTY",
"Items empty when trying to open the Article Reader. Was (static) companion object field set ?"
)
Crashlytics.logException(Exception("Empty items on Reader Activity."))
finish() finish()
} }
val api = SelfossApi( currentItem = intent.getIntExtra("currentItem", 0)
this,
this@ReaderActivity,
settings.getBoolean("isSelfSignedCert", false),
sharedPref.getBoolean("should_log_everything", false)
)
val currentItem = intent.getIntExtra("currentItem", 0) readItem(allItems[currentItem].id)
var adapter = ScreenSlidePagerAdapter(supportFragmentManager) pager.adapter = ScreenSlidePagerAdapter(supportFragmentManager, AppColors(this@ReaderActivity))
pager.adapter = adapter
pager.currentItem = currentItem pager.currentItem = currentItem
}
override fun onResume() {
super.onResume()
notifyAdapter()
pager.setPageTransformer(true, DepthPageTransformer()) pager.setPageTransformer(true, DepthPageTransformer())
(indicator as CircleIndicator).setViewPager(pager) (indicator as CircleIndicator).setViewPager(pager)
pager.addOnPageChangeListener(
object : ViewPager.SimpleOnPageChangeListener() {
override fun onPageSelected(position: Int) {
if (allItems[position].starred) {
canRemoveFromFavorite()
} else {
canFavorite()
}
readItem(allItems[pager.currentItem].id)
}
}
)
}
fun readItem(id: String) {
if (markOnScroll) { if (markOnScroll) {
pager.addOnPageChangeListener( api.markItem(id).enqueue(
object : ViewPager.SimpleOnPageChangeListener() { object : Callback<SuccessResponse> {
var isLastItem = false override fun onResponse(
call: Call<SuccessResponse>,
override fun onPageSelected(position: Int) { response: Response<SuccessResponse>
isLastItem = (position === (allItems.size - 1)) ) {
} if (!response.succeeded() && debugReadingItems) {
val message =
override fun onPageScrollStateChanged(state: Int) { "message: ${response.message()} " +
if (state === ViewPager.SCROLL_STATE_DRAGGING || (state === ViewPager.SCROLL_STATE_IDLE && isLastItem)) { "response isSuccess: ${response.isSuccessful} " +
api.markItem(allItems[pager.currentItem].id).enqueue( "response code: ${response.code()} " +
object : Callback<SuccessResponse> { "response message: ${response.message()} " +
override fun onResponse( "response errorBody: ${response.errorBody()?.string()} " +
call: Call<SuccessResponse>, "body success: ${response.body()?.success} " +
response: Response<SuccessResponse> "body isSuccess: ${response.body()?.isSuccess}"
) { ACRA.getErrorReporter()
if (!response.succeeded() && debugReadingItems) { .maybeHandleSilentException(Exception(message), this@ReaderActivity)
val message =
"message: ${response.message()} " +
"response isSuccess: ${response.isSuccessful} " +
"response code: ${response.code()} " +
"response message: ${response.message()} " +
"response errorBody: ${response.errorBody()?.string()} " +
"body success: ${response.body()?.success} " +
"body isSuccess: ${response.body()?.isSuccess}"
Crashlytics.setUserIdentifier(userIdentifier)
Crashlytics.log(
100,
"READ_DEBUG_SUCCESS",
message
)
Crashlytics.logException(Exception("Was success, but did it work ?"))
}
}
override fun onFailure(
call: Call<SuccessResponse>,
t: Throwable
) {
if (debugReadingItems) {
Crashlytics.setUserIdentifier(userIdentifier)
Crashlytics.log(
100,
"READ_DEBUG_ERROR",
t.message
)
Crashlytics.logException(t)
}
}
}
)
}
} }
} }
override fun onFailure(
call: Call<SuccessResponse>,
t: Throwable
) {
if (debugReadingItems) {
ACRA.getErrorReporter().maybeHandleSilentException(t, this@ReaderActivity)
}
}
}
) )
} }
} }
private fun notifyAdapter() {
(pager.adapter as ScreenSlidePagerAdapter).notifyDataSetChanged()
}
override fun onPause() { override fun onPause() {
super.onPause() super.onPause()
if (markOnScroll) { if (markOnScroll) {
@ -146,14 +170,37 @@ class ReaderActivity : AppCompatActivity() {
oldInstanceState!!.clear() oldInstanceState!!.clear()
} }
private inner class ScreenSlidePagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) { private inner class ScreenSlidePagerAdapter(fm: FragmentManager, val appColors: AppColors) :
FragmentStatePagerAdapter(fm) {
override fun getCount(): Int { override fun getCount(): Int {
return allItems.size return allItems.size
} }
override fun getItem(position: Int): ArticleFragment { override fun getItem(position: Int): ArticleFragment {
return ArticleFragment.newInstance(position, allItems, useWebview) return ArticleFragment.newInstance(position, allItems)
} }
override fun startUpdate(container: ViewGroup) {
super.startUpdate(container)
container.background = ColorDrawable(ContextCompat.getColor(this@ReaderActivity, appColors.colorBackground))
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
val inflater = menuInflater
inflater.inflate(R.menu.reader_menu, menu)
toolbarMenu = menu
if (!allItems.isEmpty() && allItems[currentItem].starred) {
canRemoveFromFavorite()
} else {
canFavorite()
}
return true
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
@ -162,6 +209,54 @@ class ReaderActivity : AppCompatActivity() {
onBackPressed() onBackPressed()
return true return true
} }
R.id.save -> {
api.starrItem(allItems[pager.currentItem].id)
.enqueue(object : Callback<SuccessResponse> {
override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
allItems[pager.currentItem] = allItems[pager.currentItem].toggleStar()
notifyAdapter()
canRemoveFromFavorite()
}
override fun onFailure(
call: Call<SuccessResponse>,
t: Throwable
) {
Toast.makeText(
baseContext,
R.string.cant_mark_favortie,
Toast.LENGTH_SHORT
).show()
}
})
}
R.id.unsave -> {
api.unstarrItem(allItems[pager.currentItem].id)
.enqueue(object : Callback<SuccessResponse> {
override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
allItems[pager.currentItem] = allItems[pager.currentItem].toggleStar()
notifyAdapter()
canFavorite()
}
override fun onFailure(
call: Call<SuccessResponse>,
t: Throwable
) {
Toast.makeText(
baseContext,
R.string.cant_unmark_favortie,
Toast.LENGTH_SHORT
).show()
}
})
}
} }
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }

View File

@ -1,14 +1,18 @@
package apps.amine.bou.readerforselfoss package apps.amine.bou.readerforselfoss
import android.content.Intent import android.content.Intent
import android.content.res.ColorStateList
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.preference.PreferenceManager import android.preference.PreferenceManager
import android.support.v7.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import android.support.v7.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import android.widget.Toast import android.widget.Toast
import apps.amine.bou.readerforselfoss.adapters.SourcesListAdapter import apps.amine.bou.readerforselfoss.adapters.SourcesListAdapter
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
import apps.amine.bou.readerforselfoss.api.selfoss.Sources import apps.amine.bou.readerforselfoss.api.selfoss.Source
import apps.amine.bou.readerforselfoss.themes.AppColors
import apps.amine.bou.readerforselfoss.themes.Toppings
import com.ftinc.scoop.Scoop import com.ftinc.scoop.Scoop
import kotlinx.android.synthetic.main.activity_sources.* import kotlinx.android.synthetic.main.activity_sources.*
import retrofit2.Call import retrofit2.Call
@ -17,14 +21,27 @@ import retrofit2.Response
class SourcesActivity : AppCompatActivity() { class SourcesActivity : AppCompatActivity() {
private lateinit var appColors: AppColors
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
appColors = AppColors(this@SourcesActivity)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
Scoop.getInstance().apply(this)
setContentView(R.layout.activity_sources) setContentView(R.layout.activity_sources)
val scoop = Scoop.getInstance()
scoop.bind(this, Toppings.PRIMARY.value, toolbar)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
}
setSupportActionBar(toolbar) setSupportActionBar(toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true)
fab.rippleColor = appColors.colorAccentDark
fab.backgroundTintList = ColorStateList.valueOf(appColors.colorAccent)
} }
override fun onStop() { override fun onStop() {
@ -39,41 +56,41 @@ class SourcesActivity : AppCompatActivity() {
val prefs = PreferenceManager.getDefaultSharedPreferences(this) val prefs = PreferenceManager.getDefaultSharedPreferences(this)
val api = SelfossApi( val api = SelfossApi(
this, this,
this@SourcesActivity, this@SourcesActivity,
prefs.getBoolean("isSelfSignedCert", false), prefs.getBoolean("isSelfSignedCert", false),
prefs.getBoolean("should_log_everything", false) prefs.getBoolean("should_log_everything", false)
) )
var items: ArrayList<Sources> = ArrayList() var items: ArrayList<Source> = ArrayList()
recyclerView.setHasFixedSize(true) recyclerView.setHasFixedSize(true)
recyclerView.layoutManager = mLayoutManager recyclerView.layoutManager = mLayoutManager
api.sources.enqueue(object : Callback<List<Sources>> { api.sources.enqueue(object : Callback<List<Source>> {
override fun onResponse( override fun onResponse(
call: Call<List<Sources>>, call: Call<List<Source>>,
response: Response<List<Sources>> response: Response<List<Source>>
) { ) {
if (response.body() != null && response.body()!!.isNotEmpty()) { if (response.body() != null && response.body()!!.isNotEmpty()) {
items = response.body() as ArrayList<Sources> items = response.body() as ArrayList<Source>
} }
val mAdapter = SourcesListAdapter(this@SourcesActivity, items, api) val mAdapter = SourcesListAdapter(this@SourcesActivity, items, api)
recyclerView.adapter = mAdapter recyclerView.adapter = mAdapter
mAdapter.notifyDataSetChanged() mAdapter.notifyDataSetChanged()
if (items.isEmpty()) { if (items.isEmpty()) {
Toast.makeText( Toast.makeText(
this@SourcesActivity, this@SourcesActivity,
R.string.nothing_here, R.string.nothing_here,
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
} }
override fun onFailure(call: Call<List<Sources>>, t: Throwable) { override fun onFailure(call: Call<List<Source>>, t: Throwable) {
Toast.makeText( Toast.makeText(
this@SourcesActivity, this@SourcesActivity,
R.string.cant_get_sources, R.string.cant_get_sources,
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
}) })

View File

@ -2,16 +2,13 @@ package apps.amine.bou.readerforselfoss.adapters
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.graphics.Color import androidx.cardview.widget.CardView
import android.support.design.widget.Snackbar import androidx.recyclerview.widget.RecyclerView
import android.support.v7.widget.CardView
import android.support.v7.widget.RecyclerView
import android.text.Html import android.text.Html
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView.ScaleType import android.widget.ImageView.ScaleType
import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import apps.amine.bou.readerforselfoss.R import apps.amine.bou.readerforselfoss.R
import apps.amine.bou.readerforselfoss.api.selfoss.Item import apps.amine.bou.readerforselfoss.api.selfoss.Item
@ -26,12 +23,10 @@ import apps.amine.bou.readerforselfoss.utils.openInBrowserAsNewTask
import apps.amine.bou.readerforselfoss.utils.openItemUrl import apps.amine.bou.readerforselfoss.utils.openItemUrl
import apps.amine.bou.readerforselfoss.utils.shareLink import apps.amine.bou.readerforselfoss.utils.shareLink
import apps.amine.bou.readerforselfoss.utils.sourceAndDateText import apps.amine.bou.readerforselfoss.utils.sourceAndDateText
import apps.amine.bou.readerforselfoss.utils.succeeded
import apps.amine.bou.readerforselfoss.utils.toTextDrawableString import apps.amine.bou.readerforselfoss.utils.toTextDrawableString
import com.amulyakhare.textdrawable.TextDrawable import com.amulyakhare.textdrawable.TextDrawable
import com.amulyakhare.textdrawable.util.ColorGenerator import com.amulyakhare.textdrawable.util.ColorGenerator
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.crashlytics.android.Crashlytics
import com.like.LikeButton import com.like.LikeButton
import com.like.OnLikeListener import com.like.OnLikeListener
import kotlinx.android.synthetic.main.card_item.view.* import kotlinx.android.synthetic.main.card_item.view.*
@ -40,20 +35,22 @@ import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
class ItemCardAdapter( class ItemCardAdapter(
private val app: Activity, override val app: Activity,
private val items: ArrayList<Item>, override var items: ArrayList<Item>,
private val api: SelfossApi, override val api: SelfossApi,
private val helper: CustomTabActivityHelper, private val helper: CustomTabActivityHelper,
private val internalBrowser: Boolean, private val internalBrowser: Boolean,
private val articleViewer: Boolean, private val articleViewer: Boolean,
private val fullHeightCards: Boolean, private val fullHeightCards: Boolean,
private val appColors: AppColors, override val appColors: AppColors,
val debugReadingItems: Boolean, override val debugReadingItems: Boolean,
val userIdentifier: String override val userIdentifier: String,
) : RecyclerView.Adapter<ItemCardAdapter.ViewHolder>() { override val updateItems: (ArrayList<Item>) -> Unit
) : ItemsAdapter<ItemCardAdapter.ViewHolder>() {
private val c: Context = app.baseContext private val c: Context = app.baseContext
private val generator: ColorGenerator = ColorGenerator.MATERIAL private val generator: ColorGenerator = ColorGenerator.MATERIAL
private val imageMaxHeight: Int = c.resources.getDimension(R.dimen.card_image_max_height).toInt() private val imageMaxHeight: Int =
c.resources.getDimension(R.dimen.card_image_max_height).toInt()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(c).inflate(R.layout.card_item, parent, false) as CardView val v = LayoutInflater.from(c).inflate(R.layout.card_item, parent, false) as CardView
@ -67,6 +64,8 @@ class ItemCardAdapter(
holder.mView.favButton.isLiked = itm.starred holder.mView.favButton.isLiked = itm.starred
holder.mView.title.text = Html.fromHtml(itm.title) holder.mView.title.text = Html.fromHtml(itm.title)
holder.mView.title.setLinkTextColor(appColors.colorAccent)
holder.mView.sourceTitleAndDate.text = itm.sourceAndDateText() holder.mView.sourceTitleAndDate.text = itm.sourceAndDateText()
if (!fullHeightCards) { if (!fullHeightCards) {
@ -87,10 +86,10 @@ class ItemCardAdapter(
val color = generator.getColor(itm.sourcetitle) val color = generator.getColor(itm.sourcetitle)
val drawable = val drawable =
TextDrawable TextDrawable
.builder() .builder()
.round() .round()
.build(itm.sourcetitle.toTextDrawableString(), color) .build(itm.sourcetitle.toTextDrawableString(c), color)
holder.mView.sourceImage.setImageDrawable(drawable) holder.mView.sourceImage.setImageDrawable(drawable)
} else { } else {
c.circularBitmapDrawable(itm.getIcon(c), holder.mView.sourceImage) c.circularBitmapDrawable(itm.getIcon(c), holder.mView.sourceImage)
@ -103,89 +102,9 @@ class ItemCardAdapter(
return items.size return items.size
} }
private fun doUnmark(i: Item, position: Int) {
val s = Snackbar
.make(
app.findViewById(R.id.coordLayout),
R.string.marked_as_read,
Snackbar.LENGTH_LONG
)
.setAction(R.string.undo_string) {
items.add(position, i)
notifyItemInserted(position)
api.unmarkItem(i.id).enqueue(object : Callback<SuccessResponse> {
override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
}
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
items.remove(i)
notifyItemRemoved(position)
doUnmark(i, position)
}
})
}
val view = s.view
val tv: TextView = view.findViewById(android.support.design.R.id.snackbar_text)
tv.setTextColor(Color.WHITE)
s.show()
}
fun removeItemAtIndex(position: Int) {
val i = items[position]
items.remove(i)
notifyItemRemoved(position)
api.markItem(i.id).enqueue(object : Callback<SuccessResponse> {
override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
if (!response.succeeded() && debugReadingItems) {
val message =
"message: ${response.message()} " +
"response isSuccess: ${response.isSuccessful} " +
"response code: ${response.code()} " +
"response message: ${response.message()} " +
"response errorBody: ${response.errorBody()?.string()} " +
"body success: ${response.body()?.success} " +
"body isSuccess: ${response.body()?.isSuccess}"
Crashlytics.setUserIdentifier(userIdentifier)
Crashlytics.log(100, "READ_DEBUG_SUCCESS", message)
Crashlytics.logException(Exception("Was success, but did it work ?"))
Toast.makeText(c, message, Toast.LENGTH_LONG).show()
}
doUnmark(i, position)
}
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
if (debugReadingItems) {
Crashlytics.setUserIdentifier(userIdentifier)
Crashlytics.log(100, "READ_DEBUG_ERROR", t.message)
Crashlytics.logException(t)
Toast.makeText(c, t.message, Toast.LENGTH_LONG).show()
}
Toast.makeText(
app,
app.getString(R.string.cant_mark_read),
Toast.LENGTH_SHORT
).show()
items.add(i)
notifyItemInserted(position)
}
})
}
inner class ViewHolder(val mView: CardView) : RecyclerView.ViewHolder(mView) { inner class ViewHolder(val mView: CardView) : RecyclerView.ViewHolder(mView) {
init { init {
mView.setCardBackgroundColor(appColors.cardBackground) mView.setCardBackgroundColor(appColors.cardBackgroundColor)
handleClickListeners() handleClickListeners()
handleCustomTabActions() handleCustomTabActions()
} }
@ -197,20 +116,20 @@ class ItemCardAdapter(
val (id) = items[adapterPosition] val (id) = items[adapterPosition]
api.starrItem(id).enqueue(object : Callback<SuccessResponse> { api.starrItem(id).enqueue(object : Callback<SuccessResponse> {
override fun onResponse( override fun onResponse(
call: Call<SuccessResponse>, call: Call<SuccessResponse>,
response: Response<SuccessResponse> response: Response<SuccessResponse>
) { ) {
} }
override fun onFailure( override fun onFailure(
call: Call<SuccessResponse>, call: Call<SuccessResponse>,
t: Throwable t: Throwable
) { ) {
mView.favButton.isLiked = false mView.favButton.isLiked = false
Toast.makeText( Toast.makeText(
c, c,
R.string.cant_mark_favortie, R.string.cant_mark_favortie,
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
}) })
@ -220,20 +139,20 @@ class ItemCardAdapter(
val (id) = items[adapterPosition] val (id) = items[adapterPosition]
api.unstarrItem(id).enqueue(object : Callback<SuccessResponse> { api.unstarrItem(id).enqueue(object : Callback<SuccessResponse> {
override fun onResponse( override fun onResponse(
call: Call<SuccessResponse>, call: Call<SuccessResponse>,
response: Response<SuccessResponse> response: Response<SuccessResponse>
) { ) {
} }
override fun onFailure( override fun onFailure(
call: Call<SuccessResponse>, call: Call<SuccessResponse>,
t: Throwable t: Throwable
) { ) {
mView.favButton.isLiked = true mView.favButton.isLiked = true
Toast.makeText( Toast.makeText(
c, c,
R.string.cant_unmark_favortie, R.string.cant_unmark_favortie,
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
}) })
@ -255,13 +174,13 @@ class ItemCardAdapter(
mView.setOnClickListener { mView.setOnClickListener {
c.openItemUrl( c.openItemUrl(
items, items,
adapterPosition, adapterPosition,
items[adapterPosition].getLinkDecoded(), items[adapterPosition].getLinkDecoded(),
customTabsIntent, customTabsIntent,
internalBrowser, internalBrowser,
articleViewer, articleViewer,
app app
) )
} }
} }

View File

@ -2,21 +2,19 @@ package apps.amine.bou.readerforselfoss.adapters
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.graphics.Color import androidx.constraintlayout.widget.ConstraintLayout
import android.support.constraint.ConstraintLayout import androidx.recyclerview.widget.RecyclerView
import android.support.design.widget.Snackbar
import android.support.v7.widget.RecyclerView
import android.text.Html import android.text.Html
import android.util.TypedValue import android.util.TypedValue
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.TextView
import android.widget.Toast import android.widget.Toast
import apps.amine.bou.readerforselfoss.R import apps.amine.bou.readerforselfoss.R
import apps.amine.bou.readerforselfoss.api.selfoss.Item import apps.amine.bou.readerforselfoss.api.selfoss.Item
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
import apps.amine.bou.readerforselfoss.themes.AppColors
import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
import apps.amine.bou.readerforselfoss.utils.glide.bitmapCenterCrop import apps.amine.bou.readerforselfoss.utils.glide.bitmapCenterCrop
@ -25,10 +23,9 @@ import apps.amine.bou.readerforselfoss.utils.openInBrowserAsNewTask
import apps.amine.bou.readerforselfoss.utils.openItemUrl import apps.amine.bou.readerforselfoss.utils.openItemUrl
import apps.amine.bou.readerforselfoss.utils.shareLink import apps.amine.bou.readerforselfoss.utils.shareLink
import apps.amine.bou.readerforselfoss.utils.sourceAndDateText import apps.amine.bou.readerforselfoss.utils.sourceAndDateText
import apps.amine.bou.readerforselfoss.utils.succeeded import apps.amine.bou.readerforselfoss.utils.toTextDrawableString
import com.amulyakhare.textdrawable.TextDrawable import com.amulyakhare.textdrawable.TextDrawable
import com.amulyakhare.textdrawable.util.ColorGenerator import com.amulyakhare.textdrawable.util.ColorGenerator
import com.crashlytics.android.Crashlytics
import com.like.LikeButton import com.like.LikeButton
import com.like.OnLikeListener import com.like.OnLikeListener
import kotlinx.android.synthetic.main.list_item.view.* import kotlinx.android.synthetic.main.list_item.view.*
@ -39,25 +36,27 @@ import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
class ItemListAdapter( class ItemListAdapter(
private val app: Activity, override val app: Activity,
private val items: ArrayList<Item>, override var items: ArrayList<Item>,
private val api: SelfossApi, override val api: SelfossApi,
private val helper: CustomTabActivityHelper, private val helper: CustomTabActivityHelper,
private val clickBehavior: Boolean, private val clickBehavior: Boolean,
private val internalBrowser: Boolean, private val internalBrowser: Boolean,
private val articleViewer: Boolean, private val articleViewer: Boolean,
val debugReadingItems: Boolean, override val debugReadingItems: Boolean,
val userIdentifier: String override val userIdentifier: String,
) : RecyclerView.Adapter<ItemListAdapter.ViewHolder>() { override val appColors: AppColors,
override val updateItems: (ArrayList<Item>) -> Unit
) : ItemsAdapter<ItemListAdapter.ViewHolder>() {
private val generator: ColorGenerator = ColorGenerator.MATERIAL private val generator: ColorGenerator = ColorGenerator.MATERIAL
private val c: Context = app.baseContext private val c: Context = app.baseContext
private val bars: ArrayList<Boolean> = ArrayList(Collections.nCopies(items.size + 1, false)) private val bars: ArrayList<Boolean> = ArrayList(Collections.nCopies(items.size + 1, false))
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(c).inflate( val v = LayoutInflater.from(c).inflate(
R.layout.list_item, R.layout.list_item,
parent, parent,
false false
) as ConstraintLayout ) as ConstraintLayout
return ViewHolder(v) return ViewHolder(v)
} }
@ -66,21 +65,22 @@ class ItemListAdapter(
val itm = items[position] val itm = items[position]
holder.mView.favButton.isLiked = itm.starred
holder.mView.title.text = Html.fromHtml(itm.title) holder.mView.title.text = Html.fromHtml(itm.title)
holder.mView.title.setLinkTextColor(appColors.colorAccent)
holder.mView.sourceTitleAndDate.text = itm.sourceAndDateText() holder.mView.sourceTitleAndDate.text = itm.sourceAndDateText()
if (itm.getThumbnail(c).isEmpty()) { if (itm.getThumbnail(c).isEmpty()) {
val sizeInInt = 46 val sizeInInt = 46
val sizeInDp = TypedValue.applyDimension( val sizeInDp = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, sizeInInt.toFloat(), c.resources TypedValue.COMPLEX_UNIT_DIP, sizeInInt.toFloat(), c.resources
.displayMetrics .displayMetrics
).toInt() ).toInt()
val marginInInt = 16 val marginInInt = 16
val marginInDp = TypedValue.applyDimension( val marginInDp = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, marginInInt.toFloat(), c.resources TypedValue.COMPLEX_UNIT_DIP, marginInInt.toFloat(), c.resources
.displayMetrics .displayMetrics
).toInt() ).toInt()
@ -92,14 +92,13 @@ class ItemListAdapter(
if (itm.getIcon(c).isEmpty()) { if (itm.getIcon(c).isEmpty()) {
val color = generator.getColor(itm.sourcetitle) val color = generator.getColor(itm.sourcetitle)
val textDrawable = StringBuilder()
for (s in itm.sourcetitle.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) {
textDrawable.append(s[0])
}
val builder = TextDrawable.builder().round() val drawable =
TextDrawable
.builder()
.round()
.build(itm.sourcetitle.toTextDrawableString(c), color)
val drawable = builder.build(textDrawable.toString(), color)
holder.mView.itemImage.setImageDrawable(drawable) holder.mView.itemImage.setImageDrawable(drawable)
} else { } else {
c.circularBitmapDrawable(itm.getIcon(c), holder.mView.itemImage) c.circularBitmapDrawable(itm.getIcon(c), holder.mView.itemImage)
@ -108,9 +107,14 @@ class ItemListAdapter(
c.bitmapCenterCrop(itm.getThumbnail(c), holder.mView.itemImage) c.bitmapCenterCrop(itm.getThumbnail(c), holder.mView.itemImage)
} }
if (bars[position]) { // TODO: maybe handle this differently. It crashes when changing tab
holder.mView.actionBar.visibility = View.VISIBLE try {
} else { if (bars[position]) {
holder.mView.actionBar.visibility = View.VISIBLE
} else {
holder.mView.actionBar.visibility = View.GONE
}
} catch (e: IndexOutOfBoundsException) {
holder.mView.actionBar.visibility = View.GONE holder.mView.actionBar.visibility = View.GONE
} }
@ -119,85 +123,6 @@ class ItemListAdapter(
override fun getItemCount(): Int = items.size override fun getItemCount(): Int = items.size
private fun doUnmark(i: Item, position: Int) {
val s = Snackbar
.make(
app.findViewById(R.id.coordLayout),
R.string.marked_as_read,
Snackbar.LENGTH_LONG
)
.setAction(R.string.undo_string) {
items.add(position, i)
notifyItemInserted(position)
api.unmarkItem(i.id).enqueue(object : Callback<SuccessResponse> {
override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
}
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
items.remove(i)
notifyItemRemoved(position)
doUnmark(i, position)
}
})
}
val view = s.view
val tv: TextView = view.findViewById(android.support.design.R.id.snackbar_text)
tv.setTextColor(Color.WHITE)
s.show()
}
fun removeItemAtIndex(position: Int) {
val i = items[position]
items.remove(i)
notifyItemRemoved(position)
api.markItem(i.id).enqueue(object : Callback<SuccessResponse> {
override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
if (!response.succeeded() && debugReadingItems) {
val message =
"message: ${response.message()} " +
"response isSuccess: ${response.isSuccessful} " +
"response code: ${response.code()} " +
"response message: ${response.message()} " +
"response errorBody: ${response.errorBody()?.string()} " +
"body success: ${response.body()?.success} " +
"body isSuccess: ${response.body()?.isSuccess}"
Crashlytics.setUserIdentifier(userIdentifier)
Crashlytics.log(100, "READ_DEBUG_SUCCESS", message)
Crashlytics.logException(Exception("Was success, but did it work ?"))
Toast.makeText(c, message, Toast.LENGTH_LONG).show()
}
doUnmark(i, position)
}
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
if (debugReadingItems) {
Crashlytics.setUserIdentifier(userIdentifier)
Crashlytics.log(100, "READ_DEBUG_ERROR", t.message)
Crashlytics.logException(t)
Toast.makeText(c, t.message, Toast.LENGTH_LONG).show()
}
Toast.makeText(
app,
app.getString(R.string.cant_mark_read),
Toast.LENGTH_SHORT
).show()
items.add(i)
notifyItemInserted(position)
}
})
}
inner class ViewHolder(val mView: ConstraintLayout) : RecyclerView.ViewHolder(mView) { inner class ViewHolder(val mView: ConstraintLayout) : RecyclerView.ViewHolder(mView) {
init { init {
@ -212,20 +137,20 @@ class ItemListAdapter(
val (id) = items[adapterPosition] val (id) = items[adapterPosition]
api.starrItem(id).enqueue(object : Callback<SuccessResponse> { api.starrItem(id).enqueue(object : Callback<SuccessResponse> {
override fun onResponse( override fun onResponse(
call: Call<SuccessResponse>, call: Call<SuccessResponse>,
response: Response<SuccessResponse> response: Response<SuccessResponse>
) { ) {
} }
override fun onFailure( override fun onFailure(
call: Call<SuccessResponse>, call: Call<SuccessResponse>,
t: Throwable t: Throwable
) { ) {
mView.favButton.isLiked = false mView.favButton.isLiked = false
Toast.makeText( Toast.makeText(
c, c,
R.string.cant_mark_favortie, R.string.cant_mark_favortie,
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
}) })
@ -235,20 +160,20 @@ class ItemListAdapter(
val (id) = items[adapterPosition] val (id) = items[adapterPosition]
api.unstarrItem(id).enqueue(object : Callback<SuccessResponse> { api.unstarrItem(id).enqueue(object : Callback<SuccessResponse> {
override fun onResponse( override fun onResponse(
call: Call<SuccessResponse>, call: Call<SuccessResponse>,
response: Response<SuccessResponse> response: Response<SuccessResponse>
) { ) {
} }
override fun onFailure( override fun onFailure(
call: Call<SuccessResponse>, call: Call<SuccessResponse>,
t: Throwable t: Throwable
) { ) {
mView.favButton.isLiked = true mView.favButton.isLiked = true
Toast.makeText( Toast.makeText(
c, c,
R.string.cant_unmark_favortie, R.string.cant_unmark_favortie,
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
}) })
@ -273,13 +198,13 @@ class ItemListAdapter(
if (!clickBehavior) { if (!clickBehavior) {
mView.setOnClickListener { mView.setOnClickListener {
c.openItemUrl( c.openItemUrl(
items, items,
adapterPosition, adapterPosition,
items[adapterPosition].getLinkDecoded(), items[adapterPosition].getLinkDecoded(),
customTabsIntent, customTabsIntent,
internalBrowser, internalBrowser,
articleViewer, articleViewer,
app app
) )
} }
mView.setOnLongClickListener { mView.setOnLongClickListener {
@ -290,13 +215,13 @@ class ItemListAdapter(
mView.setOnClickListener { actionBarShowHide() } mView.setOnClickListener { actionBarShowHide() }
mView.setOnLongClickListener { mView.setOnLongClickListener {
c.openItemUrl( c.openItemUrl(
items, items,
adapterPosition, adapterPosition,
items[adapterPosition].getLinkDecoded(), items[adapterPosition].getLinkDecoded(),
customTabsIntent, customTabsIntent,
internalBrowser, internalBrowser,
articleViewer, articleViewer,
app app
) )
true true
} }

View File

@ -0,0 +1,131 @@
package apps.amine.bou.readerforselfoss.adapters
import android.app.Activity
import android.graphics.Color
import com.google.android.material.snackbar.Snackbar
import androidx.recyclerview.widget.RecyclerView
import android.widget.TextView
import android.widget.Toast
import apps.amine.bou.readerforselfoss.R
import apps.amine.bou.readerforselfoss.api.selfoss.Item
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
import apps.amine.bou.readerforselfoss.themes.AppColors
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
import apps.amine.bou.readerforselfoss.utils.succeeded
import org.acra.ACRA
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapter<VH>() {
abstract var items: ArrayList<Item>
abstract val api: SelfossApi
abstract val debugReadingItems: Boolean
abstract val userIdentifier: String
abstract val app: Activity
abstract val appColors: AppColors
abstract val updateItems: (ArrayList<Item>) -> Unit
fun updateAllItems(newItems: ArrayList<Item>) {
items = newItems
notifyDataSetChanged()
updateItems(items)
}
private fun doUnmark(i: Item, position: Int) {
val s = Snackbar
.make(
app.findViewById(R.id.coordLayout),
R.string.marked_as_read,
Snackbar.LENGTH_LONG
)
.setAction(R.string.undo_string) {
items.add(position, i)
notifyItemInserted(position)
updateItems(items)
api.unmarkItem(i.id).enqueue(object : Callback<SuccessResponse> {
override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
}
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
items.remove(i)
notifyItemRemoved(position)
updateItems(items)
doUnmark(i, position)
}
})
}
val view = s.view
val tv: TextView = view.findViewById(com.google.android.material.R.id.snackbar_text)
tv.setTextColor(Color.WHITE)
s.show()
}
fun removeItemAtIndex(position: Int) {
val i = items[position]
items.remove(i)
notifyItemRemoved(position)
updateItems(items)
api.markItem(i.id).enqueue(object : Callback<SuccessResponse> {
override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
if (!response.succeeded() && debugReadingItems) {
val message =
"message: ${response.message()} " +
"response isSuccess: ${response.isSuccessful} " +
"response code: ${response.code()} " +
"response message: ${response.message()} " +
"response errorBody: ${response.errorBody()?.string()} " +
"body success: ${response.body()?.success} " +
"body isSuccess: ${response.body()?.isSuccess}"
ACRA.getErrorReporter().maybeHandleSilentException(Exception(message), app)
Toast.makeText(app.baseContext, message, Toast.LENGTH_LONG).show()
}
doUnmark(i, position)
}
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
if (debugReadingItems) {
ACRA.getErrorReporter().maybeHandleSilentException(t, app)
Toast.makeText(app.baseContext, t.message, Toast.LENGTH_LONG).show()
}
Toast.makeText(
app,
app.getString(R.string.cant_mark_read),
Toast.LENGTH_SHORT
).show()
items.add(i)
notifyItemInserted(position)
updateItems(items)
}
})
}
fun addItemAtIndex(item: Item, position: Int) {
items.add(position, item)
notifyItemInserted(position)
updateItems(items)
}
fun addItemsAtEnd(newItems: List<Item>) {
val oldSize = items.size
items.addAll(newItems)
notifyItemRangeInserted(oldSize, newItems.size)
updateItems(items)
}
}

View File

@ -2,15 +2,15 @@ package apps.amine.bou.readerforselfoss.adapters
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.support.constraint.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Button import android.widget.Button
import android.widget.Toast import android.widget.Toast
import apps.amine.bou.readerforselfoss.R import apps.amine.bou.readerforselfoss.R
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
import apps.amine.bou.readerforselfoss.api.selfoss.Sources import apps.amine.bou.readerforselfoss.api.selfoss.Source
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
import apps.amine.bou.readerforselfoss.utils.glide.circularBitmapDrawable import apps.amine.bou.readerforselfoss.utils.glide.circularBitmapDrawable
import apps.amine.bou.readerforselfoss.utils.toTextDrawableString import apps.amine.bou.readerforselfoss.utils.toTextDrawableString
@ -22,18 +22,18 @@ import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
class SourcesListAdapter( class SourcesListAdapter(
private val app: Activity, private val app: Activity,
private val items: ArrayList<Sources>, private val items: ArrayList<Source>,
private val api: SelfossApi private val api: SelfossApi
) : RecyclerView.Adapter<SourcesListAdapter.ViewHolder>() { ) : RecyclerView.Adapter<SourcesListAdapter.ViewHolder>() {
private val c: Context = app.baseContext private val c: Context = app.baseContext
private val generator: ColorGenerator = ColorGenerator.MATERIAL private val generator: ColorGenerator = ColorGenerator.MATERIAL
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val v = LayoutInflater.from(c).inflate( val v = LayoutInflater.from(c).inflate(
R.layout.source_list_item, R.layout.source_list_item,
parent, parent,
false false
) as ConstraintLayout ) as ConstraintLayout
return ViewHolder(v) return ViewHolder(v)
} }
@ -45,10 +45,10 @@ class SourcesListAdapter(
val color = generator.getColor(itm.title) val color = generator.getColor(itm.title)
val drawable = val drawable =
TextDrawable TextDrawable
.builder() .builder()
.round() .round()
.build(itm.title.toTextDrawableString(), color) .build(itm.title.toTextDrawableString(c), color)
holder.mView.itemImage.setImageDrawable(drawable) holder.mView.itemImage.setImageDrawable(drawable)
} else { } else {
c.circularBitmapDrawable(itm.getIcon(c), holder.mView.itemImage) c.circularBitmapDrawable(itm.getIcon(c), holder.mView.itemImage)
@ -73,8 +73,8 @@ class SourcesListAdapter(
val (id) = items[adapterPosition] val (id) = items[adapterPosition]
api.deleteSource(id).enqueue(object : Callback<SuccessResponse> { api.deleteSource(id).enqueue(object : Callback<SuccessResponse> {
override fun onResponse( override fun onResponse(
call: Call<SuccessResponse>, call: Call<SuccessResponse>,
response: Response<SuccessResponse> response: Response<SuccessResponse>
) { ) {
if (response.body() != null && response.body()!!.isSuccess) { if (response.body() != null && response.body()!!.isSuccess) {
items.removeAt(adapterPosition) items.removeAt(adapterPosition)
@ -82,18 +82,18 @@ class SourcesListAdapter(
notifyItemRangeChanged(adapterPosition, itemCount) notifyItemRangeChanged(adapterPosition, itemCount)
} else { } else {
Toast.makeText( Toast.makeText(
app, app,
R.string.can_delete_source, R.string.can_delete_source,
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
} }
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) { override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
Toast.makeText( Toast.makeText(
app, app,
R.string.can_delete_source, R.string.can_delete_source,
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
).show() ).show()
} }
}) })

View File

@ -7,7 +7,7 @@ import retrofit2.Call
import retrofit2.Retrofit import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory import retrofit2.converter.gson.GsonConverterFactory
class MercuryApi(private val key: String, shouldLog: Boolean) { class MercuryApi(shouldLog: Boolean) {
private val service: MercuryService private val service: MercuryService
init { init {
@ -21,19 +21,19 @@ class MercuryApi(private val key: String, shouldLog: Boolean) {
val client = OkHttpClient.Builder().addInterceptor(interceptor).build() val client = OkHttpClient.Builder().addInterceptor(interceptor).build()
val gson = GsonBuilder() val gson = GsonBuilder()
.setLenient() .setLenient()
.create() .create()
val retrofit = val retrofit =
Retrofit Retrofit
.Builder() .Builder()
.baseUrl("https://mercury.postlight.com") .baseUrl("https://www.amine-bou.fr")
.client(client) .client(client)
.addConverterFactory(GsonConverterFactory.create(gson)) .addConverterFactory(GsonConverterFactory.create(gson))
.build() .build()
service = retrofit.create(MercuryService::class.java) service = retrofit.create(MercuryService::class.java)
} }
fun parseUrl(url: String): Call<ParsedContent> { fun parseUrl(url: String): Call<ParsedContent> {
return service.parseUrl(url, this.key) return service.parseUrl(url)
} }
} }

View File

@ -5,39 +5,40 @@ import android.os.Parcelable
import com.google.gson.annotations.SerializedName import com.google.gson.annotations.SerializedName
class ParsedContent( class ParsedContent(
@SerializedName("title") val title: String, @SerializedName("title") val title: String,
@SerializedName("content") val content: String, @SerializedName("content") val content: String?,
@SerializedName("date_published") val date_published: String, @SerializedName("date_published") val date_published: String,
@SerializedName("lead_image_url") val lead_image_url: String, @SerializedName("lead_image_url") val lead_image_url: String?,
@SerializedName("dek") val dek: String, @SerializedName("dek") val dek: String,
@SerializedName("url") val url: String, @SerializedName("url") val url: String,
@SerializedName("domain") val domain: String, @SerializedName("domain") val domain: String,
@SerializedName("excerpt") val excerpt: String, @SerializedName("excerpt") val excerpt: String,
@SerializedName("total_pages") val total_pages: Int, @SerializedName("total_pages") val total_pages: Int,
@SerializedName("rendered_pages") val rendered_pages: Int, @SerializedName("rendered_pages") val rendered_pages: Int,
@SerializedName("next_page_url") val next_page_url: String @SerializedName("next_page_url") val next_page_url: String
) : Parcelable { ) : Parcelable {
companion object { companion object {
@JvmField @JvmField
val CREATOR: Parcelable.Creator<ParsedContent> = object : Parcelable.Creator<ParsedContent> { val CREATOR: Parcelable.Creator<ParsedContent> =
override fun createFromParcel(source: Parcel): ParsedContent = ParsedContent(source) object : Parcelable.Creator<ParsedContent> {
override fun newArray(size: Int): Array<ParsedContent?> = arrayOfNulls(size) override fun createFromParcel(source: Parcel): ParsedContent = ParsedContent(source)
} override fun newArray(size: Int): Array<ParsedContent?> = arrayOfNulls(size)
}
} }
constructor(source: Parcel) : this( constructor(source: Parcel) : this(
title = source.readString(), title = source.readString(),
content = source.readString(), content = source.readString(),
date_published = source.readString(), date_published = source.readString(),
lead_image_url = source.readString(), lead_image_url = source.readString(),
dek = source.readString(), dek = source.readString(),
url = source.readString(), url = source.readString(),
domain = source.readString(), domain = source.readString(),
excerpt = source.readString(), excerpt = source.readString(),
total_pages = source.readInt(), total_pages = source.readInt(),
rendered_pages = source.readInt(), rendered_pages = source.readInt(),
next_page_url = source.readString() next_page_url = source.readString()
) )
override fun describeContents() = 0 override fun describeContents() = 0

View File

@ -6,6 +6,6 @@ import retrofit2.http.Header
import retrofit2.http.Query import retrofit2.http.Query
interface MercuryService { interface MercuryService {
@GET("parser") @GET("parser.php")
fun parseUrl(@Query("url") url: String, @Header("x-api-key") key: String): Call<ParsedContent> fun parseUrl(@Query("link") link: String): Call<ParsedContent>
} }

View File

@ -10,13 +10,13 @@ internal class BooleanTypeAdapter : JsonDeserializer<Boolean> {
@Throws(JsonParseException::class) @Throws(JsonParseException::class)
override fun deserialize( override fun deserialize(
json: JsonElement, json: JsonElement,
typeOfT: Type, typeOfT: Type,
context: JsonDeserializationContext context: JsonDeserializationContext
): Boolean? = ): Boolean? =
try { try {
json.asInt == 1 json.asInt == 1
} catch (e: Exception) { } catch (e: Exception) {
json.asBoolean json.asBoolean
} }
} }

View File

@ -20,10 +20,10 @@ import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
class SelfossApi( class SelfossApi(
c: Context, c: Context,
callingActivity: Activity, callingActivity: Activity,
isWithSelfSignedCert: Boolean, isWithSelfSignedCert: Boolean,
shouldLog: Boolean shouldLog: Boolean
) { ) {
private lateinit var service: SelfossService private lateinit var service: SelfossService
@ -32,25 +32,25 @@ class SelfossApi(
private val password: String private val password: String
fun OkHttpClient.Builder.maybeWithSelfSigned(isWithSelfSignedCert: Boolean): OkHttpClient.Builder = fun OkHttpClient.Builder.maybeWithSelfSigned(isWithSelfSignedCert: Boolean): OkHttpClient.Builder =
if (isWithSelfSignedCert) { if (isWithSelfSignedCert) {
getUnsafeHttpClient() getUnsafeHttpClient()
} else { } else {
this this
} }
fun Credentials.createAuthenticator(): DispatchingAuthenticator = fun Credentials.createAuthenticator(): DispatchingAuthenticator =
DispatchingAuthenticator.Builder() DispatchingAuthenticator.Builder()
.with("digest", DigestAuthenticator(this)) .with("digest", DigestAuthenticator(this))
.with("basic", BasicAuthenticator(this)) .with("basic", BasicAuthenticator(this))
.build() .build()
fun DispatchingAuthenticator.getHttpClien(isWithSelfSignedCert: Boolean): OkHttpClient.Builder { fun DispatchingAuthenticator.getHttpClien(isWithSelfSignedCert: Boolean): OkHttpClient.Builder {
val authCache = ConcurrentHashMap<String, CachingAuthenticator>() val authCache = ConcurrentHashMap<String, CachingAuthenticator>()
return OkHttpClient return OkHttpClient
.Builder() .Builder()
.maybeWithSelfSigned(isWithSelfSignedCert) .maybeWithSelfSigned(isWithSelfSignedCert)
.authenticator(CachingAuthenticatorDecorator(this, authCache)) .authenticator(CachingAuthenticatorDecorator(this, authCache))
.addInterceptor(AuthenticationCacheInterceptor(authCache)) .addInterceptor(AuthenticationCacheInterceptor(authCache))
} }
init { init {
@ -58,16 +58,16 @@ class SelfossApi(
password = config.userPassword password = config.userPassword
val authenticator = val authenticator =
Credentials( Credentials(
config.httpUserLogin, config.httpUserLogin,
config.httpUserPassword config.httpUserPassword
).createAuthenticator() ).createAuthenticator()
val gson = val gson =
GsonBuilder() GsonBuilder()
.registerTypeAdapter(Boolean::class.javaPrimitiveType, BooleanTypeAdapter()) .registerTypeAdapter(Boolean::class.javaPrimitiveType, BooleanTypeAdapter())
.setLenient() .setLenient()
.create() .create()
val logging = HttpLoggingInterceptor() val logging = HttpLoggingInterceptor()
@ -83,12 +83,12 @@ class SelfossApi(
try { try {
val retrofit = val retrofit =
Retrofit Retrofit
.Builder() .Builder()
.baseUrl(config.baseUrl) .baseUrl(config.baseUrl)
.client(httpClient.build()) .client(httpClient.build())
.addConverterFactory(GsonConverterFactory.create(gson)) .addConverterFactory(GsonConverterFactory.create(gson))
.build() .build()
service = retrofit.create(SelfossService::class.java) service = retrofit.create(SelfossService::class.java)
} catch (e: IllegalArgumentException) { } catch (e: IllegalArgumentException) {
Config.logoutAndRedirect(c, callingActivity, config.settings.edit(), baseUrlFail = true) Config.logoutAndRedirect(c, callingActivity, config.settings.edit(), baseUrlFail = true)
@ -96,59 +96,59 @@ class SelfossApi(
} }
fun login(): Call<SuccessResponse> = fun login(): Call<SuccessResponse> =
service.loginToSelfoss(config.userLogin, config.userPassword) service.loginToSelfoss(config.userLogin, config.userPassword)
fun readItems( fun readItems(
tag: String?, tag: String?,
sourceId: Long?, sourceId: Long?,
search: String?, search: String?,
itemsNumber: Int, itemsNumber: Int,
offset: Int offset: Int
): Call<List<Item>> = ): Call<List<Item>> =
getItems("read", tag, sourceId, search, itemsNumber, offset) getItems("read", tag, sourceId, search, itemsNumber, offset)
fun newItems( fun newItems(
tag: String?, tag: String?,
sourceId: Long?, sourceId: Long?,
search: String?, search: String?,
itemsNumber: Int, itemsNumber: Int,
offset: Int offset: Int
): Call<List<Item>> = ): Call<List<Item>> =
getItems("unread", tag, sourceId, search, itemsNumber, offset) getItems("unread", tag, sourceId, search, itemsNumber, offset)
fun starredItems( fun starredItems(
tag: String?, tag: String?,
sourceId: Long?, sourceId: Long?,
search: String?, search: String?,
itemsNumber: Int, itemsNumber: Int,
offset: Int offset: Int
): Call<List<Item>> = ): Call<List<Item>> =
getItems("starred", tag, sourceId, search, itemsNumber, offset) getItems("starred", tag, sourceId, search, itemsNumber, offset)
private fun getItems( private fun getItems(
type: String, type: String,
tag: String?, tag: String?,
sourceId: Long?, sourceId: Long?,
search: String?, search: String?,
items: Int, items: Int,
offset: Int offset: Int
): Call<List<Item>> = ): Call<List<Item>> =
service.getItems(type, tag, sourceId, search, userName, password, items, offset) service.getItems(type, tag, sourceId, search, userName, password, items, offset)
fun markItem(itemId: String): Call<SuccessResponse> = fun markItem(itemId: String): Call<SuccessResponse> =
service.markAsRead(itemId, userName, password) service.markAsRead(itemId, userName, password)
fun unmarkItem(itemId: String): Call<SuccessResponse> = fun unmarkItem(itemId: String): Call<SuccessResponse> =
service.unmarkAsRead(itemId, userName, password) service.unmarkAsRead(itemId, userName, password)
fun readAll(ids: List<String>): Call<SuccessResponse> = fun readAll(ids: List<String>): Call<SuccessResponse> =
service.markAllAsRead(ids, userName, password) service.markAllAsRead(ids, userName, password)
fun starrItem(itemId: String): Call<SuccessResponse> = fun starrItem(itemId: String): Call<SuccessResponse> =
service.starr(itemId, userName, password) service.starr(itemId, userName, password)
fun unstarrItem(itemId: String): Call<SuccessResponse> = fun unstarrItem(itemId: String): Call<SuccessResponse> =
service.unstarr(itemId, userName, password) service.unstarr(itemId, userName, password)
val stats: Call<Stats> val stats: Call<Stats>
get() = service.stats(userName, password) get() = service.stats(userName, password)
@ -157,23 +157,23 @@ class SelfossApi(
get() = service.tags(userName, password) get() = service.tags(userName, password)
fun update(): Call<String> = fun update(): Call<String> =
service.update(userName, password) service.update(userName, password)
val sources: Call<List<Sources>> val sources: Call<List<Source>>
get() = service.sources(userName, password) get() = service.sources(userName, password)
fun deleteSource(id: String): Call<SuccessResponse> = fun deleteSource(id: String): Call<SuccessResponse> =
service.deleteSource(id, userName, password) service.deleteSource(id, userName, password)
fun spouts(): Call<Map<String, Spout>> = fun spouts(): Call<Map<String, Spout>> =
service.spouts(userName, password) service.spouts(userName, password)
fun createSource( fun createSource(
title: String, title: String,
url: String, url: String,
spout: String, spout: String,
tags: String, tags: String,
filter: String filter: String
): Call<SuccessResponse> = ): Call<SuccessResponse> =
service.createSource(title, url, spout, tags, filter, userName, password) service.createSource(title, url, spout, tags, filter, userName, password)
} }

View File

@ -21,9 +21,9 @@ private fun constructUrl(config: Config?, path: String, file: String): String {
} }
data class Tag( data class Tag(
@SerializedName("tag") val tag: String, @SerializedName("tag") val tag: String,
@SerializedName("color") val color: String, @SerializedName("color") val color: String,
@SerializedName("unread") val unread: Int @SerializedName("unread") val unread: Int
) )
class SuccessResponse(@SerializedName("success") val success: Boolean) { class SuccessResponse(@SerializedName("success") val success: Boolean) {
@ -32,23 +32,23 @@ class SuccessResponse(@SerializedName("success") val success: Boolean) {
} }
class Stats( class Stats(
@SerializedName("total") val total: Int, @SerializedName("total") val total: Int,
@SerializedName("unread") val unread: Int, @SerializedName("unread") val unread: Int,
@SerializedName("starred") val starred: Int @SerializedName("starred") val starred: Int
) )
data class Spout( data class Spout(
@SerializedName("name") val name: String, @SerializedName("name") val name: String,
@SerializedName("description") val description: String @SerializedName("description") val description: String
) )
data class Sources( data class Source(
@SerializedName("id") val id: String, @SerializedName("id") val id: String,
@SerializedName("title") val title: String, @SerializedName("title") val title: String,
@SerializedName("tags") val tags: String, @SerializedName("tags") val tags: String,
@SerializedName("spout") val spout: String, @SerializedName("spout") val spout: String,
@SerializedName("error") val error: String, @SerializedName("error") val error: String,
@SerializedName("icon") val icon: String @SerializedName("icon") val icon: String
) { ) {
var config: Config? = null var config: Config? = null
@ -61,16 +61,17 @@ data class Sources(
} }
data class Item( data class Item(
@SerializedName("id") val id: String, @SerializedName("id") val id: String,
@SerializedName("datetime") val datetime: String, @SerializedName("datetime") val datetime: String,
@SerializedName("title") val title: String, @SerializedName("title") val title: String,
@SerializedName("content") val content: String, @SerializedName("content") val content: String,
@SerializedName("unread") val unread: Boolean, @SerializedName("unread") val unread: Boolean,
@SerializedName("starred") val starred: Boolean, @SerializedName("starred") var starred: Boolean,
@SerializedName("thumbnail") val thumbnail: String, @SerializedName("thumbnail") val thumbnail: String,
@SerializedName("icon") val icon: String, @SerializedName("icon") val icon: String,
@SerializedName("link") val link: String, @SerializedName("link") val link: String,
@SerializedName("sourcetitle") val sourcetitle: String @SerializedName("sourcetitle") val sourcetitle: String,
@SerializedName("tags") val tags: String
) : Parcelable { ) : Parcelable {
var config: Config? = null var config: Config? = null
@ -83,16 +84,17 @@ data class Item(
} }
constructor(source: Parcel) : this( constructor(source: Parcel) : this(
id = source.readString(), id = source.readString(),
datetime = source.readString(), datetime = source.readString(),
title = source.readString(), title = source.readString(),
content = source.readString(), content = source.readString(),
unread = 0.toByte() != source.readByte(), unread = 0.toByte() != source.readByte(),
starred = 0.toByte() != source.readByte(), starred = 0.toByte() != source.readByte(),
thumbnail = source.readString(), thumbnail = source.readString(),
icon = source.readString(), icon = source.readString(),
link = source.readString(), link = source.readString(),
sourcetitle = source.readString() sourcetitle = source.readString(),
tags = source.readString()
) )
override fun describeContents() = 0 override fun describeContents() = 0
@ -108,6 +110,7 @@ data class Item(
dest.writeString(icon) dest.writeString(icon)
dest.writeString(link) dest.writeString(link)
dest.writeString(sourcetitle) dest.writeString(sourcetitle)
dest.writeString(tags)
} }
fun getIcon(app: Context): String { fun getIcon(app: Context): String {
@ -127,15 +130,16 @@ data class Item(
// TODO: maybe find a better way to handle these kind of urls // TODO: maybe find a better way to handle these kind of urls
fun getLinkDecoded(): String { fun getLinkDecoded(): String {
var stringUrl: String var stringUrl: String
stringUrl = if (link.startsWith("http://news.google.com/news/") || link.startsWith("https://news.google.com/news/")) { stringUrl =
if (link.contains("&amp;url=")) { if (link.startsWith("http://news.google.com/news/") || link.startsWith("https://news.google.com/news/")) {
link.substringAfter("&amp;url=") if (link.contains("&amp;url=")) {
} else { link.substringAfter("&amp;url=")
this.link.replace("&amp;", "&") } else {
} this.link.replace("&amp;", "&")
} else { }
this.link.replace("&amp;", "&") } else {
} this.link.replace("&amp;", "&")
}
// handle :443 => https // handle :443 => https
if (stringUrl.contains(":443")) { if (stringUrl.contains(":443")) {
@ -144,7 +148,7 @@ data class Item(
// handle url not starting with http // handle url not starting with http
if (stringUrl.startsWith("//")) { if (stringUrl.startsWith("//")) {
stringUrl = "http:" + stringUrl stringUrl = "http:$stringUrl"
} }
return stringUrl return stringUrl

View File

@ -17,102 +17,102 @@ internal interface SelfossService {
@GET("items") @GET("items")
fun getItems( fun getItems(
@Query("type") type: String, @Query("type") type: String,
@Query("tag") tag: String?, @Query("tag") tag: String?,
@Query("source") source: Long?, @Query("source") source: Long?,
@Query("search") search: String?, @Query("search") search: String?,
@Query("username") username: String, @Query("username") username: String,
@Query("password") password: String, @Query("password") password: String,
@Query("items") items: Int, @Query("items") items: Int,
@Query("offset") offset: Int @Query("offset") offset: Int
): Call<List<Item>> ): Call<List<Item>>
@Headers("Content-Type: application/x-www-form-urlencoded") @Headers("Content-Type: application/x-www-form-urlencoded")
@POST("mark/{id}") @POST("mark/{id}")
fun markAsRead( fun markAsRead(
@Path("id") id: String, @Path("id") id: String,
@Query("username") username: String, @Query("username") username: String,
@Query("password") password: String @Query("password") password: String
): Call<SuccessResponse> ): Call<SuccessResponse>
@Headers("Content-Type: application/x-www-form-urlencoded") @Headers("Content-Type: application/x-www-form-urlencoded")
@POST("unmark/{id}") @POST("unmark/{id}")
fun unmarkAsRead( fun unmarkAsRead(
@Path("id") id: String, @Path("id") id: String,
@Query("username") username: String, @Query("username") username: String,
@Query("password") password: String @Query("password") password: String
): Call<SuccessResponse> ): Call<SuccessResponse>
@FormUrlEncoded @FormUrlEncoded
@POST("mark") @POST("mark")
fun markAllAsRead( fun markAllAsRead(
@Field("ids[]") ids: List<String>, @Field("ids[]") ids: List<String>,
@Query("username") username: String, @Query("username") username: String,
@Query("password") password: String @Query("password") password: String
): Call<SuccessResponse> ): Call<SuccessResponse>
@Headers("Content-Type: application/x-www-form-urlencoded") @Headers("Content-Type: application/x-www-form-urlencoded")
@POST("starr/{id}") @POST("starr/{id}")
fun starr( fun starr(
@Path("id") id: String, @Path("id") id: String,
@Query("username") username: String, @Query("username") username: String,
@Query("password") password: String @Query("password") password: String
): Call<SuccessResponse> ): Call<SuccessResponse>
@Headers("Content-Type: application/x-www-form-urlencoded") @Headers("Content-Type: application/x-www-form-urlencoded")
@POST("unstarr/{id}") @POST("unstarr/{id}")
fun unstarr( fun unstarr(
@Path("id") id: String, @Path("id") id: String,
@Query("username") username: String, @Query("username") username: String,
@Query("password") password: String @Query("password") password: String
): Call<SuccessResponse> ): Call<SuccessResponse>
@GET("stats") @GET("stats")
fun stats( fun stats(
@Query("username") username: String, @Query("username") username: String,
@Query("password") password: String @Query("password") password: String
): Call<Stats> ): Call<Stats>
@GET("tags") @GET("tags")
fun tags( fun tags(
@Query("username") username: String, @Query("username") username: String,
@Query("password") password: String @Query("password") password: String
): Call<List<Tag>> ): Call<List<Tag>>
@GET("update") @GET("update")
fun update( fun update(
@Query("username") username: String, @Query("username") username: String,
@Query("password") password: String @Query("password") password: String
): Call<String> ): Call<String>
@GET("sources/spouts") @GET("sources/spouts")
fun spouts( fun spouts(
@Query("username") username: String, @Query("username") username: String,
@Query("password") password: String @Query("password") password: String
): Call<Map<String, Spout>> ): Call<Map<String, Spout>>
@GET("sources/list") @GET("sources/list")
fun sources( fun sources(
@Query("username") username: String, @Query("username") username: String,
@Query("password") password: String @Query("password") password: String
): Call<List<Sources>> ): Call<List<Source>>
@DELETE("source/{id}") @DELETE("source/{id}")
fun deleteSource( fun deleteSource(
@Path("id") id: String, @Path("id") id: String,
@Query("username") username: String, @Query("username") username: String,
@Query("password") password: String @Query("password") password: String
): Call<SuccessResponse> ): Call<SuccessResponse>
@FormUrlEncoded @FormUrlEncoded
@POST("source") @POST("source")
fun createSource( fun createSource(
@Field("title") title: String, @Field("title") title: String,
@Field("url") url: String, @Field("url") url: String,
@Field("spout") spout: String, @Field("spout") spout: String,
@Field("tags") tags: String, @Field("tags") tags: String,
@Field("filter") filter: String, @Field("filter") filter: String,
@Query("username") username: String, @Query("username") username: String,
@Query("password") password: String @Query("password") password: String
): Call<SuccessResponse> ): Call<SuccessResponse>
} }

View File

@ -1,44 +1,53 @@
package apps.amine.bou.readerforselfoss.fragments package apps.amine.bou.readerforselfoss.fragments
import android.content.Context
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.res.ColorStateList
import android.graphics.drawable.ColorDrawable
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.preference.PreferenceManager import android.preference.PreferenceManager
import android.support.customtabs.CustomTabsIntent import androidx.browser.customtabs.CustomTabsIntent
import android.support.design.widget.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import android.support.v4.app.Fragment import androidx.fragment.app.Fragment
import android.support.v4.widget.NestedScrollView import androidx.core.content.ContextCompat
import android.text.Html import androidx.core.widget.NestedScrollView
import android.text.method.LinkMovementMethod
import android.util.TypedValue
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MenuItem import android.view.MenuItem
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.webkit.WebSettings import android.webkit.WebSettings
import apps.amine.bou.readerforselfoss.BuildConfig
import apps.amine.bou.readerforselfoss.R import apps.amine.bou.readerforselfoss.R
import apps.amine.bou.readerforselfoss.api.mercury.MercuryApi import apps.amine.bou.readerforselfoss.api.mercury.MercuryApi
import apps.amine.bou.readerforselfoss.api.mercury.ParsedContent import apps.amine.bou.readerforselfoss.api.mercury.ParsedContent
import apps.amine.bou.readerforselfoss.api.selfoss.Item import apps.amine.bou.readerforselfoss.api.selfoss.Item
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
import apps.amine.bou.readerforselfoss.themes.AppColors
import apps.amine.bou.readerforselfoss.utils.Config
import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
import apps.amine.bou.readerforselfoss.utils.isEmptyOrNullOrNullString import apps.amine.bou.readerforselfoss.utils.isEmptyOrNullOrNullString
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
import apps.amine.bou.readerforselfoss.utils.openItemUrl import apps.amine.bou.readerforselfoss.utils.openItemUrl
import apps.amine.bou.readerforselfoss.utils.shareLink import apps.amine.bou.readerforselfoss.utils.shareLink
import apps.amine.bou.readerforselfoss.utils.sourceAndDateText import apps.amine.bou.readerforselfoss.utils.sourceAndDateText
import apps.amine.bou.readerforselfoss.utils.succeeded
import apps.amine.bou.readerforselfoss.utils.toPx
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
import com.crashlytics.android.Crashlytics
import com.github.rubensousa.floatingtoolbar.FloatingToolbar import com.github.rubensousa.floatingtoolbar.FloatingToolbar
import kotlinx.android.synthetic.main.fragment_article.view.* import kotlinx.android.synthetic.main.fragment_article.view.*
import org.sufficientlysecure.htmltextview.HtmlHttpImageGetter import org.acra.ACRA
import retrofit2.Call import retrofit2.Call
import retrofit2.Callback import retrofit2.Callback
import retrofit2.Response import retrofit2.Response
import java.net.MalformedURLException
import java.net.URL
class ArticleFragment : Fragment() { class ArticleFragment : Fragment() {
private lateinit var pageNumber: Number private lateinit var pageNumber: Number
private var fontSize: Int = 14
private lateinit var allItems: ArrayList<Item> private lateinit var allItems: ArrayList<Item>
private lateinit var mCustomTabActivityHelper: CustomTabActivityHelper private lateinit var mCustomTabActivityHelper: CustomTabActivityHelper
private lateinit var url: String private lateinit var url: String
@ -46,9 +55,9 @@ class ArticleFragment : Fragment() {
private lateinit var contentSource: String private lateinit var contentSource: String
private lateinit var contentImage: String private lateinit var contentImage: String
private lateinit var contentTitle: String private lateinit var contentTitle: String
private lateinit var editor: SharedPreferences.Editor
private lateinit var fab: FloatingActionButton private lateinit var fab: FloatingActionButton
private lateinit var appColors: AppColors
private var useWebview: Boolean = false
override fun onStop() { override fun onStop() {
super.onStop() super.onStop()
@ -56,21 +65,24 @@ class ArticleFragment : Fragment() {
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
appColors = AppColors(activity!!)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
pageNumber = arguments!!.getInt(ARG_POSITION) pageNumber = arguments!!.getInt(ARG_POSITION)
allItems = arguments!!.getParcelableArrayList(ARG_ITEMS) allItems = arguments!!.getParcelableArrayList(ARG_ITEMS)
useWebview = arguments!!.getBoolean(ARG_WEBVIEW)
} }
private lateinit var rootView: ViewGroup private lateinit var rootView: ViewGroup
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
rootView = inflater rootView = inflater
.inflate(R.layout.fragment_article, container, false) as ViewGroup .inflate(R.layout.fragment_article, container, false) as ViewGroup
url = allItems[pageNumber.toInt()].getLinkDecoded() url = allItems[pageNumber.toInt()].getLinkDecoded()
contentText = allItems[pageNumber.toInt()].content contentText = allItems[pageNumber.toInt()].content
@ -78,230 +90,353 @@ class ArticleFragment : Fragment() {
contentImage = allItems[pageNumber.toInt()].getThumbnail(activity!!) contentImage = allItems[pageNumber.toInt()].getThumbnail(activity!!)
contentSource = allItems[pageNumber.toInt()].sourceAndDateText() contentSource = allItems[pageNumber.toInt()].sourceAndDateText()
val prefs = PreferenceManager.getDefaultSharedPreferences(activity)
editor = prefs.edit()
fontSize = prefs.getString("reader_font_size", "14").toInt()
val settings = activity!!.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
val debugReadingItems = prefs.getBoolean("read_debug", false)
val api = SelfossApi(
context!!,
activity!!,
settings.getBoolean("isSelfSignedCert", false),
prefs.getBoolean("should_log_everything", false)
)
fab = rootView.fab fab = rootView.fab
val mFloatingToolbar: FloatingToolbar = rootView.floatingToolbar
mFloatingToolbar.attachFab(fab) fab.backgroundTintList = ColorStateList.valueOf(appColors.colorAccent)
fab.rippleColor = appColors.colorAccentDark
val floatingToolbar: FloatingToolbar = rootView.floatingToolbar
floatingToolbar.attachFab(fab)
floatingToolbar.background = ColorDrawable(appColors.colorAccent)
val customTabsIntent = activity!!.buildCustomTabsIntent() val customTabsIntent = activity!!.buildCustomTabsIntent()
mCustomTabActivityHelper = CustomTabActivityHelper() mCustomTabActivityHelper = CustomTabActivityHelper()
mCustomTabActivityHelper.bindCustomTabsService(activity) mCustomTabActivityHelper.bindCustomTabsService(activity)
val prefs = PreferenceManager.getDefaultSharedPreferences(activity)
mFloatingToolbar.setClickListener( floatingToolbar.setClickListener(
object : FloatingToolbar.ItemClickListener { object : FloatingToolbar.ItemClickListener {
override fun onItemClick(item: MenuItem) { override fun onItemClick(item: MenuItem) {
when (item.itemId) { when (item.itemId) {
R.id.more_action -> getContentFromMercury(customTabsIntent, prefs) R.id.more_action -> getContentFromMercury(customTabsIntent, prefs)
R.id.share_action -> activity!!.shareLink(url) R.id.share_action -> activity!!.shareLink(url)
R.id.open_action -> activity!!.openItemUrl( R.id.open_action -> activity!!.openItemUrl(
allItems, allItems,
pageNumber.toInt(), pageNumber.toInt(),
url, url,
customTabsIntent, customTabsIntent,
false, false,
false, false,
activity!! activity!!
) )
else -> Unit R.id.unread_action -> api.unmarkItem(allItems[pageNumber.toInt()].id).enqueue(
} object : Callback<SuccessResponse> {
} override fun onResponse(
call: Call<SuccessResponse>,
response: Response<SuccessResponse>
) {
if (!response.succeeded() && debugReadingItems) {
val message =
"message: ${response.message()} " +
"response isSuccess: ${response.isSuccessful} " +
"response code: ${response.code()} " +
"response message: ${response.message()} " +
"response errorBody: ${response.errorBody()?.string()} " +
"body success: ${response.body()?.success} " +
"body isSuccess: ${response.body()?.isSuccess}"
ACRA.getErrorReporter().maybeHandleSilentException(Exception(message), activity!!)
}
}
override fun onItemLongClick(item: MenuItem?) { override fun onFailure(
call: Call<SuccessResponse>,
t: Throwable
) {
if (debugReadingItems) {
ACRA.getErrorReporter().maybeHandleSilentException(t, activity!!)
}
}
}
)
else -> Unit
} }
} }
override fun onItemLongClick(item: MenuItem?) {
}
}
) )
rootView.source.text = contentSource
if (contentText.isEmptyOrNullOrNullString()) { if (contentText.isEmptyOrNullOrNullString()) {
getContentFromMercury(customTabsIntent, prefs) getContentFromMercury(customTabsIntent, prefs)
} else { } else {
rootView.source.text = contentSource
rootView.titleView.text = contentTitle rootView.titleView.text = contentTitle
if (!useWebview) { htmlToWebview(contentText, prefs)
htmlToTextview(contentText, customTabsIntent, prefs)
} else {
htmlToWebview(contentText)
}
if (!contentImage.isEmptyOrNullOrNullString()) { if (!contentImage.isEmptyOrNullOrNullString() && context != null) {
rootView.imageView.visibility = View.VISIBLE rootView.imageView.visibility = View.VISIBLE
Glide Glide
.with(activity!!.baseContext) .with(context!!)
.asBitmap() .asBitmap()
.load(contentImage) .load(contentImage)
.apply(RequestOptions.fitCenterTransform()) .apply(RequestOptions.fitCenterTransform())
.into(rootView.imageView) .into(rootView.imageView)
} else { } else {
rootView.imageView.visibility = View.GONE rootView.imageView.visibility = View.GONE
} }
} }
rootView.nestedScrollView.setOnScrollChangeListener( rootView.nestedScrollView.setOnScrollChangeListener(
NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY -> NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
if (scrollY > oldScrollY) { if (scrollY > oldScrollY) {
fab.hide() fab.hide()
} else { } else {
if (mFloatingToolbar.isShowing) mFloatingToolbar.hide() else fab.show() if (floatingToolbar.isShowing) floatingToolbar.hide() else fab.show()
}
} }
}
) )
if (!useWebview) {
rootView.content.movementMethod = LinkMovementMethod.getInstance()
}
return rootView return rootView
} }
private fun getContentFromMercury( private fun getContentFromMercury(
customTabsIntent: CustomTabsIntent, customTabsIntent: CustomTabsIntent,
prefs: SharedPreferences prefs: SharedPreferences
) { ) {
rootView.progressBar.visibility = View.VISIBLE rootView.progressBar.visibility = View.VISIBLE
val parser = MercuryApi( val parser = MercuryApi(
BuildConfig.MERCURY_KEY, prefs.getBoolean("should_log_everything", false)
prefs.getBoolean("should_log_everything", false)
) )
parser.parseUrl(url).enqueue( parser.parseUrl(url).enqueue(
object : Callback<ParsedContent> { object : Callback<ParsedContent> {
override fun onResponse( override fun onResponse(
call: Call<ParsedContent>, call: Call<ParsedContent>,
response: Response<ParsedContent> response: Response<ParsedContent>
) { ) {
if (response.body() != null && response.body()!!.content != null && response.body()!!.content.isNotEmpty()) { // TODO: clean all the following after finding the mercury content issue
rootView.source.text = response.body()!!.domain try {
rootView.titleView.text = response.body()!!.title if (response.body() != null && response.body()!!.content != null && !response.body()!!.content.isNullOrEmpty()) {
url = response.body()!!.url try {
rootView.titleView.text = response.body()!!.title
if (response.body()!!.content != null && !response.body()!!.content.isEmpty()) { try {
if (!useWebview) { // Note: Mercury may return relative urls... If it does the url val will not be changed.
htmlToTextview( URL(response.body()!!.url)
response.body()!!.content, url = response.body()!!.url
customTabsIntent, } catch (e: MalformedURLException) {
prefs ACRA.getErrorReporter().maybeHandleSilentException(e, activity!!)
) }
} else { } catch (e: Exception) {
htmlToWebview(response.body()!!.content) if (context != null) {
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
} }
} }
if (response.body()!!.lead_image_url != null && !response.body()!!.lead_image_url.isEmpty()) { try {
rootView.imageView.visibility = View.VISIBLE htmlToWebview(response.body()!!.content.orEmpty(), prefs)
Glide } catch (e: Exception) {
.with(activity!!.baseContext) if (context != null) {
.asBitmap() ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
.load(response.body()!!.lead_image_url) }
.apply(RequestOptions.fitCenterTransform())
.into(rootView.imageView)
} else {
rootView.imageView.visibility = View.GONE
} }
rootView.nestedScrollView.scrollTo(0, 0) try {
if (response.body()!!.lead_image_url != null && !response.body()!!.lead_image_url.isNullOrEmpty() && context != null) {
rootView.imageView.visibility = View.VISIBLE
try {
Glide
.with(context!!)
.asBitmap()
.load(response.body()!!.lead_image_url)
.apply(RequestOptions.fitCenterTransform())
.into(rootView.imageView)
} catch (e: Exception) {
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
}
} else {
rootView.imageView.visibility = View.GONE
}
} catch (e: Exception) {
if (context != null) {
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
}
}
rootView.progressBar.visibility = View.GONE try {
rootView.nestedScrollView.scrollTo(0, 0)
rootView.progressBar.visibility = View.GONE
} catch (e: Exception) {
if (context != null) {
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
}
}
} else { } else {
openInBrowserAfterFailing(customTabsIntent) try {
openInBrowserAfterFailing(customTabsIntent)
} catch (e: Exception) {
if (context != null) {
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
}
}
}
} catch (e: Exception) {
if (context != null) {
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
} }
} }
override fun onFailure(
call: Call<ParsedContent>,
t: Throwable
) = openInBrowserAfterFailing(customTabsIntent)
} }
override fun onFailure(
call: Call<ParsedContent>,
t: Throwable
) = openInBrowserAfterFailing(customTabsIntent)
}
) )
} }
private fun htmlToTextview( private fun htmlToWebview(c: String, prefs: SharedPreferences) {
c: String, val stringColor = String.format("#%06X", 0xFFFFFF and appColors.colorAccent)
customTabsIntent: CustomTabsIntent,
prefs: SharedPreferences
) {
try {
rootView.content.visibility = View.VISIBLE
rootView.content.text = Html.fromHtml(
c,
HtmlHttpImageGetter(rootView.content, null, true),
null
)
} catch (e: Exception) {
Crashlytics.setUserIdentifier(prefs.getString("unique_id", ""))
Crashlytics.log(100, "CANT_TRANSFORM_TO_HTML", e.message)
Crashlytics.logException(e)
openInBrowserAfterFailing(customTabsIntent)
}
}
private fun htmlToWebview(c: String) {
val attrBackground = TypedValue()
activity!!.baseContext.theme.resolveAttribute(
android.R.attr.colorBackground,
attrBackground,
true
)
val defaultColor = TypedValue()
activity!!.baseContext.theme.resolveAttribute(
android.R.attr.colorAccent,
defaultColor,
true
)
val accentColor = fab.backgroundTintList?.defaultColor ?: defaultColor.data
val stringColor = String.format("#%06X", 0xFFFFFF and accentColor)
rootView.webcontent.visibility = View.VISIBLE rootView.webcontent.visibility = View.VISIBLE
rootView.webcontent.setBackgroundColor(attrBackground.data) val (textColor, backgroundColor) = if (appColors.isDarkTheme) {
if (context != null) {
rootView.webcontent.setBackgroundColor(
ContextCompat.getColor(
context!!,
R.color.dark_webview
)
)
Pair(ContextCompat.getColor(context!!, R.color.dark_webview_text), ContextCompat.getColor(context!!, R.color.light_webview_text))
} else {
Pair(null, null)
}
} else {
if (context != null) {
rootView.webcontent.setBackgroundColor(
ContextCompat.getColor(
context!!,
R.color.light_webview
)
)
Pair(ContextCompat.getColor(context!!, R.color.light_webview_text), ContextCompat.getColor(context!!, R.color.dark_webview_text))
} else {
Pair(null, null)
}
}
val stringTextColor: String = if (textColor != null) {
String.format("#%06X", 0xFFFFFF and textColor)
} else {
"#000000"
}
val stringBackgroundColor = if (backgroundColor != null) {
String.format("#%06X", 0xFFFFFF and backgroundColor)
} else {
"#FFFFFF"
}
rootView.webcontent.settings.useWideViewPort = true rootView.webcontent.settings.useWideViewPort = true
rootView.webcontent.settings.loadWithOverviewMode = true rootView.webcontent.settings.loadWithOverviewMode = true
rootView.webcontent.settings.javaScriptEnabled = false rootView.webcontent.settings.javaScriptEnabled = false
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
rootView.webcontent.settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING rootView.webcontent.settings.layoutAlgorithm =
WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING
} else { } else {
rootView.webcontent.settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.SINGLE_COLUMN rootView.webcontent.settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.SINGLE_COLUMN
} }
rootView.webcontent.loadData( var baseUrl: String? = null
"<style>img{display: inline-block;height: auto; width: 100%; max-width: 100%;} a{color: $stringColor}</style>$c",
"text/html; charset=utf-8", try {
"utf-8" val itemUrl = URL(url)
baseUrl = itemUrl.protocol + "://" + itemUrl.host
} catch (e: MalformedURLException) {
ACRA.getErrorReporter().maybeHandleSilentException(e, activity!!)
}
rootView.webcontent.loadDataWithBaseURL(
baseUrl,
"""<html>
|<head>
| <style>
| img {
| display: inline-block;
| height: auto;
| width: 100%;
| max-width: 100%;
| }
| a {
| color: $stringColor !important;
| }
| *:not(a) {
| color: $stringTextColor;
| }
| * {
| font-size: ${fontSize.toPx}px;
| text-align: justify;
| word-break: break-word;
| overflow:hidden;
| }
| a, pre, code {
| text-align: left;
| }
| pre, code {
| white-space: pre-wrap;
| width:100%;
| background-color: $stringBackgroundColor;
| }
| </style>
|</head>
|<body>
| $c
|</body>""".trimMargin(),
"text/html",
"utf-8",
null
) )
} }
private fun openInBrowserAfterFailing(customTabsIntent: CustomTabsIntent) { private fun openInBrowserAfterFailing(customTabsIntent: CustomTabsIntent) {
rootView.progressBar.visibility = View.GONE rootView.progressBar.visibility = View.GONE
activity!!.openItemUrl( activity!!.openItemUrl(
allItems, allItems,
pageNumber.toInt(), pageNumber.toInt(),
url, url,
customTabsIntent, customTabsIntent,
true, true,
false, false,
activity!! activity!!
) )
} }
companion object { companion object {
private val ARG_POSITION = "position" private const val ARG_POSITION = "position"
private val ARG_ITEMS = "items" private const val ARG_ITEMS = "items"
private val ARG_WEBVIEW = "userWebview"
fun newInstance( fun newInstance(
position: Int, position: Int,
allItems: ArrayList<Item>, allItems: ArrayList<Item>
webview: Boolean
): ArticleFragment { ): ArticleFragment {
val fragment = ArticleFragment() val fragment = ArticleFragment()
val args = Bundle() val args = Bundle()
args.putInt(ARG_POSITION, position) args.putInt(ARG_POSITION, position)
args.putBoolean(ARG_WEBVIEW, webview)
args.putParcelableArrayList(ARG_ITEMS, allItems) args.putParcelableArrayList(ARG_ITEMS, allItems)
fragment.arguments = args fragment.arguments = args
return fragment return fragment
} }
} }
} }

View File

@ -0,0 +1,37 @@
package apps.amine.bou.readerforselfoss.persistence.dao
import androidx.lifecycle.LiveData
import androidx.room.Delete
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import apps.amine.bou.readerforselfoss.persistence.entities.SourceEntity
import apps.amine.bou.readerforselfoss.persistence.entities.TagEntity
@Dao
interface DrawerDataDao {
@Query("SELECT * FROM tags")
fun tags(): List<TagEntity>
@Query("SELECT * FROM sources")
fun sources(): List<SourceEntity>
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAllTags(vararg tags: TagEntity)
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertAllSources(vararg sources: SourceEntity)
@Query("DELETE FROM tags")
fun deleteAllTags()
@Query("DELETE FROM sources")
fun deleteAllSources()
@Delete
fun deleteTag(tag: TagEntity)
@Delete
fun deleteSource(source: SourceEntity)
}

View File

@ -0,0 +1,12 @@
package apps.amine.bou.readerforselfoss.persistence.database
import androidx.room.RoomDatabase
import androidx.room.Database
import apps.amine.bou.readerforselfoss.persistence.dao.DrawerDataDao
import apps.amine.bou.readerforselfoss.persistence.entities.SourceEntity
import apps.amine.bou.readerforselfoss.persistence.entities.TagEntity
@Database(entities = [TagEntity::class, SourceEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun drawerDataDao(): DrawerDataDao
}

View File

@ -0,0 +1,33 @@
package apps.amine.bou.readerforselfoss.persistence.entities
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
@Entity(tableName = "tags")
data class TagEntity(
@PrimaryKey
@ColumnInfo(name = "tag")
val tag: String,
@ColumnInfo(name = "color")
val color: String,
@ColumnInfo(name = "unread")
val unread: Int
)
@Entity(tableName = "sources")
data class SourceEntity(
@PrimaryKey
@ColumnInfo(name = "id")
val id: String,
@ColumnInfo(name = "title")
val title: String,
@ColumnInfo(name = "tags")
val tags: String,
@ColumnInfo(name = "spout")
val spout: String,
@ColumnInfo(name = "error")
val error: String,
@ColumnInfo(name = "icon")
val icon: String
)

View File

@ -1,38 +1,43 @@
package apps.amine.bou.readerforselfoss.settings; package apps.amine.bou.readerforselfoss.settings;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.preference.PreferenceActivity; import android.preference.PreferenceActivity;
import android.support.annotation.LayoutRes; import androidx.annotation.LayoutRes;
import android.support.annotation.NonNull; import androidx.annotation.NonNull;
import android.support.annotation.Nullable; import androidx.annotation.Nullable;
import android.support.design.widget.AppBarLayout; import com.google.android.material.appbar.AppBarLayout;
import android.support.v7.app.ActionBar; import androidx.appcompat.app.ActionBar;
import android.support.v7.app.AppCompatDelegate; import androidx.appcompat.app.AppCompatDelegate;
import android.support.v7.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import apps.amine.bou.readerforselfoss.R;
import com.ftinc.scoop.Scoop; import com.ftinc.scoop.Scoop;
import apps.amine.bou.readerforselfoss.R;
import apps.amine.bou.readerforselfoss.themes.AppColors;
import apps.amine.bou.readerforselfoss.themes.Toppings;
/** /**
* A {@link PreferenceActivity} which implements and proxies the necessary calls * A {@link PreferenceActivity} which implements and proxies the necessary calls
* to be used with AppCompat. * to be used with AppCompat.
*/ */
public abstract class AppCompatPreferenceActivity extends PreferenceActivity { //NOSONAR public abstract class AppCompatPreferenceActivity extends PreferenceActivity {
private AppCompatDelegate mDelegate; private AppCompatDelegate mDelegate;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
new AppColors(this);
getDelegate().installViewFactory(); getDelegate().installViewFactory();
getDelegate().onCreate(savedInstanceState); getDelegate().onCreate(savedInstanceState);
Scoop.getInstance().apply(this);
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
} }
@ -40,9 +45,16 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity { /
protected void onPostCreate(Bundle savedInstanceState) { protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState); super.onPostCreate(savedInstanceState);
LinearLayout root = (LinearLayout)findViewById(android.R.id.list).getParent().getParent().getParent(); LinearLayout root = (LinearLayout) findViewById(android.R.id.list).getParent().getParent().getParent();
AppBarLayout bar = (AppBarLayout) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false); AppBarLayout bar = (AppBarLayout) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
Toolbar toolbar = bar.findViewById(R.id.toolbar); Toolbar toolbar = bar.findViewById(R.id.toolbar);
Scoop scoop = Scoop.getInstance();
scoop.bind(this, Toppings.PRIMARY.getValue(), toolbar);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.getValue());
}
setSupportActionBar(toolbar); setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowHomeEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true);

View File

@ -1,8 +1,6 @@
package apps.amine.bou.readerforselfoss.settings; package apps.amine.bou.readerforselfoss.settings;
import java.util.List;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.ClipData; import android.content.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
@ -21,16 +19,21 @@ import android.preference.PreferenceActivity;
import android.preference.PreferenceFragment; import android.preference.PreferenceFragment;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.preference.SwitchPreference; import android.preference.SwitchPreference;
import android.support.v7.app.ActionBar; import androidx.appcompat.app.ActionBar;
import android.text.Editable;
import android.text.InputFilter; import android.text.InputFilter;
import android.text.Spanned; import android.text.Spanned;
import android.text.TextWatcher;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.widget.Toast; import android.widget.Toast;
import apps.amine.bou.readerforselfoss.BuildConfig; import java.util.List;
import apps.amine.bou.readerforselfoss.R; import apps.amine.bou.readerforselfoss.R;
import apps.amine.bou.readerforselfoss.themes.AppColors;
import apps.amine.bou.readerforselfoss.utils.Config; import apps.amine.bou.readerforselfoss.utils.Config;
import com.ftinc.scoop.ui.ScoopSettingsActivity;
/** /**
@ -44,7 +47,7 @@ import com.ftinc.scoop.ui.ScoopSettingsActivity;
* href="http://developer.android.com/guide/topics/ui/settings.html">Settings * href="http://developer.android.com/guide/topics/ui/settings.html">Settings
* API Guide</a> for more information on developing a Settings UI. * API Guide</a> for more information on developing a Settings UI.
*/ */
public class SettingsActivity extends AppCompatPreferenceActivity { //NOSONAR public class SettingsActivity extends AppCompatPreferenceActivity {
/** /**
* A preference value change listener that updates the preference's summary * A preference value change listener that updates the preference's summary
* to reflect its new value. * to reflect its new value.
@ -90,6 +93,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity { //NOSONAR
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
new AppColors(this);
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setupActionBar(); setupActionBar();
} }
@ -130,8 +134,10 @@ public class SettingsActivity extends AppCompatPreferenceActivity { //NOSONAR
protected boolean isValidFragment(String fragmentName) { protected boolean isValidFragment(String fragmentName) {
return PreferenceFragment.class.getName().equals(fragmentName) return PreferenceFragment.class.getName().equals(fragmentName)
|| GeneralPreferenceFragment.class.getName().equals(fragmentName) || GeneralPreferenceFragment.class.getName().equals(fragmentName)
|| ArticleViewerPreferenceFragment.class.getName().equals(fragmentName)
|| DebugPreferenceFragment.class.getName().equals(fragmentName) || DebugPreferenceFragment.class.getName().equals(fragmentName)
|| LinksPreferenceFragment.class.getName().equals(fragmentName); || LinksPreferenceFragment.class.getName().equals(fragmentName)
|| ThemePreferenceFragment.class.getName().equals(fragmentName);
} }
/** /**
@ -150,7 +156,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity { //NOSONAR
final SwitchPreference tabOnTap = (SwitchPreference) findPreference("tab_on_tap"); final SwitchPreference tabOnTap = (SwitchPreference) findPreference("tab_on_tap");
tabOnTap.setEnabled(!cardViewActive.isChecked()); tabOnTap.setEnabled(!cardViewActive.isChecked());
cardViewActive.setOnPreferenceChangeListener(new OnPreferenceChangeListener() { cardViewActive.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
public boolean onPreferenceChange(Preference preference, Object newValue){ public boolean onPreferenceChange(Preference preference, Object newValue) {
boolean isEnabled = (Boolean) newValue; boolean isEnabled = (Boolean) newValue;
tabOnTap.setEnabled(!isEnabled); tabOnTap.setEnabled(!isEnabled);
return true; return true;
@ -159,21 +165,72 @@ public class SettingsActivity extends AppCompatPreferenceActivity { //NOSONAR
EditTextPreference itemsNumber = (EditTextPreference) findPreference("prefer_api_items_number"); EditTextPreference itemsNumber = (EditTextPreference) findPreference("prefer_api_items_number");
itemsNumber.getEditText().setFilters(new InputFilter[]{ itemsNumber.getEditText().setFilters(new InputFilter[]{
new InputFilter (){ new InputFilter() {
@Override @Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
try { try {
int input = Integer.parseInt(dest.toString() + source.toString()); int input = Integer.parseInt(dest.toString() + source.toString());
if (input <= 200 && input >0) if (input <= 200 && input > 0)
return null; return null;
} catch (NumberFormatException nfe) { } catch (NumberFormatException nfe) {
Toast.makeText(getActivity(), R.string.items_number_should_be_number, Toast.LENGTH_LONG).show(); Toast.makeText(getActivity(), R.string.items_number_should_be_number, Toast.LENGTH_LONG).show();
}
return "";
} }
return "";
} }
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
getActivity().finish();
return true;
}
return super.onOptionsItemSelected(item);
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static class ArticleViewerPreferenceFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.pref_viewer);
setHasOptionsMenu(true);
final EditTextPreference fontSize = (EditTextPreference) findPreference("reader_font_size");
fontSize.getEditText().addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
@Override
public void afterTextChanged(Editable editable) {
try {
fontSize.getEditText().setTextSize(Integer.parseInt(editable.toString()));
} catch (NumberFormatException e) {}
} }
}); });
fontSize.getEditText().setFilters(new InputFilter[]{
new InputFilter() {
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
try {
int input = Integer.parseInt(dest.toString() + source.toString());
if (input > 0)
return null;
} catch (NumberFormatException nfe) {}
return "";
}
}
});
} }
@Override @Override
@ -195,7 +252,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity { //NOSONAR
addPreferencesFromResource(R.xml.pref_debug); addPreferencesFromResource(R.xml.pref_debug);
setHasOptionsMenu(true); setHasOptionsMenu(true);
SharedPreferences pref = getActivity().getSharedPreferences(Config.Companion.getSettingsName(), Context.MODE_PRIVATE); SharedPreferences pref = getActivity().getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE);
final String id = pref.getString("unique_id", "..."); final String id = pref.getString("unique_id", "...");
final Preference identifier = findPreference("debug_identifier"); final Preference identifier = findPreference("debug_identifier");
@ -205,11 +262,14 @@ public class SettingsActivity extends AppCompatPreferenceActivity { //NOSONAR
identifier.setOnPreferenceClickListener(new OnPreferenceClickListener() { identifier.setOnPreferenceClickListener(new OnPreferenceClickListener() {
@Override @Override
public boolean onPreferenceClick(Preference preference) { public boolean onPreferenceClick(Preference preference) {
ClipData clip = ClipData.newPlainText("Selfoss unique id", id); if (clipboard != null) {
clipboard.setPrimaryClip(clip); ClipData clip = ClipData.newPlainText("Selfoss unique id", id);
clipboard.setPrimaryClip(clip);
Toast.makeText(getActivity(), R.string.unique_id_to_clipboard, Toast.LENGTH_LONG).show(); Toast.makeText(getActivity(), R.string.unique_id_to_clipboard, Toast.LENGTH_LONG).show();
return true; return true;
}
return false;
} }
}); });
identifier.setTitle(id); identifier.setTitle(id);
@ -243,10 +303,10 @@ public class SettingsActivity extends AppCompatPreferenceActivity { //NOSONAR
addPreferencesFromResource(R.xml.pref_links); addPreferencesFromResource(R.xml.pref_links);
setHasOptionsMenu(true); setHasOptionsMenu(true);
findPreference( "trackerLink" ).setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { findPreference("trackerLink").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override @Override
public boolean onPreferenceClick(Preference preference) { public boolean onPreferenceClick(Preference preference) {
openUrl(Uri.parse(BuildConfig.TRACKER_URL)); openUrl(Uri.parse(Config.trackerUrl));
return true; return true;
} }
}); });
@ -254,7 +314,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity { //NOSONAR
findPreference("sourceLink").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { findPreference("sourceLink").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override @Override
public boolean onPreferenceClick(Preference preference) { public boolean onPreferenceClick(Preference preference) {
openUrl(Uri.parse(BuildConfig.SOURCE_URL)); openUrl(Uri.parse(Config.sourceUrl));
return false; return false;
} }
}); });
@ -262,7 +322,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity { //NOSONAR
findPreference("translation").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { findPreference("translation").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override @Override
public boolean onPreferenceClick(Preference preference) { public boolean onPreferenceClick(Preference preference) {
openUrl(Uri.parse(BuildConfig.TRANSLATION_URL)); openUrl(Uri.parse(Config.translationUrl));
return false; return false;
} }
}); });
@ -279,14 +339,38 @@ public class SettingsActivity extends AppCompatPreferenceActivity { //NOSONAR
} }
} }
@Override @TargetApi(Build.VERSION_CODES.HONEYCOMB)
public void onHeaderClick(Header header, int position) { public static class ThemePreferenceFragment extends PreferenceFragment {
super.onHeaderClick(header, position); @Override
if (header.id == R.id.theme_change) { public void onCreate(Bundle savedInstanceState) {
Intent intent = ScoopSettingsActivity.createIntent(getApplicationContext()); super.onCreate(savedInstanceState);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); addPreferencesFromResource(R.xml.pref_theme);
getApplicationContext().startActivity(intent); setHasOptionsMenu(true);
finish(); }
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == android.R.id.home) {
getActivity().finish();
return true;
} else if (id == R.id.clear) {
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getActivity());
SharedPreferences.Editor editor = pref.edit();
editor.remove("color_primary");
editor.remove("color_primary_dark");
editor.remove("color_accent");
editor.remove("color_accent_dark");
editor.remove("dark_theme");
editor.apply();
getActivity().finish();
}
return super.onOptionsItemSelected(item);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.settings_theme, menu);
} }
} }

View File

@ -2,49 +2,68 @@ package apps.amine.bou.readerforselfoss.themes
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.support.annotation.ColorInt import android.preference.PreferenceManager
import androidx.annotation.ColorInt
import androidx.appcompat.view.ContextThemeWrapper
import android.util.TypedValue import android.util.TypedValue
import apps.amine.bou.readerforselfoss.R import apps.amine.bou.readerforselfoss.R
import android.view.LayoutInflater
import android.view.ViewGroup
class AppColors(a: Activity) { class AppColors(a: Activity) {
@ColorInt val accent: Int
@ColorInt val dark: Int @ColorInt val colorPrimary: Int
@ColorInt val primary: Int @ColorInt val colorPrimaryDark: Int
@ColorInt val cardBackground: Int @ColorInt val colorAccent: Int
@ColorInt val windowBackground: Int @ColorInt val colorAccentDark: Int
@ColorInt val cardBackgroundColor: Int
@ColorInt val colorBackground: Int
val isDarkTheme: Boolean val isDarkTheme: Boolean
init { init {
val sharedPref = PreferenceManager.getDefaultSharedPreferences(a)
colorPrimary =
sharedPref.getInt(
"color_primary",
a.resources.getColor(R.color.colorPrimary)
)
colorPrimaryDark =
sharedPref.getInt(
"color_primary_dark",
a.resources.getColor(R.color.colorPrimaryDark)
)
colorAccent =
sharedPref.getInt(
"color_accent",
a.resources.getColor(R.color.colorAccent)
)
colorAccentDark =
sharedPref.getInt(
"color_accent_dark",
a.resources.getColor(R.color.colorAccentDark)
)
isDarkTheme =
sharedPref.getBoolean(
"dark_theme",
false
)
colorBackground = if (isDarkTheme) {
a.setTheme(R.style.NoBarDark)
R.color.darkBackground
} else {
a.setTheme(R.style.NoBar)
android.R.color.background_light
}
val wrapper = Context::class.java val wrapper = Context::class.java
val method = wrapper!!.getMethod("getThemeResId") val method = wrapper!!.getMethod("getThemeResId")
method.isAccessible = true method.isAccessible = true
isDarkTheme = when (method.invoke(a.baseContext)) {
R.style.NoBarTealOrangeDark,
R.style.NoBarDark,
R.style.NoBarBlueAmberDark,
R.style.NoBarGreyOrangeDark,
R.style.NoBarIndigoPinkDark,
R.style.NoBarRedTealDark,
R.style.NoBarCyanPinkDark -> true
else -> false
}
val typedAccent = TypedValue()
val typedAccentDark = TypedValue()
val typedPrimary = TypedValue()
val typedCardBackground = TypedValue() val typedCardBackground = TypedValue()
val typedWindowBackground = TypedValue()
a.theme.resolveAttribute(R.attr.colorAccent, typedAccent, true)
a.theme.resolveAttribute(R.attr.colorAccent, typedAccent, true)
a.theme.resolveAttribute(R.attr.colorPrimary, typedPrimary, true)
a.theme.resolveAttribute(R.attr.cardBackgroundColor, typedCardBackground, true) a.theme.resolveAttribute(R.attr.cardBackgroundColor, typedCardBackground, true)
a.theme.resolveAttribute(android.R.attr.colorBackground, typedWindowBackground, true)
accent = typedAccent.data cardBackgroundColor = typedCardBackground.data
dark = typedAccentDark.data
primary = typedPrimary.data
cardBackground = typedCardBackground.data
windowBackground = typedWindowBackground.data
} }
} }

View File

@ -0,0 +1,8 @@
package apps.amine.bou.readerforselfoss.themes
enum class Toppings(val value: Int) {
PRIMARY(1),
PRIMARY_DARK(2),
ACCENT(3),
ACCENT_DARK(4)
}

View File

@ -1,6 +1,6 @@
package apps.amine.bou.readerforselfoss.transformers package apps.amine.bou.readerforselfoss.transformers
import android.support.v4.view.ViewPager import androidx.viewpager.widget.ViewPager
import android.view.View import android.view.View
class DepthPageTransformer : ViewPager.PageTransformer { class DepthPageTransformer : ViewPager.PageTransformer {

View File

@ -0,0 +1,12 @@
package apps.amine.bou.readerforselfoss.utils
import android.content.Context
import android.preference.PreferenceManager
import org.acra.ErrorReporter
fun ErrorReporter.maybeHandleSilentException(throwable: Throwable, ctx: Context) {
val sharedPref = PreferenceManager.getDefaultSharedPreferences(ctx)
if (sharedPref.getBoolean("acra_should_log", false)) {
this.handleSilentException(throwable)
}
}

View File

@ -4,4 +4,4 @@ import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
import retrofit2.Response import retrofit2.Response
fun Response<SuccessResponse>.succeeded(): Boolean = fun Response<SuccessResponse>.succeeded(): Boolean =
this.code() === 200 && this.body() != null && this.body()!!.isSuccess this.code() === 200 && this.body() != null && this.body()!!.isSuccess

View File

@ -2,59 +2,10 @@ package apps.amine.bou.readerforselfoss.utils
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences
import android.net.Uri
import android.support.v7.app.AlertDialog
import apps.amine.bou.readerforselfoss.R import apps.amine.bou.readerforselfoss.R
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
fun String?.isEmptyOrNullOrNullString(): Boolean = fun String?.isEmptyOrNullOrNullString(): Boolean =
this == null || this == "null" || this.isEmpty() this == null || this == "null" || this.isEmpty()
fun Context.checkApkVersion(
settings: SharedPreferences,
editor: SharedPreferences.Editor,
mFirebaseRemoteConfig: FirebaseRemoteConfig
) = {
fun isThereAnUpdate() {
val APK_LINK = "github_apk"
val apkLink = mFirebaseRemoteConfig.getString(APK_LINK)
val storedLink = settings.getString(APK_LINK, "")
if (apkLink != storedLink && !apkLink.isEmpty()) {
val alertDialog = AlertDialog.Builder(this).create()
alertDialog.setTitle(getString(R.string.new_apk_available_title))
alertDialog.setMessage(getString(R.string.new_apk_available_message))
alertDialog.setButton(
AlertDialog.BUTTON_POSITIVE,
getString(R.string.new_apk_available_get)
) { _, _ ->
editor.putString(APK_LINK, apkLink)
editor.apply()
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(apkLink))
startActivity(browserIntent)
}
alertDialog.setButton(
AlertDialog.BUTTON_NEUTRAL, getString(R.string.new_apk_available_no),
{ dialog, _ ->
editor.putString(APK_LINK, apkLink)
editor.apply()
dialog.dismiss()
}
)
alertDialog.show()
}
}
mFirebaseRemoteConfig.fetch(43200)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
mFirebaseRemoteConfig.activateFetched()
}
isThereAnUpdate()
}
}
fun String.longHash(): Long { fun String.longHash(): Long {
var h = 98764321261L var h = 98764321261L
@ -68,11 +19,11 @@ fun String.longHash(): Long {
} }
fun String.toStringUriWithHttp(): String = fun String.toStringUriWithHttp(): String =
if (!this.startsWith("https://") && !this.startsWith("http://")) { if (!this.startsWith("https://") && !this.startsWith("http://")) {
"http://" + this "http://" + this
} else { } else {
this this
} }
fun Context.shareLink(itemUrl: String) { fun Context.shareLink(itemUrl: String) {
val sendIntent = Intent() val sendIntent = Intent()
@ -81,9 +32,9 @@ fun Context.shareLink(itemUrl: String) {
sendIntent.putExtra(Intent.EXTRA_TEXT, itemUrl.toStringUriWithHttp()) sendIntent.putExtra(Intent.EXTRA_TEXT, itemUrl.toStringUriWithHttp())
sendIntent.type = "text/plain" sendIntent.type = "text/plain"
startActivity( startActivity(
Intent.createChooser( Intent.createChooser(
sendIntent, sendIntent,
getString(R.string.share) getString(R.string.share)
).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK) ).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
) )
} }

View File

@ -26,13 +26,21 @@ class Config(c: Context) {
get() = settings.getString("httpPassword", "") get() = settings.getString("httpPassword", "")
companion object { companion object {
val settingsName = "paramsselfoss" const val settingsName = "paramsselfoss"
const val feedbackEmail = "aminecmi@gmail.com"
const val translationUrl = "https://crwd.in/readerforselfoss"
const val sourceUrl = "https://github.com/aminecmi/ReaderforSelfoss"
const val trackerUrl = "https://github.com/aminecmi/ReaderforSelfoss/issues"
fun logoutAndRedirect( fun logoutAndRedirect(
c: Context, c: Context,
callingActivity: Activity, callingActivity: Activity,
editor: SharedPreferences.Editor, editor: SharedPreferences.Editor,
baseUrlFail: Boolean = false baseUrlFail: Boolean = false
): Boolean { ): Boolean {
editor.remove("url") editor.remove("url")
editor.remove("login") editor.remove("login")

View File

@ -7,37 +7,37 @@ import javax.net.ssl.SSLContext
import javax.net.ssl.TrustManager import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager import javax.net.ssl.X509TrustManager
fun getUnsafeHttpClient() = fun getUnsafeHttpClient(): OkHttpClient.Builder =
try { try {
// Create a trust manager that does not validate certificate chains // Create a trust manager that does not validate certificate chains
val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager { val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
override fun getAcceptedIssuers(): Array<X509Certificate> = override fun getAcceptedIssuers(): Array<X509Certificate> =
arrayOf() arrayOf()
@Throws(CertificateException::class) @Throws(CertificateException::class)
override fun checkClientTrusted( override fun checkClientTrusted(
chain: Array<java.security.cert.X509Certificate>, chain: Array<java.security.cert.X509Certificate>,
authType: String authType: String
) { ) {
} }
@Throws(CertificateException::class) @Throws(CertificateException::class)
override fun checkServerTrusted( override fun checkServerTrusted(
chain: Array<java.security.cert.X509Certificate>, chain: Array<java.security.cert.X509Certificate>,
authType: String authType: String
) { ) {
} }
}) })
// Install the all-trusting trust manager // Install the all-trusting trust manager
val sslContext = SSLContext.getInstance("SSL") val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, java.security.SecureRandom()) sslContext.init(null, trustAllCerts, java.security.SecureRandom())
val sslSocketFactory = sslContext.socketFactory val sslSocketFactory = sslContext.socketFactory
OkHttpClient.Builder() OkHttpClient.Builder()
.sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager) .sslSocketFactory(sslSocketFactory, trustAllCerts[0] as X509TrustManager)
.hostnameVerifier { _, _ -> true } .hostnameVerifier { _, _ -> true }
} catch (e: Exception) { } catch (e: Exception) {
throw RuntimeException(e) throw RuntimeException(e)
} }

View File

@ -1,15 +1,21 @@
package apps.amine.bou.readerforselfoss.utils package apps.amine.bou.readerforselfoss.utils
import android.content.Context
import android.text.format.DateUtils import android.text.format.DateUtils
import apps.amine.bou.readerforselfoss.api.selfoss.Item import apps.amine.bou.readerforselfoss.api.selfoss.Item
import org.acra.ACRA
import java.text.ParseException import java.text.ParseException
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
fun String.toTextDrawableString(): String { fun String.toTextDrawableString(c: Context): String {
val textDrawable = StringBuilder() val textDrawable = StringBuilder()
for (s in this.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) { for (s in this.split(" ".toRegex()).filter { !it.isEmpty() }.toTypedArray()) {
textDrawable.append(s[0]) try {
textDrawable.append(s[0])
} catch (e: StringIndexOutOfBoundsException) {
ACRA.getErrorReporter().maybeHandleSilentException(e, c)
}
} }
return textDrawable.toString() return textDrawable.toString()
} }
@ -17,10 +23,10 @@ fun String.toTextDrawableString(): String {
fun Item.sourceAndDateText(): String { fun Item.sourceAndDateText(): String {
val formattedDate: String = try { val formattedDate: String = try {
" " + DateUtils.getRelativeTimeSpanString( " " + DateUtils.getRelativeTimeSpanString(
SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(this.datetime).time, SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(this.datetime).time,
Date().time, Date().time,
DateUtils.MINUTE_IN_MILLIS, DateUtils.MINUTE_IN_MILLIS,
DateUtils.FORMAT_ABBREV_RELATIVE DateUtils.FORMAT_ABBREV_RELATIVE
) )
} catch (e: ParseException) { } catch (e: ParseException) {
e.printStackTrace() e.printStackTrace()
@ -28,4 +34,18 @@ fun Item.sourceAndDateText(): String {
} }
return this.sourcetitle + formattedDate return this.sourcetitle + formattedDate
} }
fun Item.toggleStar(): Item {
this.starred = !this.starred
return this
}
fun List<Item>.flattenTags(): List<Item> =
this.flatMap {
val item = it
val tags: List<String> = it.tags.split(",")
tags.map {
item.copy(tags = it.trim())
}
}

View File

@ -6,7 +6,7 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.net.Uri import android.net.Uri
import android.support.customtabs.CustomTabsIntent import androidx.browser.customtabs.CustomTabsIntent
import android.util.Patterns import android.util.Patterns
import android.widget.Toast import android.widget.Toast
import apps.amine.bou.readerforselfoss.R import apps.amine.bou.readerforselfoss.R
@ -20,10 +20,10 @@ fun Context.buildCustomTabsIntent(): CustomTabsIntent {
val actionIntent = Intent(Intent.ACTION_SEND) val actionIntent = Intent(Intent.ACTION_SEND)
actionIntent.type = "text/plain" actionIntent.type = "text/plain"
val createPendingShareIntent: PendingIntent = PendingIntent.getActivity( val createPendingShareIntent: PendingIntent = PendingIntent.getActivity(
this, this,
0, 0,
actionIntent, actionIntent,
0 0
) )
val intentBuilder = CustomTabsIntent.Builder() val intentBuilder = CustomTabsIntent.Builder()
@ -35,14 +35,14 @@ fun Context.buildCustomTabsIntent(): CustomTabsIntent {
intentBuilder.setStartAnimations( intentBuilder.setStartAnimations(
this, this,
R.anim.slide_in_right, R.anim.slide_in_right,
R.anim.slide_out_left R.anim.slide_out_left
) )
intentBuilder.setExitAnimations( intentBuilder.setExitAnimations(
this, this,
android.R.anim.slide_in_left, android.R.anim.slide_in_left,
android.R.anim.slide_out_right android.R.anim.slide_out_right
) )
val closeicon = BitmapFactory.decodeResource(resources, R.drawable.ic_close_white_24dp) val closeicon = BitmapFactory.decodeResource(resources, R.drawable.ic_close_white_24dp)
@ -50,8 +50,8 @@ fun Context.buildCustomTabsIntent(): CustomTabsIntent {
val shareLabel = this.getString(R.string.label_share) val shareLabel = this.getString(R.string.label_share)
val icon = BitmapFactory.decodeResource( val icon = BitmapFactory.decodeResource(
resources, resources,
R.drawable.ic_share_white_24dp R.drawable.ic_share_white_24dp
) )
intentBuilder.setActionButton(icon, shareLabel, createPendingShareIntent) intentBuilder.setActionButton(icon, shareLabel, createPendingShareIntent)
@ -59,12 +59,12 @@ fun Context.buildCustomTabsIntent(): CustomTabsIntent {
} }
fun Context.openItemUrlInternally( fun Context.openItemUrlInternally(
allItems: ArrayList<Item>, allItems: ArrayList<Item>,
currentItem: Int, currentItem: Int,
linkDecoded: String, linkDecoded: String,
customTabsIntent: CustomTabsIntent, customTabsIntent: CustomTabsIntent,
articleViewer: Boolean, articleViewer: Boolean,
app: Activity app: Activity
) { ) {
if (articleViewer) { if (articleViewer) {
ReaderActivity.allItems = allItems ReaderActivity.allItems = allItems
@ -74,9 +74,9 @@ fun Context.openItemUrlInternally(
} else { } else {
try { try {
CustomTabActivityHelper.openCustomTab( CustomTabActivityHelper.openCustomTab(
app, app,
customTabsIntent, customTabsIntent,
Uri.parse(linkDecoded) Uri.parse(linkDecoded)
) { _, uri -> ) { _, uri ->
val intent = Intent(Intent.ACTION_VIEW, uri) val intent = Intent(Intent.ACTION_VIEW, uri)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
@ -89,32 +89,32 @@ fun Context.openItemUrlInternally(
} }
fun Context.openItemUrl( fun Context.openItemUrl(
allItems: ArrayList<Item>, allItems: ArrayList<Item>,
currentItem: Int, currentItem: Int,
linkDecoded: String, linkDecoded: String,
customTabsIntent: CustomTabsIntent, customTabsIntent: CustomTabsIntent,
internalBrowser: Boolean, internalBrowser: Boolean,
articleViewer: Boolean, articleViewer: Boolean,
app: Activity app: Activity
) { ) {
if (!linkDecoded.isUrlValid()) { if (!linkDecoded.isUrlValid()) {
Toast.makeText( Toast.makeText(
this, this,
this.getString(R.string.cant_open_invalid_url), this.getString(R.string.cant_open_invalid_url),
Toast.LENGTH_LONG Toast.LENGTH_LONG
).show() ).show()
} else { } else {
if (!internalBrowser) { if (!internalBrowser) {
openInBrowser(linkDecoded, app) openInBrowser(linkDecoded, app)
} else { } else {
this.openItemUrlInternally( this.openItemUrlInternally(
allItems, allItems,
currentItem, currentItem,
linkDecoded, linkDecoded,
customTabsIntent, customTabsIntent,
articleViewer, articleViewer,
app app
) )
} }
} }
@ -127,7 +127,7 @@ private fun openInBrowser(linkDecoded: String, app: Activity) {
} }
fun String.isUrlValid(): Boolean = fun String.isUrlValid(): Boolean =
HttpUrl.parse(this) != null && Patterns.WEB_URL.matcher(this).matches() HttpUrl.parse(this) != null && Patterns.WEB_URL.matcher(this).matches()
fun String.isBaseUrlValid(): Boolean { fun String.isBaseUrlValid(): Boolean {
val baseUrl = HttpUrl.parse(this) val baseUrl = HttpUrl.parse(this)

View File

@ -1,53 +0,0 @@
package apps.amine.bou.readerforselfoss.utils
import android.content.Context
import android.support.design.widget.CoordinatorLayout
import android.support.design.widget.FloatingActionButton
import android.util.AttributeSet
import android.view.View
class ScrollAwareFABBehavior(
context: Context,
attrs: AttributeSet
) : CoordinatorLayout.Behavior<FloatingActionButton>() {
override fun onStartNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: FloatingActionButton,
directTargetChild: View,
target: View,
nestedScrollAxes: Int
): Boolean {
return true
}
override fun onNestedScroll(
coordinatorLayout: CoordinatorLayout,
child: FloatingActionButton,
target: View,
dxConsumed: Int,
dyConsumed: Int,
dxUnconsumed: Int,
dyUnconsumed: Int
) {
super.onNestedScroll(
coordinatorLayout,
child,
target,
dxConsumed,
dyConsumed,
dxUnconsumed,
dyUnconsumed
)
if (dyConsumed > 0 && child.visibility == View.VISIBLE) {
child.hide(object : FloatingActionButton.OnVisibilityChangedListener() {
override fun onHidden(fab: FloatingActionButton?) {
super.onHidden(fab)
fab!!.visibility = View.INVISIBLE
}
})
} else if (dyConsumed < 0 && child.visibility != View.VISIBLE) {
child.show()
}
}
}

View File

@ -0,0 +1,9 @@
package apps.amine.bou.readerforselfoss.utils
import android.content.res.Resources
val Int.toPx: Int
get() = (this * Resources.getSystem().displayMetrics.density).toInt()
val Int.toDp: Int
get() = (this / Resources.getSystem().displayMetrics.density).toInt()

View File

@ -9,4 +9,4 @@ fun TextBadgeItem.removeBadge(): TextBadgeItem {
} }
fun TextBadgeItem.maybeShow(): TextBadgeItem = fun TextBadgeItem.maybeShow(): TextBadgeItem =
if (this.isHidden) this.show() else this if (this.isHidden) this.show() else this

View File

@ -1,15 +1,15 @@
package apps.amine.bou.readerforselfoss.utils.customtabs; package apps.amine.bou.readerforselfoss.utils.customtabs;
import java.util.List;
import android.app.Activity; import android.app.Activity;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.support.customtabs.CustomTabsClient; import androidx.browser.customtabs.CustomTabsClient;
import android.support.customtabs.CustomTabsIntent; import androidx.browser.customtabs.CustomTabsIntent;
import android.support.customtabs.CustomTabsServiceConnection; import androidx.browser.customtabs.CustomTabsServiceConnection;
import android.support.customtabs.CustomTabsSession; import androidx.browser.customtabs.CustomTabsSession;
import java.util.List;
/** /**
* This is a helper class to manage the connection to the Custom Tabs Service. * This is a helper class to manage the connection to the Custom Tabs Service.
@ -23,15 +23,15 @@ public class CustomTabActivityHelper implements ServiceConnectionCallback {
/** /**
* Opens the URL on a Custom Tab if possible. Otherwise fallsback to opening it on a WebView. * Opens the URL on a Custom Tab if possible. Otherwise fallsback to opening it on a WebView.
* *
* @param activity The host activity. * @param activity The host activity.
* @param customTabsIntent a CustomTabsIntent to be used if Custom Tabs is available. * @param customTabsIntent a CustomTabsIntent to be used if Custom Tabs is available.
* @param uri the Uri to be opened. * @param uri the Uri to be opened.
* @param fallback a CustomTabFallback to be used if Custom Tabs is not available. * @param fallback a CustomTabFallback to be used if Custom Tabs is not available.
*/ */
public static void openCustomTab(Activity activity, public static void openCustomTab(Activity activity,
CustomTabsIntent customTabsIntent, CustomTabsIntent customTabsIntent,
Uri uri, Uri uri,
CustomTabFallback fallback) { CustomTabFallback fallback) {
String packageName = CustomTabsHelper.getPackageNameToUse(activity); String packageName = CustomTabsHelper.getPackageNameToUse(activity);
//If we cant find a package name, it means theres no browser that supports //If we cant find a package name, it means theres no browser that supports
@ -48,6 +48,7 @@ public class CustomTabActivityHelper implements ServiceConnectionCallback {
/** /**
* Unbinds the Activity from the Custom Tabs Service. * Unbinds the Activity from the Custom Tabs Service.
*
* @param activity the activity that is connected to the service. * @param activity the activity that is connected to the service.
*/ */
public void unbindCustomTabsService(Activity activity) { public void unbindCustomTabsService(Activity activity) {
@ -74,6 +75,7 @@ public class CustomTabActivityHelper implements ServiceConnectionCallback {
/** /**
* Register a Callback to be called when connected or disconnected from the Custom Tabs Service. * Register a Callback to be called when connected or disconnected from the Custom Tabs Service.
*
* @param connectionCallback * @param connectionCallback
*/ */
public void setConnectionCallback(ConnectionCallback connectionCallback) { public void setConnectionCallback(ConnectionCallback connectionCallback) {
@ -82,6 +84,7 @@ public class CustomTabActivityHelper implements ServiceConnectionCallback {
/** /**
* Binds the Activity to the Custom Tabs Service. * Binds the Activity to the Custom Tabs Service.
*
* @param activity the activity to be binded to the service. * @param activity the activity to be binded to the service.
*/ */
public void bindCustomTabsService(Activity activity) { public void bindCustomTabsService(Activity activity) {
@ -95,16 +98,15 @@ public class CustomTabActivityHelper implements ServiceConnectionCallback {
} }
/** /**
* @see {@link CustomTabsSession#mayLaunchUrl(Uri, Bundle, List)}.
* @return true if call to mayLaunchUrl was accepted. * @return true if call to mayLaunchUrl was accepted.
* @see {@link CustomTabsSession#mayLaunchUrl(Uri, Bundle, List)}.
*/ */
public boolean mayLaunchUrl(Uri uri, Bundle extras, List<Bundle> otherLikelyBundles) { public boolean mayLaunchUrl(Uri uri, Bundle extras, List<Bundle> otherLikelyBundles) {
if (mClient == null) return false; if (mClient == null) return false;
CustomTabsSession session = getSession(); CustomTabsSession session = getSession();
if (session == null) return false; return session != null && session.mayLaunchUrl(uri, extras, otherLikelyBundles);
return session.mayLaunchUrl(uri, extras, otherLikelyBundles);
} }
@Override @Override
@ -142,9 +144,8 @@ public class CustomTabActivityHelper implements ServiceConnectionCallback {
*/ */
public interface CustomTabFallback { public interface CustomTabFallback {
/** /**
*
* @param activity The Activity that wants to open the Uri. * @param activity The Activity that wants to open the Uri.
* @param uri The uri to be opened by the fallback. * @param uri The uri to be opened by the fallback.
*/ */
void openUri(Activity activity, Uri uri); void openUri(Activity activity, Uri uri);
} }

View File

@ -1,19 +1,19 @@
package apps.amine.bou.readerforselfoss.utils.customtabs; package apps.amine.bou.readerforselfoss.utils.customtabs;
import java.util.ArrayList;
import java.util.List;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.net.Uri; import android.net.Uri;
import android.support.customtabs.CustomTabsService; import androidx.browser.customtabs.CustomTabsService;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import java.util.ArrayList;
import java.util.List;
import apps.amine.bou.readerforselfoss.utils.customtabs.helpers.KeepAliveService; import apps.amine.bou.readerforselfoss.utils.customtabs.helpers.KeepAliveService;
@SuppressWarnings("ALL") @SuppressWarnings("ALL")
@ -28,7 +28,8 @@ class CustomTabsHelper {
private static String sPackageNameToUse; private static String sPackageNameToUse;
private CustomTabsHelper() {} private CustomTabsHelper() {
}
public static void addKeepAliveExtra(Context context, Intent intent) { public static void addKeepAliveExtra(Context context, Intent intent) {
Intent keepAliveIntent = new Intent().setClassName( Intent keepAliveIntent = new Intent().setClassName(
@ -40,7 +41,7 @@ class CustomTabsHelper {
* Goes through all apps that handle VIEW intents and have a warmup service. Picks * Goes through all apps that handle VIEW intents and have a warmup service. Picks
* the one chosen by the user if there is one, otherwise makes a best effort to return a * the one chosen by the user if there is one, otherwise makes a best effort to return a
* valid package name. * valid package name.
* * <p>
* This is <strong>not</strong> threadsafe. * This is <strong>not</strong> threadsafe.
* *
* @param context {@link Context} to use for accessing {@link PackageManager}. * @param context {@link Context} to use for accessing {@link PackageManager}.
@ -94,6 +95,7 @@ class CustomTabsHelper {
/** /**
* Used to check whether there is a specialized handler for a given intent. * Used to check whether there is a specialized handler for a given intent.
*
* @param intent The intent to check with. * @param intent The intent to check with.
* @return Whether there is a specialized handler for the given intent. * @return Whether there is a specialized handler for the given intent.
*/ */

View File

@ -1,11 +1,11 @@
package apps.amine.bou.readerforselfoss.utils.customtabs; package apps.amine.bou.readerforselfoss.utils.customtabs;
import java.lang.ref.WeakReference;
import android.content.ComponentName; import android.content.ComponentName;
import android.support.customtabs.CustomTabsClient; import androidx.browser.customtabs.CustomTabsClient;
import android.support.customtabs.CustomTabsServiceConnection; import androidx.browser.customtabs.CustomTabsServiceConnection;
import java.lang.ref.WeakReference;
/** /**
* Implementation for the CustomTabsServiceConnection that avoids leaking the * Implementation for the CustomTabsServiceConnection that avoids leaking the

View File

@ -1,12 +1,13 @@
package apps.amine.bou.readerforselfoss.utils.customtabs; package apps.amine.bou.readerforselfoss.utils.customtabs;
import android.support.customtabs.CustomTabsClient; import androidx.browser.customtabs.CustomTabsClient;
public interface ServiceConnectionCallback { public interface ServiceConnectionCallback {
/** /**
* Called when the service is connected. * Called when the service is connected.
*
* @param client a CustomTabsClient * @param client a CustomTabsClient
*/ */
void onServiceConnected(CustomTabsClient client); void onServiceConnected(CustomTabsClient client);

View File

@ -1,7 +1,7 @@
/* From https://github.com/mikepenz/MaterialDrawer/blob/develop/app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/CustomBaseViewHolder.java */ /* From https://github.com/mikepenz/MaterialDrawer/blob/develop/app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/CustomBaseViewHolder.java */
package apps.amine.bou.readerforselfoss.utils.drawer package apps.amine.bou.readerforselfoss.utils.drawer
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import android.view.View import android.view.View
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView

View File

@ -2,10 +2,10 @@
package apps.amine.bou.readerforselfoss.utils.drawer package apps.amine.bou.readerforselfoss.utils.drawer
import android.net.Uri import android.net.Uri
import android.support.annotation.ColorInt import androidx.annotation.ColorInt
import android.support.annotation.ColorRes import androidx.annotation.ColorRes
import android.support.annotation.StringRes import androidx.annotation.StringRes
import android.support.v7.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.mikepenz.materialdrawer.holder.ColorHolder import com.mikepenz.materialdrawer.holder.ColorHolder
import com.mikepenz.materialdrawer.holder.ImageHolder import com.mikepenz.materialdrawer.holder.ImageHolder
@ -15,7 +15,8 @@ import com.mikepenz.materialdrawer.util.DrawerImageLoader
import com.mikepenz.materialdrawer.util.DrawerUIUtils import com.mikepenz.materialdrawer.util.DrawerUIUtils
import com.mikepenz.materialize.util.UIUtils import com.mikepenz.materialize.util.UIUtils
abstract class CustomUrlBasePrimaryDrawerItem<T, VH : RecyclerView.ViewHolder> : BaseDrawerItem<T, VH>() { abstract class CustomUrlBasePrimaryDrawerItem<T, VH : RecyclerView.ViewHolder> :
BaseDrawerItem<T, VH>() {
fun withIcon(url: String): T { fun withIcon(url: String): T {
this.icon = ImageHolder(url) this.icon = ImageHolder(url)
return this as T return this as T
@ -76,8 +77,8 @@ abstract class CustomUrlBasePrimaryDrawerItem<T, VH : RecyclerView.ViewHolder> :
//set the background for the item //set the background for the item
UIUtils.setBackground( UIUtils.setBackground(
viewHolder.view, viewHolder.view,
UIUtils.getSelectableBackground(ctx, selectedColor, true) UIUtils.getSelectableBackground(ctx, selectedColor, true)
) )
//set the text for the name //set the text for the name
StringHolder.applyTo(this.getName(), viewHolder.name) StringHolder.applyTo(this.getName(), viewHolder.name)
@ -88,9 +89,9 @@ abstract class CustomUrlBasePrimaryDrawerItem<T, VH : RecyclerView.ViewHolder> :
viewHolder.name.setTextColor(getTextColorStateList(color, selectedTextColor)) viewHolder.name.setTextColor(getTextColorStateList(color, selectedTextColor))
//set the description text color //set the description text color
ColorHolder.applyToOr( ColorHolder.applyToOr(
descriptionTextColor, descriptionTextColor,
viewHolder.description, viewHolder.description,
getTextColorStateList(color, selectedTextColor) getTextColorStateList(color, selectedTextColor)
) )
//define the typeface for our textViews //define the typeface for our textViews

View File

@ -1,8 +1,8 @@
/* From https://github.com/mikepenz/MaterialDrawer/blob/develop/app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/CustomUrlPrimaryDrawerItem.java */ /* From https://github.com/mikepenz/MaterialDrawer/blob/develop/app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/CustomUrlPrimaryDrawerItem.java */
package apps.amine.bou.readerforselfoss.utils.drawer package apps.amine.bou.readerforselfoss.utils.drawer
import android.support.annotation.LayoutRes import androidx.annotation.LayoutRes
import android.support.annotation.StringRes import androidx.annotation.StringRes
import android.view.View import android.view.View
import android.widget.TextView import android.widget.TextView
import apps.amine.bou.readerforselfoss.R import apps.amine.bou.readerforselfoss.R
@ -10,7 +10,9 @@ import com.mikepenz.materialdrawer.holder.BadgeStyle
import com.mikepenz.materialdrawer.holder.StringHolder import com.mikepenz.materialdrawer.holder.StringHolder
import com.mikepenz.materialdrawer.model.interfaces.ColorfulBadgeable import com.mikepenz.materialdrawer.model.interfaces.ColorfulBadgeable
class CustomUrlPrimaryDrawerItem : CustomUrlBasePrimaryDrawerItem<CustomUrlPrimaryDrawerItem, CustomUrlPrimaryDrawerItem.ViewHolder>(), ColorfulBadgeable<CustomUrlPrimaryDrawerItem> { class CustomUrlPrimaryDrawerItem :
CustomUrlBasePrimaryDrawerItem<CustomUrlPrimaryDrawerItem, CustomUrlPrimaryDrawerItem.ViewHolder>(),
ColorfulBadgeable<CustomUrlPrimaryDrawerItem> {
protected var mBadge: StringHolder = StringHolder("") protected var mBadge: StringHolder = StringHolder("")
protected var mBadgeStyle = BadgeStyle() protected var mBadgeStyle = BadgeStyle()
@ -64,8 +66,8 @@ class CustomUrlPrimaryDrawerItem : CustomUrlBasePrimaryDrawerItem<CustomUrlPrima
//style the badge if it is visible //style the badge if it is visible
if (badgeVisible) { if (badgeVisible) {
mBadgeStyle.style( mBadgeStyle.style(
viewHolder.badge, viewHolder.badge,
getTextColorStateList(getColor(ctx), getSelectedTextColor(ctx)) getTextColorStateList(getColor(ctx), getSelectedTextColor(ctx))
) )
viewHolder.badgeContainer.visibility = View.VISIBLE viewHolder.badgeContainer.visibility = View.VISIBLE
} else { } else {

View File

@ -2,38 +2,38 @@ package apps.amine.bou.readerforselfoss.utils.glide
import android.content.Context import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
import android.widget.ImageView import android.widget.ImageView
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions import com.bumptech.glide.request.RequestOptions
import com.bumptech.glide.request.target.BitmapImageViewTarget import com.bumptech.glide.request.target.BitmapImageViewTarget
fun Context.bitmapCenterCrop(url: String, iv: ImageView) = fun Context.bitmapCenterCrop(url: String, iv: ImageView) =
Glide.with(this) Glide.with(this)
.asBitmap() .asBitmap()
.load(url) .load(url)
.apply(RequestOptions.centerCropTransform()) .apply(RequestOptions.centerCropTransform())
.into(iv) .into(iv)
fun Context.bitmapFitCenter(url: String, iv: ImageView) = fun Context.bitmapFitCenter(url: String, iv: ImageView) =
Glide.with(this) Glide.with(this)
.asBitmap() .asBitmap()
.load(url) .load(url)
.apply(RequestOptions.fitCenterTransform()) .apply(RequestOptions.fitCenterTransform())
.into(iv) .into(iv)
fun Context.circularBitmapDrawable(url: String, iv: ImageView) = fun Context.circularBitmapDrawable(url: String, iv: ImageView) =
Glide.with(this) Glide.with(this)
.asBitmap() .asBitmap()
.load(url) .load(url)
.apply(RequestOptions.centerCropTransform()) .apply(RequestOptions.centerCropTransform())
.into(object : BitmapImageViewTarget(iv) { .into(object : BitmapImageViewTarget(iv) {
override fun setResource(resource: Bitmap?) { override fun setResource(resource: Bitmap?) {
val circularBitmapDrawable = RoundedBitmapDrawableFactory.create( val circularBitmapDrawable = RoundedBitmapDrawableFactory.create(
resources, resources,
resource resource
) )
circularBitmapDrawable.isCircular = true circularBitmapDrawable.isCircular = true
iv.setImageDrawable(circularBitmapDrawable) iv.setImageDrawable(circularBitmapDrawable)
} }
}) })

View File

@ -23,9 +23,9 @@ class SelfSignedGlideModule : GlideModule {
val client = getUnsafeHttpClient().build() val client = getUnsafeHttpClient().build()
registry?.append( registry?.append(
GlideUrl::class.java, GlideUrl::class.java,
InputStream::class.java, InputStream::class.java,
com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader.Factory(client) com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader.Factory(client)
) )
} }
} }

View File

@ -0,0 +1,41 @@
package apps.amine.bou.readerforselfoss.utils.persistence
import android.content.Context
import apps.amine.bou.readerforselfoss.api.selfoss.Source
import apps.amine.bou.readerforselfoss.api.selfoss.Tag
import apps.amine.bou.readerforselfoss.persistence.entities.SourceEntity
import apps.amine.bou.readerforselfoss.persistence.entities.TagEntity
fun TagEntity.toView(): Tag =
Tag(
this.tag,
this.color,
this.unread
)
fun SourceEntity.toView(): Source =
Source(
this.id,
this.title,
this.tags,
this.spout,
this.error,
this.icon
)
fun Source.toEntity(context: Context): SourceEntity =
SourceEntity(
this.id,
this.title,
this.tags,
this.spout,
this.error,
this.getIcon(context)
)
fun Tag.toEntity(): TagEntity =
TagEntity(
this.tag,
this.color,
this.unread
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 551 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 215 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 684 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 986 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 311 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 567 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@ -1,36 +1,37 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <ScrollView xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context="apps.amine.bou.readerforselfoss.AddSourceActivity" tools:context="apps.amine.bou.readerforselfoss.AddSourceActivity">
android:orientation="vertical"> <LinearLayout
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:theme="@style/ToolBarStyle"
app:popupTheme="?attr/toolbarPopupTheme" />
</android.support.design.widget.AppBarLayout>
<android.support.constraint.ConstraintLayout
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_width="match_parent"> android:orientation="vertical">
<android.support.constraint.ConstraintLayout
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:theme="@style/ToolBarStyle"
app:popupTheme="?attr/toolbarPopupTheme" />
</com.google.android.material.appbar.AppBarLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_width="match_parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
@ -119,19 +120,20 @@
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="16dp" android:layout_marginBottom="16dp"
app:layout_constraintVertical_bias="0.0"/> app:layout_constraintVertical_bias="0.0"/>
</android.support.constraint.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
<ProgressBar <ProgressBar
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/progress" android:id="@+id/progress"
app:layout_constraintTop_toTopOf="parent" style="?android:attr/progressBarStyleLarge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintTop_toTopOf="parent"
android:visibility="visible"/> tools:visibility="gone" />
</android.support.constraint.ConstraintLayout> </LinearLayout>
</ScrollView>
</LinearLayout>

View File

@ -30,13 +30,13 @@
app:prompt_view_background_color="?attr/colorAccent" app:prompt_view_background_color="?attr/colorAccent"
app:prompt_view_thanks_display_time_ms="2000"/> app:prompt_view_thanks_display_time_ms="2000"/>
<android.support.design.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/coordLayout" android:id="@+id/coordLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_below="@id/promptView"> android:layout_below="@id/promptView">
<android.support.design.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/intern_coordLayout" android:id="@+id/intern_coordLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -46,18 +46,18 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<android.support.design.widget.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/toolBar" android:id="@+id/toolBar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
app:theme="@style/ToolBarStyle" app:theme="@style/ToolBarStyle"
app:popupTheme="?attr/toolbarPopupTheme" /> app:popupTheme="?attr/toolbarPopupTheme" />
</android.support.design.widget.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<FrameLayout <FrameLayout
android:id="@+id/drawer_layout" android:id="@+id/drawer_layout"
@ -66,7 +66,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<android.support.v4.widget.SwipeRefreshLayout <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout" android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -88,26 +88,28 @@
android:textAppearance="@style/TextAppearance.AppCompat.Headline" android:textAppearance="@style/TextAppearance.AppCompat.Headline"
android:background="@color/transparent" android:background="@color/transparent"
android:visibility="gone" /> android:visibility="gone" />
<android.support.v7.widget.RecyclerView
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView" android:id="@+id/recyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@color/transparent" android:background="@color/transparent"
android:clipToPadding="false" android:clipToPadding="false"
android:paddingBottom="60dp"
android:scrollbars="vertical" android:scrollbars="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior" /> app:layout_behavior="@string/appbar_scrolling_view_behavior" />
</LinearLayout> </LinearLayout>
</android.support.v4.widget.SwipeRefreshLayout> </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
</FrameLayout> </FrameLayout>
</LinearLayout> </LinearLayout>
</android.support.design.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.ashokvarma.bottomnavigation.BottomNavigationBar <com.ashokvarma.bottomnavigation.BottomNavigationBar
android:layout_gravity="bottom" android:layout_gravity="bottom"
android:id="@+id/bottomBar" android:id="@+id/bottomBar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="60dp"/> android:layout_height="60dp"/>
</android.support.design.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
</RelativeLayout> </RelativeLayout>

View File

@ -6,18 +6,18 @@
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:orientation="vertical" android:orientation="vertical"
tools:context="apps.amine.bou.readerforselfoss.LoginActivity"> tools:context="apps.amine.bou.readerforselfoss.LoginActivity">
<android.support.design.widget.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
app:theme="@style/ToolBarStyle" app:theme="@style/ToolBarStyle"
app:popupTheme="?attr/toolbarPopupTheme" /> app:popupTheme="?attr/toolbarPopupTheme" />
</android.support.design.widget.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -45,7 +45,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<android.support.design.widget.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/urlLayout" android:id="@+id/urlLayout"
@ -60,7 +60,7 @@
android:inputType="textUri" android:inputType="textUri"
android:maxLines="1" /> android:maxLines="1" />
</android.support.design.widget.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<Switch <Switch
android:text="@string/withLoginSwitch" android:text="@string/withLoginSwitch"
@ -69,7 +69,7 @@
android:id="@+id/withLogin" android:id="@+id/withLogin"
android:layout_weight="1"/> android:layout_weight="1"/>
<android.support.design.widget.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/loginLayout" android:id="@+id/loginLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -83,9 +83,9 @@
android:inputType="text" android:inputType="text"
android:maxLines="1" /> android:maxLines="1" />
</android.support.design.widget.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<android.support.design.widget.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/passwordLayout" android:id="@+id/passwordLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -99,7 +99,7 @@
android:inputType="textPassword" android:inputType="textPassword"
android:maxLines="1" /> android:maxLines="1" />
</android.support.design.widget.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<Switch <Switch
android:id="@+id/withHttpLogin" android:id="@+id/withHttpLogin"
@ -108,20 +108,21 @@
android:layout_weight="1" android:layout_weight="1"
android:text="@string/withHttpLoginSwitch" /> android:text="@string/withHttpLoginSwitch" />
<android.support.design.widget.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/httpLoginInput" android:id="@+id/httpLoginInput"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:visibility="gone"> android:visibility="gone">
<EditText <EditText
android:inputType="text"
android:id="@+id/httpLoginView" android:id="@+id/httpLoginView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/prompt_http_login" /> android:hint="@string/prompt_http_login" />
</android.support.design.widget.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<android.support.design.widget.TextInputLayout <com.google.android.material.textfield.TextInputLayout
android:id="@+id/httpPasswordInput" android:id="@+id/httpPasswordInput"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@ -133,7 +134,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:hint="@string/prompt_http_password" android:hint="@string/prompt_http_password"
android:inputType="textPassword" /> android:inputType="textPassword" />
</android.support.design.widget.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>
<Switch <Switch
android:id="@+id/withSelfhostedCert" android:id="@+id/withSelfhostedCert"

View File

@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context="apps.amine.bou.readerforselfoss.MainActivity"> tools:context="apps.amine.bou.readerforselfoss.MainActivity">
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,11 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout <com.google.android.material.appbar.AppBarLayout
android:id="@+id/appBarLayout" android:id="@+id/appBarLayout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -13,19 +12,20 @@
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"> app:layout_constraintTop_toTopOf="parent">
<android.support.v7.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/toolBar" android:id="@+id/toolBar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
app:popupTheme="?attr/toolbarPopupTheme" app:popupTheme="?attr/toolbarPopupTheme"
app:theme="@style/ToolBarStyle" /> app:theme="@style/ToolBarStyle" />
</android.support.design.widget.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<android.support.v4.view.ViewPager <androidx.viewpager.widget.ViewPager
android:id="@+id/pager" android:id="@+id/pager"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
android:paddingTop="20dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
@ -37,8 +37,8 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="20dp" android:layout_height="20dp"
android:background="#55000000" android:background="#55000000"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" /> app:layout_constraintRight_toRightOf="parent"
</android.support.constraint.ConstraintLayout> app:layout_constraintTop_toTopOf="@+id/pager" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,35 +1,33 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context="apps.amine.bou.readerforselfoss.SourcesActivity" tools:context="apps.amine.bou.readerforselfoss.SourcesActivity">
xmlns:fab="http://schemas.android.com/apk/res-auto"> <com.google.android.material.appbar.AppBarLayout
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
app:theme="@style/ToolBarStyle" app:theme="@style/ToolBarStyle"
app:popupTheme="?attr/toolbarPopupTheme" /> app:popupTheme="?attr/toolbarPopupTheme" />
</android.support.design.widget.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<android.support.v7.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView" android:id="@+id/recyclerView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:scrollbars="vertical" android:scrollbars="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior"> app:layout_behavior="@string/appbar_scrolling_view_behavior">
</android.support.v7.widget.RecyclerView> </androidx.recyclerview.widget.RecyclerView>
<android.support.design.widget.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab" android:id="@+id/fab"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -44,4 +42,4 @@
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:layout_marginRight="16dp" android:layout_marginRight="16dp"
app:layout_behavior="apps.amine.bou.readerforselfoss.utils.ScrollAwareFABBehavior" /> app:layout_behavior="apps.amine.bou.readerforselfoss.utils.ScrollAwareFABBehavior" />
</android.support.design.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView <androidx.cardview.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:card_view="http://schemas.android.com/apk/res-auto" xmlns:card_view="http://schemas.android.com/apk/res-auto"
@ -18,7 +18,7 @@
card_view:cardUseCompatPadding="true" card_view:cardUseCompatPadding="true"
card_view:layout_constraintBottom_toBottomOf="parent"> card_view:layout_constraintBottom_toBottomOf="parent">
<android.support.constraint.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
@ -34,7 +34,7 @@
app:srcCompat="@drawable/background_splash" app:srcCompat="@drawable/background_splash"
card_view:layout_constraintBottom_toTopOf="@+id/constraintLayout" /> card_view:layout_constraintBottom_toTopOf="@+id/constraintLayout" />
<android.support.constraint.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraintLayout" android:id="@+id/constraintLayout"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -143,7 +143,7 @@
</RelativeLayout> </RelativeLayout>
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</android.support.v7.widget.CardView> </androidx.cardview.widget.CardView>

View File

@ -1,4 +1,4 @@
<android.support.design.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
@ -6,12 +6,12 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:descendantFocusability="blocksDescendants"> android:descendantFocusability="blocksDescendants">
<android.support.v4.widget.NestedScrollView <androidx.core.widget.NestedScrollView
android:id="@+id/nestedScrollView" android:id="@+id/nestedScrollView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
<android.support.constraint.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -70,23 +70,9 @@
app:layout_constraintTop_toBottomOf="@+id/source" app:layout_constraintTop_toBottomOf="@+id/source"
tools:visibility="visible" /> tools:visibility="visible" />
<TextView </androidx.constraintlayout.widget.ConstraintLayout>
android:id="@+id/content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="24dp"
android:paddingBottom="48dp"
android:textColorLink="?attr/colorAccent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/source"
android:visibility="gone"/>
</android.support.constraint.ConstraintLayout>
</android.support.v4.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
<FrameLayout <FrameLayout
android:layout_width="match_parent" android:layout_width="match_parent"
@ -101,16 +87,12 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
android:layout_gravity="bottom" android:layout_gravity="bottom"
app:floatingItemBackground="?attr/colorAccent"
app:floatingMenu="@menu/reader_toolbar" /> app:floatingMenu="@menu/reader_toolbar" />
<android.support.design.widget.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab" android:id="@+id/fab"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_gravity="end|bottom|right" android:layout_gravity="end|bottom|right"
android:layout_marginBottom="16dp" android:layout_marginBottom="16dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
@ -141,4 +123,4 @@
android:progressTint="?attr/colorAccent" /> android:progressTint="?attr/colorAccent" />
</FrameLayout> </FrameLayout>
</android.support.design.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -7,18 +7,11 @@
android:minHeight="88dp"> android:minHeight="88dp">
<android.support.constraint.Guideline
android:id="@+id/guideline4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_begin="99dp" />
<ImageView <ImageView
android:id="@+id/itemImage" android:id="@+id/itemImage"
android:layout_width="88dp" android:layout_width="88dp"
android:layout_height="88dp" android:layout_height="88dp"
app:layout_constraintBottom_toTopOf="@+id/guideline4" app:layout_constraintBottom_toBottomOf="@+id/actionBar"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
@ -26,24 +19,21 @@
android:id="@+id/title" android:id="@+id/title"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginTop="16dp" android:layout_marginTop="8dp"
android:ellipsize="end"
android:fontFamily="sans-serif" android:fontFamily="sans-serif"
android:gravity="start" android:gravity="start"
android:maxLines="3" android:maxLines="3"
android:ellipsize="end"
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:textAllCaps="false" android:textAllCaps="false"
android:textSize="16sp" android:textSize="16sp"
android:textStyle="bold" android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/sourceTitleAndDate"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/itemImage" app:layout_constraintStart_toEndOf="@+id/itemImage"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
tools:text="Titre" /> tools:text="Titre" />
<TextView <TextView
@ -53,13 +43,15 @@
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="16dp"
android:layout_marginStart="16dp" android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:gravity="start" android:gravity="start"
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:textSize="14sp" android:textSize="14sp"
app:layout_constraintBottom_toTopOf="@+id/guideline4" app:layout_constraintBottom_toBottomOf="@+id/actionBar"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0" app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toEndOf="@+id/itemImage" app:layout_constraintStart_toEndOf="@+id/itemImage"
app:layout_constraintTop_toBottomOf="@+id/title"
tools:text="Google Actualité Il y a 5h" /> tools:text="Google Actualité Il y a 5h" />
<RelativeLayout <RelativeLayout
@ -71,8 +63,6 @@
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline4"
app:layout_constraintVertical_bias="1.0"
tools:visibility="visible"> tools:visibility="visible">
<com.like.LikeButton <com.like.LikeButton
@ -125,4 +115,4 @@
</RelativeLayout> </RelativeLayout>
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,15 +1,15 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.AppBarLayout <com.google.android.material.appbar.AppBarLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<android.support.v7.widget.Toolbar <androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar" android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize" android:layout_height="?attr/actionBarSize"
app:theme="@style/ToolBarStyle" app:theme="@style/ToolBarStyle"
app:popupTheme="?attr/toolbarPopupTheme" /> app:popupTheme="?attr/toolbarPopupTheme" />
</android.support.design.widget.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
@ -52,4 +52,4 @@
android:layout_width="34dp" android:layout_width="34dp"
android:layout_height="34dp"/> android:layout_height="34dp"/>
</android.support.constraint.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -6,13 +6,13 @@
android:title="@string/menu_home_search" android:title="@string/menu_home_search"
android:icon="@drawable/ic_action_search" android:icon="@drawable/ic_action_search"
app:showAsAction="ifRoom|collapseActionView" app:showAsAction="ifRoom|collapseActionView"
app:actionViewClass="android.support.v7.widget.SearchView" /> app:actionViewClass="androidx.appcompat.widget.SearchView" />
<item android:id="@+id/readAll" <item android:id="@+id/readAll"
android:icon="@drawable/ic_done_all_white_24dp" android:icon="@drawable/ic_done_all_white_24dp"
android:title="@string/readAll" android:title="@string/readAll"
android:orderInCategory="1" android:orderInCategory="1"
app:showAsAction="ifRoom"/> app:showAsAction="always"/>
<item <item
android:id="@+id/refresh" android:id="@+id/refresh"
@ -20,11 +20,6 @@
android:orderInCategory="99" android:orderInCategory="99"
android:title="@string/menu_home_refresh" /> android:title="@string/menu_home_refresh" />
<item
android:id="@+id/action_share_the_app"
android:orderInCategory="102"
android:title="@string/menu_share_the_app" />
<item android:id="@+id/action_disconnect" <item android:id="@+id/action_disconnect"
android:title="@string/action_disconnect" android:title="@string/action_disconnect"
android:orderInCategory="104" android:orderInCategory="104"

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/unsave"
android:icon="@drawable/heart_on"
android:title="@string/remove_to_favs_reader"
android:visible="true"
app:showAsAction="ifRoom" />
<item
android:id="@+id/save"
android:icon="@drawable/heart_off"
android:title="@string/add_to_favs_reader"
android:visible="true"
app:showAsAction="ifRoom" />
</menu>

View File

@ -1,8 +1,13 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<menu xmlns:tools="http://schemas.android.com/tools" <menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/unread_action"
android:icon="@drawable/ic_fiber_new"
android:title="@string/unmark"
app:showAsAction="ifRoom" />
<item <item
android:id="@+id/more_action" android:id="@+id/more_action"
android:icon="@drawable/ic_chrome_reader_mode" android:icon="@drawable/ic_chrome_reader_mode"

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/clear"
android:icon="@drawable/ic_history"
android:title="@string/drawer_action_clear"
app:showAsAction="ifRoom" />
</menu>

View File

@ -64,7 +64,6 @@
<string name="switch_unread_count">"Display the unread count as a badge for the bottom bar."</string> <string name="switch_unread_count">"Display the unread count as a badge for the bottom bar."</string>
<string name="switch_unread_count_title">"Display unread count"</string> <string name="switch_unread_count_title">"Display unread count"</string>
<string name="display_all_counts_title">"Display count for favorite and read"</string> <string name="display_all_counts_title">"Display count for favorite and read"</string>
<string name="menu_share_the_app">"Invite friends"</string>
<string name="invitation_title">"Try this app for your Selfoss RSS feeds !"</string> <string name="invitation_title">"Try this app for your Selfoss RSS feeds !"</string>
<string name="invitation_message">"I use this app for my Selfoss RSS feeds. You may like it too !"</string> <string name="invitation_message">"I use this app for my Selfoss RSS feeds. You may like it too !"</string>
<string name="invitation_cta">"Try the app"</string> <string name="invitation_cta">"Try the app"</string>
@ -118,19 +117,7 @@
<string name="base_url_error">There was an issue when trying to communicate with your Selfoss Instance. If the issue persists, please get in touch with me.</string> <string name="base_url_error">There was an issue when trying to communicate with your Selfoss Instance. If the issue persists, please get in touch with me.</string>
<string name="pref_header_theme">Themes</string> <string name="pref_header_theme">Themes</string>
<string name="default_theme">Default</string> <string name="default_theme">Default</string>
<string name="teal_orange_theme">Teal/Orange/Light</string>
<string name="cyan_pink_theme">Cyan/Pink/Light</string>
<string name="grey_orange_theme">Grey/Orange/Light</string>
<string name="blue_amber_theme">Blue/Amber/Light</string>
<string name="indigo_pink_theme">Indigo/Pink/Light</string>
<string name="red_teal_theme">Red/Teal/Light</string>
<string name="teal_orange_dark_theme">Teal/Orange/Dark</string>
<string name="cyan_pink_dark_theme">Cyan/Pink/Dark</string>
<string name="default_dark_theme">Default/Dark</string> <string name="default_dark_theme">Default/Dark</string>
<string name="grey_orange_dark_theme">Grey/Orange/Dark</string>
<string name="blue_amber_dark_theme">Blue/Amber/Dark</string>
<string name="indigo_pink_dark_theme">Indigo/Pink/Dark</string>
<string name="red_teal_dark_theme">Red/Teal/Dark</string>
<string name="pref_header_debug">Debug</string> <string name="pref_header_debug">Debug</string>
<string name="login_debug_title">Activate to log login errors</string> <string name="login_debug_title">Activate to log login errors</string>
<string name="login_debug_on">Any error on the login page will be logged</string> <string name="login_debug_on">Any error on the login page will be logged</string>
@ -140,6 +127,7 @@
<string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string> <string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string>
<string name="pref_selfoss_category">Selfoss Api</string> <string name="pref_selfoss_category">Selfoss Api</string>
<string name="pref_api_items_number_title">Loaded items number</string> <string name="pref_api_items_number_title">Loaded items number</string>
<string name="pref_hidden_tags">Hidden Tags</string>
<string name="read_debug_title">Read articles appearing as unread ?</string> <string name="read_debug_title">Read articles appearing as unread ?</string>
<string name="read_debug_off">No log when marking an item as read</string> <string name="read_debug_off">No log when marking an item as read</string>
<string name="read_debug_on">Api calls will be logged when marking an article as read</string> <string name="read_debug_on">Api calls will be logged when marking an article as read</string>
@ -158,8 +146,27 @@
<string name="reader_action_more">Read more</string> <string name="reader_action_more">Read more</string>
<string name="reader_action_open">Open in browser</string> <string name="reader_action_open">Open in browser</string>
<string name="reader_action_share">Share</string> <string name="reader_action_share">Share</string>
<string name="pref_switch_actions_pager_scroll">Mark articles as read when swiping between articles.</string> <string name="pref_switch_actions_pager_scroll_on">Mark articles as read when swiping between articles.</string>
<string name="prefer_webview_in_article_viewer_title">Use a webview in the article viewer.</string> <string name="add_to_favs_reader">Add to favorites</string>
<string name="prefer_webview_in_article_viewer_on">Using the webview for the content of the article viewer. Urls, gifs and images will be better handled, but may be slower.</string> <string name="remove_to_favs_reader">Remove from favorites</string>
<string name="prefer_webview_in_article_viewer_off">The content of the article will be displayed as simple text with links and images. Urls may not work, and gifs won\'t load.</string> <string name="pref_content_reader_font_size">Article reader content font size</string>
<string name="pref_header_viewer">Article viewer</string>
<string name="refresh_dialog_message">This will refresh your Selfoss instance.</string>
<string name="markall_dialog_message">This will mark all the items as read.</string>
<string name="pref_switch_actions_pager_scroll">Mark as read on swipe</string>
<string name="pref_switch_actions_pager_scroll_off">Don\'t mark articles as read when swiping.</string>
<string name="gdpr_dialog_message">The app does not collect any personal data. Every analytics tools were removed. Crash reports sending is now optional, as is the debug logging. Keep in mind that debugging and crash reports are essential for the app development (You can configure everything in Settings &gt; Debug).</string>
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
<string name="crash_dialog_text">Something went wrong. Please send the report to the developer.</string>
<string name="crash_dialog_comment">You can add any helpful details in the comment bellow. Don\'t include any personal data in your comment. You could send me and email with your debug id, and I\'ll keep you posted when the issue is resolved.</string>
<string name="pref_acra_user_email">Contact email</string>
<string name="pref_acra_user_email_summary">Add an email so I can contact you about the crash reports you send.</string>
<string name="pref_acra_alwaysaccept">Automatically send crash reports</string>
<string name="pref_acra_alwaysaccept_enabled">Will send crash reports automatically</string>
<string name="pref_acra_alwaysaccept_disabled">Will ask everytime when sending crash reports.</string>
<string name="pref_debug_crash_reports">Crash reports</string>
<string name="pref_debug_debug_logs">Debug logging (these will be sent without a dialog)</string>
<string name="acra_login">Enable logging</string>
<string name="drawer_item_hidden_tags">Hidden Tags</string>
<string name="unmark">Mark item as unread</string>
</resources> </resources>

View File

@ -64,7 +64,6 @@
<string name="switch_unread_count">"Display the unread count as a badge for the bottom bar."</string> <string name="switch_unread_count">"Display the unread count as a badge for the bottom bar."</string>
<string name="switch_unread_count_title">"Display unread count"</string> <string name="switch_unread_count_title">"Display unread count"</string>
<string name="display_all_counts_title">"Display count for favorite and read"</string> <string name="display_all_counts_title">"Display count for favorite and read"</string>
<string name="menu_share_the_app">"Invite friends"</string>
<string name="invitation_title">"Try this app for your Selfoss RSS feeds !"</string> <string name="invitation_title">"Try this app for your Selfoss RSS feeds !"</string>
<string name="invitation_message">"I use this app for my Selfoss RSS feeds. You may like it too !"</string> <string name="invitation_message">"I use this app for my Selfoss RSS feeds. You may like it too !"</string>
<string name="invitation_cta">"Try the app"</string> <string name="invitation_cta">"Try the app"</string>
@ -118,19 +117,7 @@
<string name="base_url_error">There was an issue when trying to communicate with your Selfoss Instance. If the issue persists, please get in touch with me.</string> <string name="base_url_error">There was an issue when trying to communicate with your Selfoss Instance. If the issue persists, please get in touch with me.</string>
<string name="pref_header_theme">Themes</string> <string name="pref_header_theme">Themes</string>
<string name="default_theme">Default</string> <string name="default_theme">Default</string>
<string name="teal_orange_theme">Teal/Orange/Light</string>
<string name="cyan_pink_theme">Cyan/Pink/Light</string>
<string name="grey_orange_theme">Grey/Orange/Light</string>
<string name="blue_amber_theme">Blue/Amber/Light</string>
<string name="indigo_pink_theme">Indigo/Pink/Light</string>
<string name="red_teal_theme">Red/Teal/Light</string>
<string name="teal_orange_dark_theme">Teal/Orange/Dark</string>
<string name="cyan_pink_dark_theme">Cyan/Pink/Dark</string>
<string name="default_dark_theme">Default/Dark</string> <string name="default_dark_theme">Default/Dark</string>
<string name="grey_orange_dark_theme">Grey/Orange/Dark</string>
<string name="blue_amber_dark_theme">Blue/Amber/Dark</string>
<string name="indigo_pink_dark_theme">Indigo/Pink/Dark</string>
<string name="red_teal_dark_theme">Red/Teal/Dark</string>
<string name="pref_header_debug">Debug</string> <string name="pref_header_debug">Debug</string>
<string name="login_debug_title">Activate to log login errors</string> <string name="login_debug_title">Activate to log login errors</string>
<string name="login_debug_on">Any error on the login page will be logged</string> <string name="login_debug_on">Any error on the login page will be logged</string>
@ -140,6 +127,7 @@
<string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string> <string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string>
<string name="pref_selfoss_category">Selfoss Api</string> <string name="pref_selfoss_category">Selfoss Api</string>
<string name="pref_api_items_number_title">Loaded items number</string> <string name="pref_api_items_number_title">Loaded items number</string>
<string name="pref_hidden_tags">Hidden Tags</string>
<string name="read_debug_title">Read articles appearing as unread ?</string> <string name="read_debug_title">Read articles appearing as unread ?</string>
<string name="read_debug_off">No log when marking an item as read</string> <string name="read_debug_off">No log when marking an item as read</string>
<string name="read_debug_on">Api calls will be logged when marking an article as read</string> <string name="read_debug_on">Api calls will be logged when marking an article as read</string>
@ -158,8 +146,27 @@
<string name="reader_action_more">Read more</string> <string name="reader_action_more">Read more</string>
<string name="reader_action_open">Open in browser</string> <string name="reader_action_open">Open in browser</string>
<string name="reader_action_share">Share</string> <string name="reader_action_share">Share</string>
<string name="pref_switch_actions_pager_scroll">Mark articles as read when swiping between articles.</string> <string name="pref_switch_actions_pager_scroll_on">Mark articles as read when swiping between articles.</string>
<string name="prefer_webview_in_article_viewer_title">Use a webview in the article viewer.</string> <string name="add_to_favs_reader">Add to favorites</string>
<string name="prefer_webview_in_article_viewer_on">Using the webview for the content of the article viewer. Urls, gifs and images will be better handled, but may be slower.</string> <string name="remove_to_favs_reader">Remove from favorites</string>
<string name="prefer_webview_in_article_viewer_off">The content of the article will be displayed as simple text with links and images. Urls may not work, and gifs won\'t load.</string> <string name="pref_content_reader_font_size">Article reader content font size</string>
<string name="pref_header_viewer">Article viewer</string>
<string name="refresh_dialog_message">This will refresh your Selfoss instance.</string>
<string name="markall_dialog_message">This will mark all the items as read.</string>
<string name="pref_switch_actions_pager_scroll">Mark as read on swipe</string>
<string name="pref_switch_actions_pager_scroll_off">Don\'t mark articles as read when swiping.</string>
<string name="gdpr_dialog_message">The app does not collect any personal data. Every analytics tools were removed. Crash reports sending is now optional, as is the debug logging. Keep in mind that debugging and crash reports are essential for the app development (You can configure everything in Settings &gt; Debug).</string>
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
<string name="crash_dialog_text">Something went wrong. Please send the report to the developer.</string>
<string name="crash_dialog_comment">You can add any helpful details in the comment bellow. Don\'t include any personal data in your comment. You could send me and email with your debug id, and I\'ll keep you posted when the issue is resolved.</string>
<string name="pref_acra_user_email">Contact email</string>
<string name="pref_acra_user_email_summary">Add an email so I can contact you about the crash reports you send.</string>
<string name="pref_acra_alwaysaccept">Automatically send crash reports</string>
<string name="pref_acra_alwaysaccept_enabled">Will send crash reports automatically</string>
<string name="pref_acra_alwaysaccept_disabled">Will ask everytime when sending crash reports.</string>
<string name="pref_debug_crash_reports">Crash reports</string>
<string name="pref_debug_debug_logs">Debug logging (these will be sent without a dialog)</string>
<string name="acra_login">Enable logging</string>
<string name="drawer_item_hidden_tags">Hidden Tags</string>
<string name="unmark">Mark item as unread</string>
</resources> </resources>

View File

@ -1,165 +1,172 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.com--> <!--Generated by crowdin.com-->
<resources xmlns:tools="http://schemas.android.com/tools"> <resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name">"Reader for Selfoss"</string> <string name="app_name">"Lector per a Selfoss"</string>
<string name="title_activity_login">"Log in"</string> <string name="title_activity_login">"Inicia la sessió"</string>
<string name="prompt_password">"Password"</string> <string name="prompt_password">"Contrasenya"</string>
<string name="prompt_http_password">"HTTP Password"</string> <string name="prompt_http_password">"Contrasenya HTTP"</string>
<string name="action_sign_in">"Go"</string> <string name="action_sign_in">"Vés-hi"</string>
<string name="error_invalid_password">"Password not long enough"</string> <string name="error_invalid_password">"La contrasenya és massa curta"</string>
<string name="error_field_required">"Field required"</string> <string name="error_field_required">"Camp necessari"</string>
<string name="prompt_url">"Url"</string> <string name="prompt_url">"URL"</string>
<string name="withLoginSwitch">"Login required ?"</string> <string name="withLoginSwitch">"Autenticació (si és necessària)"</string>
<string name="withHttpLoginSwitch">"HTTP Login required ?"</string> <string name="withHttpLoginSwitch">"Autenticació HTTP (si és necessària)"</string>
<string name="login_url_problem">"Oops. You may need to add a \"/\" at the end of the url."</string> <string name="login_url_problem">"Pot ser que falti una \"/\" al final de l'url."</string>
<string name="prompt_login">"Username"</string> <string name="prompt_login">"Nom d'usuari"</string>
<string name="prompt_http_login">"HTTP Username"</string> <string name="prompt_http_login">"Nom d'usuari HTTP"</string>
<string name="label_share">"Share"</string> <string name="label_share">"Comparteix"</string>
<string name="readAll">"Read all"</string> <string name="readAll">"Llegeix-ho tot"</string>
<string name="action_disconnect">"Disconnect"</string> <string name="action_disconnect">"Desconnecta't"</string>
<string name="title_activity_settings">"Settings"</string> <string name="title_activity_settings">"Configuració"</string>
<string name="pref_header_general">"General"</string> <string name="pref_header_general">"General"</string>
<string name="pref_switch_actions_tap_title">"Tap action on the articles"</string> <string name="pref_switch_actions_tap_title">"Fer un toc als articles"</string>
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string> <string name="add_source_hint_tags">"Etiqueta1, Etiqueta2, Etiqueta3"</string>
<string name="add_source_hint_url">"Link"</string> <string name="add_source_hint_url">"Enllaç"</string>
<string name="add_source_hint_name">"Name"</string> <string name="add_source_hint_name">"Nom"</string>
<string name="add_source">"Add a source"</string> <string name="add_source">"Afegeix una font"</string>
<string name="add_source_save">"Save"</string> <string name="add_source_save">"Desa"</string>
<string name="wrong_infos">"Check your details again."</string> <string name="wrong_infos">"Torneu a comprovar la informació."</string>
<string name="all_posts_not_read">"All posts weren't read"</string> <string name="all_posts_not_read">"No s'han llegit totes les publicacions"</string>
<string name="all_posts_read">"All posts were read"</string> <string name="all_posts_read">"S'han llegit totes les publicacions"</string>
<string name="cant_get_favs">"Can't get favorites"</string> <string name="cant_get_favs">"No es poden obtenir preferits"</string>
<string name="cant_get_new_elements">"Can't get new articles"</string> <string name="cant_get_new_elements">"No es pot accedir als articles nous"</string>
<string name="cant_get_read">"Can't get read articles"</string> <string name="cant_get_read">"No es poden llegir els articles"</string>
<string name="nothing_here">"Nothing here"</string> <string name="nothing_here">"No hi ha res"</string>
<string name="tab_new">"New"</string> <string name="tab_new">"Nou"</string>
<string name="tab_read">"All"</string> <string name="tab_read">"Tot"</string>
<string name="tab_favs">"Favorites"</string> <string name="tab_favs">"Preferits"</string>
<string name="action_about">"About"</string> <string name="action_about">"Quant a"</string>
<string name="marked_as_read">"Item read"</string> <string name="marked_as_read">"Element llegit"</string>
<string name="undo_string">"Undo"</string> <string name="undo_string">"Desfés"</string>
<string name="addStringNoUrl">"Log in to add sources."</string> <string name="addStringNoUrl">"Inicieu la sessió per afegir fonts."</string>
<string name="cant_get_sources">"Can't get sources list."</string> <string name="cant_get_sources">"No es pot obtenir la llista de fonts."</string>
<string name="cant_create_source">"Can't create source."</string> <string name="cant_create_source">"No es pot crear la font."</string>
<string name="cant_get_spouts">"Can't get spouts list."</string> <string name="cant_get_spouts">"No es pot obtenir la llista de canals."</string>
<string name="form_not_complete">"The form is not complete"</string> <string name="form_not_complete">"El formulari no està complet"</string>
<string name="pref_header_links">"Links"</string> <string name="pref_header_links">"Enllaços"</string>
<string name="issue_tracker_link">"Issue Tracker"</string> <string name="issue_tracker_link">"Detector de problemes"</string>
<string name="issue_tracker_summary">"Report a bug or ask for a new feature"</string> <string name="issue_tracker_summary">"Informa d'un error o pregunta sobre funcions noves"</string>
<string name="warning_wrong_url">"WARNING"</string> <string name="warning_wrong_url">"ADVERTÈNCIA"</string>
<string name="pref_switch_card_view_title">"Card View"</string> <string name="pref_switch_card_view_title">"Visualització de targeta"</string>
<string name="cant_mark_favortie">"Can't mark article as favorite"</string> <string name="cant_mark_favortie">"No es pot marcar l'article com a preferit"</string>
<string name="cant_unmark_favortie">"Can't remove item from favorite"</string> <string name="cant_unmark_favortie">"No es pot treure l'element de preferits"</string>
<string name="share">"Share"</string> <string name="share">"Comparteix"</string>
<string name="rating_prompt_title">"Enjoying the app ?"</string> <string name="rating_prompt_title">"Us agrada l'aplicació?"</string>
<string name="rating_prompt_yes">"Yes !"</string> <string name="rating_prompt_yes">"Sí."</string>
<string name="rating_prompt_no">"Not really …"</string> <string name="rating_prompt_no">"No gaire…"</string>
<string name="rating_prompt_feedback_title">"Can you tell us why ?"</string> <string name="rating_prompt_feedback_title">"Ens podeu dir per què?"</string>
<string name="rating_prompt_feedback_yes">"OK !"</string> <string name="rating_prompt_feedback_yes">"D'acord."</string>
<string name="rating_prompt_feedback_no">"Not now."</string> <string name="rating_prompt_feedback_no">"Ara no."</string>
<string name="rating_prompt_rating_title">"Great ! Can you rate us on the Store ?"</string> <string name="rating_prompt_rating_title">"Perfecte! Ens podeu puntuar a la Botiga?"</string>
<string name="rating_prompt_rating_yes">"Sure !"</string> <string name="rating_prompt_rating_yes">"Sí."</string>
<string name="rating_prompt_rating_no">"Not right now."</string> <string name="rating_prompt_rating_no">"Ara no."</string>
<string name="rating_prompt_thanks">"Thanks, your feedback help enhance the app !"</string> <string name="rating_prompt_thanks">"Gràcies. La vostra opinió ens ajuda a millorar l'aplicació."</string>
<string name="switch_unread_count">"Display the unread count as a badge for the bottom bar."</string> <string name="switch_unread_count">"Mostra el recompte d'articles no llegits amb un distintiu a la barra inferior."</string>
<string name="switch_unread_count_title">"Display unread count"</string> <string name="switch_unread_count_title">"Recompte d'articles no llegits"</string>
<string name="display_all_counts_title">"Display count for favorite and read"</string> <string name="display_all_counts_title">"Recompte d'articles llegits i preferits"</string>
<string name="menu_share_the_app">"Invite friends"</string> <string name="invitation_title">"Prova aquesta aplicació per als canals RSS de Selfoss."</string>
<string name="invitation_title">"Try this app for your Selfoss RSS feeds !"</string> <string name="invitation_message">"Jo utilitzo aquesta aplicació per llegir els canals RSS de Selfoss. Segur que a tu també t'agrada!"</string>
<string name="invitation_message">"I use this app for my Selfoss RSS feeds. You may like it too !"</string> <string name="invitation_cta">"Prova l'aplicació"</string>
<string name="invitation_cta">"Try the app"</string> <string name="text_wrong_url">"Sembla que esteu utilitzant un URL no vàlid. Assegureu-vos que és correcte, i si el problema persisteix, poseu-vos en contacte amb mi (a través de l'enllaç de contacte que hi ha a la Botiga). Tingueu en compte que per utilitzar aquesta aplicació cal que també utilitzeu Selfoss. Si no, no podreu accedir a canals RSS."</string>
<string name="text_wrong_url">"You seem to be trying to use an invalid URL. Make sure it is correct, and if the problem persists, contact me (via the store contact link). Please note that the app needs you to be using Selfoss. You can't access RSS feeds without it."</string> <string name="pref_general_internal_browser_title">"Obre els enllaços dins de l'aplicació"</string>
<string name="pref_general_internal_browser_title">"Open links inside the app"</string> <string name="pref_general_internal_browser_on">"Els articles s'obriran dins de l'aplicació"</string>
<string name="pref_general_internal_browser_on">"Articles will open inside the app"</string> <string name="pref_general_internal_browser_off">"Els articles s'obriran amb el navegador predeterminat"</string>
<string name="pref_general_internal_browser_off">"Articles will open with your default browser"</string> <string name="prefer_article_viewer_title">"Obre el visualitzador d'articles"</string>
<string name="prefer_article_viewer_title">"Use the article viewer"</string> <string name="prefer_article_viewer_on">"S'obrirà el visualitzador d'articles en lloc del navegador intern"</string>
<string name="prefer_article_viewer_on">"Will use the article viewer instead of the internal browser"</string> <string name="prefer_article_viewer_off">"S'obrirà el navegador intern en lloc del visualitzador d'articles"</string>
<string name="prefer_article_viewer_off">"Will use the internal browser instead of the article viewer"</string> <string name="pref_general_category_links">"Gestió d'enllaços"</string>
<string name="pref_general_category_links">"Link handling"</string> <string name="pref_general_category_displaying">"Visualització"</string>
<string name="pref_general_category_displaying">"Displaying"</string> <string name="pref_general_category_actions">"Accions"</string>
<string name="pref_general_category_actions">"Actions"</string> <string name="pref_switch_card_view_on">"Els articles es mostraran com a targetes"</string>
<string name="pref_switch_card_view_on">"The articles will be displayed as cards"</string> <string name="pref_switch_card_view_off">"Els articles es mostraran en forma de llista"</string>
<string name="pref_switch_card_view_off">"The articles will be displayed as a list"</string> <string name="pref_switch_actions_tap_on">"Mostra la barra d'acció sota l'article"</string>
<string name="pref_switch_actions_tap_on">"Displays the action bar under the article"</string> <string name="pref_switch_actions_tap_off">"En seleccionar un article, s'obrirà al navegador seleccionat"</string>
<string name="pref_switch_actions_tap_off">"When selecting an article it will open in your selected browser"</string> <string name="menu_home_refresh">"Actualitza l'accés remot"</string>
<string name="menu_home_refresh">"Update remote"</string> <string name="refresh_success_response">"S'ha actualitzat el remot. Torneu a carregar la llista d'articles"</string>
<string name="refresh_success_response">"The remote is updated, you can now reload the articles list"</string> <string name="refresh_failer_message">"L'actualització no ha funcionat. Torneu a provar-ho més tard o consulteu els registres de Selfoss."</string>
<string name="refresh_failer_message">"The update didn't work, try again later, or check your selfoss logs."</string> <string name="refresh_in_progress">"S'està actualitzant"</string>
<string name="refresh_in_progress">"Refresh in progress"</string> <string name="new_apk_available_title">"Hi ha un nou APK disponible."</string>
<string name="new_apk_available_title">"A new APK is available."</string> <string name="new_apk_available_message">"Podeu baixar l'APK nou des del dipòsit oficial."</string>
<string name="new_apk_available_message">"A new APK is available to download on the official repository."</string> <string name="new_apk_available_get">"Baixa-ho ara"</string>
<string name="new_apk_available_get">"Download now"</string> <string name="new_apk_available_no">"Ignora la versió"</string>
<string name="new_apk_available_no">"Ignore version"</string> <string name="intro_hello_title">"Hola!"</string>
<string name="intro_hello_title">"Hi there !"</string> <string name="intro_hello_message">"Gràcies per baixar l'aplicació!"</string>
<string name="intro_hello_message">"Thanks for downloading the app !"</string> <string name="intro_needs_selfoss_title">"Abans de començar…"</string>
<string name="intro_needs_selfoss_title">"Before you start…"</string> <string name="intro_needs_selfoss_message">"No podeu utilitzar l'aplicació sense una instància de Selfoss."</string>
<string name="intro_needs_selfoss_message">"You can't use the app without a Selfoss instance."</string> <string name="intro_needs_selfoss_link">"Què és Selfoss?"</string>
<string name="intro_needs_selfoss_link">"What is Selfoss ?"</string> <string name="intro_all_set_title">"Tot a punt!"</string>
<string name="intro_all_set_title">"All set !"</string> <string name="intro_all_set_message">"Ja podeu començar a utilitzar l'aplicació. Configureu l'aplicació des de la pàgina Configuració. Allà hi trobareu enllaços que us poden ser útils."</string>
<string name="intro_all_set_message">"You are ready to use the app. Don't forget to go to the settings page to configure your app, and where you'll find some useful links."</string> <string name="card_height_title">Alçada completa de les targetes</string>
<string name="card_height_title">Full height cards</string> <string name="card_height_on">L\'alçada de les targetes s\'ajustarà al seu contingut</string>
<string name="card_height_on">Cards height will adjust to its content</string> <string name="card_height_off">L\'alçada de les targetes serà fixa</string>
<string name="card_height_off">Card height will be fixed</string> <string name="source_code">Codi font</string>
<string name="source_code">Source code</string> <string name="cant_mark_read">No es pot marcar l\'article com a llegit</string>
<string name="cant_mark_read">Can\'t mark article as read</string> <string name="drawer_error_loading_tags">S\'ha produït un error en carregar les etiquetes</string>
<string name="drawer_error_loading_tags">Error loading tags…</string> <string name="drawer_error_loading_sources">S\'ha produït un error en carregar les fonts</string>
<string name="drawer_error_loading_sources">Error loading sources</string> <string name="drawer_item_filters">Filtres</string>
<string name="drawer_item_filters">Filters</string> <string name="drawer_action_clear">Esborra</string>
<string name="drawer_action_clear">clear</string> <string name="drawer_item_tags">Etiquetes</string>
<string name="drawer_item_tags">Tags</string> <string name="drawer_item_sources">Fonts</string>
<string name="drawer_item_sources">Sources</string> <string name="drawer_action_edit">Edita</string>
<string name="drawer_action_edit">edit</string> <string name="cache_drawer_error" tools:keep="@string/cache_drawer_error">La informació del calaix no s\'ha pogut emmagatzemar a la memòria cau</string>
<string name="cache_drawer_error" tools:keep="@string/cache_drawer_error">Couldn\'t cache your drawer data</string> <string name="no_tags_loaded">No s\'ha carregat cap etiqueta</string>
<string name="no_tags_loaded">No tags loaded</string> <string name="no_sources_loaded">No s\'ha carregat cap font</string>
<string name="no_sources_loaded">No sources loaded</string> <string name="drawer_loading">S\'està carregant…</string>
<string name="drawer_loading">Loading …</string> <string name="menu_home_search">Cerca</string>
<string name="menu_home_search">Search</string> <string name="can_delete_source">No es pot suprimir la font</string>
<string name="can_delete_source">Can\'t delete the source…</string> <string name="base_url_error">S\'ha produït un error en comunicar-se amb la instància de Selfoss. Si el problema persisteix, posa\'t en contacte amb mi.</string>
<string name="base_url_error">There was an issue when trying to communicate with your Selfoss Instance. If the issue persists, please get in touch with me.</string> <string name="pref_header_theme">Temes</string>
<string name="pref_header_theme">Themes</string> <string name="default_theme">Predeterminat</string>
<string name="default_theme">Default</string> <string name="default_dark_theme">Predeterminat/Fosc</string>
<string name="teal_orange_theme">Teal/Orange/Light</string> <string name="pref_header_debug">Depuració</string>
<string name="cyan_pink_theme">Cyan/Pink/Light</string> <string name="login_debug_title">Registra els errors d\'inici de sessió</string>
<string name="grey_orange_theme">Grey/Orange/Light</string> <string name="login_debug_on">Es registraran tots els errors que es produeixin a la pàgina d\'inici de sessió</string>
<string name="blue_amber_theme">Blue/Amber/Light</string> <string name="login_debug_off">No es registrarà cap error que es produeixi a la pàgina d\'inici de sessió</string>
<string name="indigo_pink_theme">Indigo/Pink/Light</string> <string name="login_menu_debug">Depuració</string>
<string name="red_teal_theme">Red/Teal/Light</string> <string name="self_hosted_cert_switch">Utilitzeu un certificat autoallotjat?</string>
<string name="teal_orange_dark_theme">Teal/Orange/Dark</string> <string name="self_signed_cert_warning">Per raons de seguretat, els certificats autosignats no seran compatibles per defecte. En activar aquesta opció, sereu responsable de qualsevol problema de seguretat que es pugui produir.</string>
<string name="cyan_pink_dark_theme">Cyan/Pink/Dark</string> <string name="pref_selfoss_category">API de Selfoss</string>
<string name="default_dark_theme">Default/Dark</string> <string name="pref_api_items_number_title">Nombre d\'elements carregats</string>
<string name="grey_orange_dark_theme">Grey/Orange/Dark</string> <string name="pref_hidden_tags">Hidden Tags</string>
<string name="blue_amber_dark_theme">Blue/Amber/Dark</string> <string name="read_debug_title">Voleu llegir els articles que apareixen com a no llegits?</string>
<string name="indigo_pink_dark_theme">Indigo/Pink/Dark</string> <string name="read_debug_off">No es registraran quan es marquen elements com a llegits</string>
<string name="red_teal_dark_theme">Red/Teal/Dark</string> <string name="read_debug_on">Les crides de l\'API es registraran en marcar un article com a llegit</string>
<string name="pref_header_debug">Debug</string> <string name="summary_debug_identifier">Identificador de depuració</string>
<string name="login_debug_title">Activate to log login errors</string> <string name="unique_id_to_clipboard">S\'ha copiat l\'identificador al porta-retalls</string>
<string name="login_debug_on">Any error on the login page will be logged</string> <string name="display_header_drawer_summary">Mostra una capçalera amb la instància URL de Selfoss al panell lateral.</string>
<string name="login_debug_off">No log on the login page</string> <string name="display_header_drawer_title">Capçalera de menú</string>
<string name="login_menu_debug">Debug</string> <string name="login_everything_title">Registra totes les crides de l\'API</string>
<string name="self_hosted_cert_switch">Using a self hosted certificate ?</string> <string name="login_everything_on">Aquesta acció registrarà totes les crides de l\'API per als programadors.</string>
<string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string> <string name="login_everything_off">No es registrarà cap crida de l\'API</string>
<string name="pref_selfoss_category">Selfoss Api</string> <string name="pref_general_infinite_loading_title">Carrega articles en desplaçar</string>
<string name="pref_api_items_number_title">Loaded items number</string> <string name="translation">Traducció</string>
<string name="read_debug_title">Read articles appearing as unread ?</string> <string name="cant_open_invalid_url">L\'element URL no és vàlid. Estic intentant solucionar aquest problema perquè l\'aplicació no falli.</string>
<string name="read_debug_off">No log when marking an item as read</string> <string name="drawer_report_bug">Informa d\'un error</string>
<string name="read_debug_on">Api calls will be logged when marking an article as read</string> <string name="items_number_should_be_number">El nombre d\'elements ha de ser enter.</string>
<string name="summary_debug_identifier">Debug identifier</string> <string name="reader_action_more">Més informació</string>
<string name="unique_id_to_clipboard">Identifier copied to your clipboard</string> <string name="reader_action_open">Obre al navegador</string>
<string name="display_header_drawer_summary">Display a header with the selfoss instance url on the lateral drawer.</string> <string name="reader_action_share">Comparteix</string>
<string name="display_header_drawer_title">Account header</string> <string name="pref_switch_actions_pager_scroll_on">Es marcaran els articles com a llegits en lliscar el dit d\'un article a l\'altre.</string>
<string name="login_everything_title">Logging every api calls</string> <string name="add_to_favs_reader">Afegeix als preferits</string>
<string name="login_everything_on">This will log every api call for debug purpose.</string> <string name="remove_to_favs_reader">Suprimeix dels preferits</string>
<string name="login_everything_off">No api call will be logged</string> <string name="pref_content_reader_font_size">Mida de la lletra del lector darticles</string>
<string name="pref_general_infinite_loading_title">Load more articles on scroll</string> <string name="pref_header_viewer">Visualitzador d\'articles</string>
<string name="translation">Translation</string> <string name="refresh_dialog_message">Aquesta acció actualitzarà la vostra instància de Selfoss.</string>
<string name="cant_open_invalid_url">The item url is invalid. I\'m looking into solving this issue so the app won\'t crash.</string> <string name="markall_dialog_message">Aquesta acció marcarà els elements com a llegits.</string>
<string name="drawer_report_bug">Report a bug</string> <string name="pref_switch_actions_pager_scroll">Marca com a llegit en lliscar el dit</string>
<string name="items_number_should_be_number">The items number should be an integer.</string> <string name="pref_switch_actions_pager_scroll_off">No es marcaran els articles com a llegits en lliscar el dit d\'un article a l\'altre.</string>
<string name="reader_action_more">Read more</string> <string name="gdpr_dialog_message">Aquesta aplicació no recull cap dada personal. S\'han suprimit totes les eines d\'anàlisi. A partir d\'ara, l\'enviament d\'informes és opcional, així com el registre de depuració d\'errors. Recordeu que la depuració i els informes d\'error són essencials per al desenvolupament de l\'aplicació (Ho podeu configurar tot a Configuració &gt; Depura).</string>
<string name="reader_action_open">Open in browser</string> <string name="gdpr_dialog_title">Aquesta aplicació no comparteix cap dada personal vostra.</string>
<string name="reader_action_share">Share</string> <string name="crash_dialog_text">Alguna cosa ha anat malament. Envieu l\'informe al desenvolupador.</string>
<string name="pref_switch_actions_pager_scroll">Mark articles as read when swiping between articles.</string> <string name="crash_dialog_comment">Podeu afegir informació útil en la secció de comentaris. No incloeu cap dada personal en el vostre comentari. També em podeu enviar un correu electrònic amb l\'identificador de depuració i us ho faré saber quan el problema s\'hagi resolt.</string>
<string name="prefer_webview_in_article_viewer_title">Use a webview in the article viewer.</string> <string name="pref_acra_user_email">Correu electrònic de contacte</string>
<string name="prefer_webview_in_article_viewer_on">Using the webview for the content of the article viewer. Urls, gifs and images will be better handled, but may be slower.</string> <string name="pref_acra_user_email_summary">Afegiu una adreça de correu electrònic per tal que pugui contactar-vos en relació amb l\'informe d\'errors que heu enviat.</string>
<string name="prefer_webview_in_article_viewer_off">The content of the article will be displayed as simple text with links and images. Urls may not work, and gifs won\'t load.</string> <string name="pref_acra_alwaysaccept">Envia informes d\'error automàtics</string>
<string name="pref_acra_alwaysaccept_enabled">S\'enviaran informes d\'error automàticament</string>
<string name="pref_acra_alwaysaccept_disabled">Us preguntarem abans d\'enviar un informe d\'error.</string>
<string name="pref_debug_crash_reports">Informes d\'error</string>
<string name="pref_debug_debug_logs">Registre de depuració (s\'enviarà automàticament)</string>
<string name="acra_login">Habilita el registre</string>
<string name="drawer_item_hidden_tags">Hidden Tags</string>
<string name="unmark">Mark item as unread</string>
</resources> </resources>

View File

@ -64,7 +64,6 @@
<string name="switch_unread_count">"Display the unread count as a badge for the bottom bar."</string> <string name="switch_unread_count">"Display the unread count as a badge for the bottom bar."</string>
<string name="switch_unread_count_title">"Display unread count"</string> <string name="switch_unread_count_title">"Display unread count"</string>
<string name="display_all_counts_title">"Display count for favorite and read"</string> <string name="display_all_counts_title">"Display count for favorite and read"</string>
<string name="menu_share_the_app">"Invite friends"</string>
<string name="invitation_title">"Try this app for your Selfoss RSS feeds !"</string> <string name="invitation_title">"Try this app for your Selfoss RSS feeds !"</string>
<string name="invitation_message">"I use this app for my Selfoss RSS feeds. You may like it too !"</string> <string name="invitation_message">"I use this app for my Selfoss RSS feeds. You may like it too !"</string>
<string name="invitation_cta">"Try the app"</string> <string name="invitation_cta">"Try the app"</string>
@ -118,19 +117,7 @@
<string name="base_url_error">There was an issue when trying to communicate with your Selfoss Instance. If the issue persists, please get in touch with me.</string> <string name="base_url_error">There was an issue when trying to communicate with your Selfoss Instance. If the issue persists, please get in touch with me.</string>
<string name="pref_header_theme">Themes</string> <string name="pref_header_theme">Themes</string>
<string name="default_theme">Default</string> <string name="default_theme">Default</string>
<string name="teal_orange_theme">Teal/Orange/Light</string>
<string name="cyan_pink_theme">Cyan/Pink/Light</string>
<string name="grey_orange_theme">Grey/Orange/Light</string>
<string name="blue_amber_theme">Blue/Amber/Light</string>
<string name="indigo_pink_theme">Indigo/Pink/Light</string>
<string name="red_teal_theme">Red/Teal/Light</string>
<string name="teal_orange_dark_theme">Teal/Orange/Dark</string>
<string name="cyan_pink_dark_theme">Cyan/Pink/Dark</string>
<string name="default_dark_theme">Default/Dark</string> <string name="default_dark_theme">Default/Dark</string>
<string name="grey_orange_dark_theme">Grey/Orange/Dark</string>
<string name="blue_amber_dark_theme">Blue/Amber/Dark</string>
<string name="indigo_pink_dark_theme">Indigo/Pink/Dark</string>
<string name="red_teal_dark_theme">Red/Teal/Dark</string>
<string name="pref_header_debug">Debug</string> <string name="pref_header_debug">Debug</string>
<string name="login_debug_title">Activate to log login errors</string> <string name="login_debug_title">Activate to log login errors</string>
<string name="login_debug_on">Any error on the login page will be logged</string> <string name="login_debug_on">Any error on the login page will be logged</string>
@ -140,6 +127,7 @@
<string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string> <string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string>
<string name="pref_selfoss_category">Selfoss Api</string> <string name="pref_selfoss_category">Selfoss Api</string>
<string name="pref_api_items_number_title">Loaded items number</string> <string name="pref_api_items_number_title">Loaded items number</string>
<string name="pref_hidden_tags">Hidden Tags</string>
<string name="read_debug_title">Read articles appearing as unread ?</string> <string name="read_debug_title">Read articles appearing as unread ?</string>
<string name="read_debug_off">No log when marking an item as read</string> <string name="read_debug_off">No log when marking an item as read</string>
<string name="read_debug_on">Api calls will be logged when marking an article as read</string> <string name="read_debug_on">Api calls will be logged when marking an article as read</string>
@ -158,8 +146,27 @@
<string name="reader_action_more">Read more</string> <string name="reader_action_more">Read more</string>
<string name="reader_action_open">Open in browser</string> <string name="reader_action_open">Open in browser</string>
<string name="reader_action_share">Share</string> <string name="reader_action_share">Share</string>
<string name="pref_switch_actions_pager_scroll">Mark articles as read when swiping between articles.</string> <string name="pref_switch_actions_pager_scroll_on">Mark articles as read when swiping between articles.</string>
<string name="prefer_webview_in_article_viewer_title">Use a webview in the article viewer.</string> <string name="add_to_favs_reader">Add to favorites</string>
<string name="prefer_webview_in_article_viewer_on">Using the webview for the content of the article viewer. Urls, gifs and images will be better handled, but may be slower.</string> <string name="remove_to_favs_reader">Remove from favorites</string>
<string name="prefer_webview_in_article_viewer_off">The content of the article will be displayed as simple text with links and images. Urls may not work, and gifs won\'t load.</string> <string name="pref_content_reader_font_size">Article reader content font size</string>
<string name="pref_header_viewer">Article viewer</string>
<string name="refresh_dialog_message">This will refresh your Selfoss instance.</string>
<string name="markall_dialog_message">This will mark all the items as read.</string>
<string name="pref_switch_actions_pager_scroll">Mark as read on swipe</string>
<string name="pref_switch_actions_pager_scroll_off">Don\'t mark articles as read when swiping.</string>
<string name="gdpr_dialog_message">The app does not collect any personal data. Every analytics tools were removed. Crash reports sending is now optional, as is the debug logging. Keep in mind that debugging and crash reports are essential for the app development (You can configure everything in Settings &gt; Debug).</string>
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
<string name="crash_dialog_text">Something went wrong. Please send the report to the developer.</string>
<string name="crash_dialog_comment">You can add any helpful details in the comment bellow. Don\'t include any personal data in your comment. You could send me and email with your debug id, and I\'ll keep you posted when the issue is resolved.</string>
<string name="pref_acra_user_email">Contact email</string>
<string name="pref_acra_user_email_summary">Add an email so I can contact you about the crash reports you send.</string>
<string name="pref_acra_alwaysaccept">Automatically send crash reports</string>
<string name="pref_acra_alwaysaccept_enabled">Will send crash reports automatically</string>
<string name="pref_acra_alwaysaccept_disabled">Will ask everytime when sending crash reports.</string>
<string name="pref_debug_crash_reports">Crash reports</string>
<string name="pref_debug_debug_logs">Debug logging (these will be sent without a dialog)</string>
<string name="acra_login">Enable logging</string>
<string name="drawer_item_hidden_tags">Hidden Tags</string>
<string name="unmark">Mark item as unread</string>
</resources> </resources>

View File

@ -64,7 +64,6 @@
<string name="switch_unread_count">"Display the unread count as a badge for the bottom bar."</string> <string name="switch_unread_count">"Display the unread count as a badge for the bottom bar."</string>
<string name="switch_unread_count_title">"Display unread count"</string> <string name="switch_unread_count_title">"Display unread count"</string>
<string name="display_all_counts_title">"Display count for favorite and read"</string> <string name="display_all_counts_title">"Display count for favorite and read"</string>
<string name="menu_share_the_app">"Invite friends"</string>
<string name="invitation_title">"Try this app for your Selfoss RSS feeds !"</string> <string name="invitation_title">"Try this app for your Selfoss RSS feeds !"</string>
<string name="invitation_message">"I use this app for my Selfoss RSS feeds. You may like it too !"</string> <string name="invitation_message">"I use this app for my Selfoss RSS feeds. You may like it too !"</string>
<string name="invitation_cta">"Try the app"</string> <string name="invitation_cta">"Try the app"</string>
@ -118,19 +117,7 @@
<string name="base_url_error">There was an issue when trying to communicate with your Selfoss Instance. If the issue persists, please get in touch with me.</string> <string name="base_url_error">There was an issue when trying to communicate with your Selfoss Instance. If the issue persists, please get in touch with me.</string>
<string name="pref_header_theme">Themes</string> <string name="pref_header_theme">Themes</string>
<string name="default_theme">Default</string> <string name="default_theme">Default</string>
<string name="teal_orange_theme">Teal/Orange/Light</string>
<string name="cyan_pink_theme">Cyan/Pink/Light</string>
<string name="grey_orange_theme">Grey/Orange/Light</string>
<string name="blue_amber_theme">Blue/Amber/Light</string>
<string name="indigo_pink_theme">Indigo/Pink/Light</string>
<string name="red_teal_theme">Red/Teal/Light</string>
<string name="teal_orange_dark_theme">Teal/Orange/Dark</string>
<string name="cyan_pink_dark_theme">Cyan/Pink/Dark</string>
<string name="default_dark_theme">Default/Dark</string> <string name="default_dark_theme">Default/Dark</string>
<string name="grey_orange_dark_theme">Grey/Orange/Dark</string>
<string name="blue_amber_dark_theme">Blue/Amber/Dark</string>
<string name="indigo_pink_dark_theme">Indigo/Pink/Dark</string>
<string name="red_teal_dark_theme">Red/Teal/Dark</string>
<string name="pref_header_debug">Debug</string> <string name="pref_header_debug">Debug</string>
<string name="login_debug_title">Activate to log login errors</string> <string name="login_debug_title">Activate to log login errors</string>
<string name="login_debug_on">Any error on the login page will be logged</string> <string name="login_debug_on">Any error on the login page will be logged</string>
@ -140,6 +127,7 @@
<string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string> <string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string>
<string name="pref_selfoss_category">Selfoss Api</string> <string name="pref_selfoss_category">Selfoss Api</string>
<string name="pref_api_items_number_title">Loaded items number</string> <string name="pref_api_items_number_title">Loaded items number</string>
<string name="pref_hidden_tags">Hidden Tags</string>
<string name="read_debug_title">Read articles appearing as unread ?</string> <string name="read_debug_title">Read articles appearing as unread ?</string>
<string name="read_debug_off">No log when marking an item as read</string> <string name="read_debug_off">No log when marking an item as read</string>
<string name="read_debug_on">Api calls will be logged when marking an article as read</string> <string name="read_debug_on">Api calls will be logged when marking an article as read</string>
@ -158,8 +146,27 @@
<string name="reader_action_more">Read more</string> <string name="reader_action_more">Read more</string>
<string name="reader_action_open">Open in browser</string> <string name="reader_action_open">Open in browser</string>
<string name="reader_action_share">Share</string> <string name="reader_action_share">Share</string>
<string name="pref_switch_actions_pager_scroll">Mark articles as read when swiping between articles.</string> <string name="pref_switch_actions_pager_scroll_on">Mark articles as read when swiping between articles.</string>
<string name="prefer_webview_in_article_viewer_title">Use a webview in the article viewer.</string> <string name="add_to_favs_reader">Add to favorites</string>
<string name="prefer_webview_in_article_viewer_on">Using the webview for the content of the article viewer. Urls, gifs and images will be better handled, but may be slower.</string> <string name="remove_to_favs_reader">Remove from favorites</string>
<string name="prefer_webview_in_article_viewer_off">The content of the article will be displayed as simple text with links and images. Urls may not work, and gifs won\'t load.</string> <string name="pref_content_reader_font_size">Article reader content font size</string>
<string name="pref_header_viewer">Article viewer</string>
<string name="refresh_dialog_message">This will refresh your Selfoss instance.</string>
<string name="markall_dialog_message">This will mark all the items as read.</string>
<string name="pref_switch_actions_pager_scroll">Mark as read on swipe</string>
<string name="pref_switch_actions_pager_scroll_off">Don\'t mark articles as read when swiping.</string>
<string name="gdpr_dialog_message">The app does not collect any personal data. Every analytics tools were removed. Crash reports sending is now optional, as is the debug logging. Keep in mind that debugging and crash reports are essential for the app development (You can configure everything in Settings &gt; Debug).</string>
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
<string name="crash_dialog_text">Something went wrong. Please send the report to the developer.</string>
<string name="crash_dialog_comment">You can add any helpful details in the comment bellow. Don\'t include any personal data in your comment. You could send me and email with your debug id, and I\'ll keep you posted when the issue is resolved.</string>
<string name="pref_acra_user_email">Contact email</string>
<string name="pref_acra_user_email_summary">Add an email so I can contact you about the crash reports you send.</string>
<string name="pref_acra_alwaysaccept">Automatically send crash reports</string>
<string name="pref_acra_alwaysaccept_enabled">Will send crash reports automatically</string>
<string name="pref_acra_alwaysaccept_disabled">Will ask everytime when sending crash reports.</string>
<string name="pref_debug_crash_reports">Crash reports</string>
<string name="pref_debug_debug_logs">Debug logging (these will be sent without a dialog)</string>
<string name="acra_login">Enable logging</string>
<string name="drawer_item_hidden_tags">Hidden Tags</string>
<string name="unmark">Mark item as unread</string>
</resources> </resources>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.com--> <!--Generated by crowdin.com-->
<resources xmlns:tools="http://schemas.android.com/tools"> <resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name">"Reader für Selfoss"</string> <string name="app_name">"Reader für selfoss"</string>
<string name="title_activity_login">"Anmelden"</string> <string name="title_activity_login">"Anmelden"</string>
<string name="prompt_password">"Passwort"</string> <string name="prompt_password">"Passwort"</string>
<string name="prompt_http_password">"HTTP Passwort"</string> <string name="prompt_http_password">"HTTP Passwort"</string>
@ -33,7 +33,7 @@
<string name="cant_get_read">"Gelese Artikel können nicht abgerufen werden"</string> <string name="cant_get_read">"Gelese Artikel können nicht abgerufen werden"</string>
<string name="nothing_here">"Keine Einträge vorhanden"</string> <string name="nothing_here">"Keine Einträge vorhanden"</string>
<string name="tab_new">"Neu"</string> <string name="tab_new">"Neu"</string>
<string name="tab_read">"All"</string> <string name="tab_read">"Alle"</string>
<string name="tab_favs">"Favoriten"</string> <string name="tab_favs">"Favoriten"</string>
<string name="action_about">"Über"</string> <string name="action_about">"Über"</string>
<string name="marked_as_read">"Artikel gelesen"</string> <string name="marked_as_read">"Artikel gelesen"</string>
@ -64,7 +64,6 @@
<string name="switch_unread_count">"Zeige die Zahl ungelesener Artikel in der unteren Leiste."</string> <string name="switch_unread_count">"Zeige die Zahl ungelesener Artikel in der unteren Leiste."</string>
<string name="switch_unread_count_title">"Zeige Anzahl ungelesener Artikel"</string> <string name="switch_unread_count_title">"Zeige Anzahl ungelesener Artikel"</string>
<string name="display_all_counts_title">"Zeige Anzahl der Favoriten und gelesenen Artikel"</string> <string name="display_all_counts_title">"Zeige Anzahl der Favoriten und gelesenen Artikel"</string>
<string name="menu_share_the_app">"Freunde einladen"</string>
<string name="invitation_title">"Probiere diese App für deine Selfoss RSS-Feeds!"</string> <string name="invitation_title">"Probiere diese App für deine Selfoss RSS-Feeds!"</string>
<string name="invitation_message">"Ich benutze diese App für meine Selfoss RSS-Feeds. Vielleicht magst du sie auch!"</string> <string name="invitation_message">"Ich benutze diese App für meine Selfoss RSS-Feeds. Vielleicht magst du sie auch!"</string>
<string name="invitation_cta">"Probier die App"</string> <string name="invitation_cta">"Probier die App"</string>
@ -118,19 +117,7 @@
<string name="base_url_error">Beim Versuch deine Selfoss-Instanz zu erreichen ist ein Fehler aufgetreten. Solltet dieser Fehler bestehen bleiben, trete bitte mit mir in Kontakt.</string> <string name="base_url_error">Beim Versuch deine Selfoss-Instanz zu erreichen ist ein Fehler aufgetreten. Solltet dieser Fehler bestehen bleiben, trete bitte mit mir in Kontakt.</string>
<string name="pref_header_theme">Designs</string> <string name="pref_header_theme">Designs</string>
<string name="default_theme">Standard</string> <string name="default_theme">Standard</string>
<string name="teal_orange_theme">Türkis/Orange/Hell</string>
<string name="cyan_pink_theme">Cyan/Pink/Hell</string>
<string name="grey_orange_theme">Türkis/Orange/Hell</string>
<string name="blue_amber_theme">Blau/Amber/Hell</string>
<string name="indigo_pink_theme">Indigo/Pink/Hell</string>
<string name="red_teal_theme">Rot/Türkis/Hell</string>
<string name="teal_orange_dark_theme">Türkis/Orange/Dunkel</string>
<string name="cyan_pink_dark_theme">Cyan/Pink/Dunkel</string>
<string name="default_dark_theme">Standard (Dunkel)</string> <string name="default_dark_theme">Standard (Dunkel)</string>
<string name="grey_orange_dark_theme">Grau/Orange/Dunkel</string>
<string name="blue_amber_dark_theme">Blau/Gelb/Dunkel</string>
<string name="indigo_pink_dark_theme">Indigo/Pink/Dunkel</string>
<string name="red_teal_dark_theme">Rot/Türkis/Dunkel</string>
<string name="pref_header_debug">Debug</string> <string name="pref_header_debug">Debug</string>
<string name="login_debug_title">Aktivieren, um Login-Fehler zu protokollieren</string> <string name="login_debug_title">Aktivieren, um Login-Fehler zu protokollieren</string>
<string name="login_debug_on">Fehler auf der Login-Seite werden protokolliert</string> <string name="login_debug_on">Fehler auf der Login-Seite werden protokolliert</string>
@ -138,8 +125,9 @@
<string name="login_menu_debug">Debug</string> <string name="login_menu_debug">Debug</string>
<string name="self_hosted_cert_switch">Verwenden Sie einen selbst gehostetes Zertifikat?</string> <string name="self_hosted_cert_switch">Verwenden Sie einen selbst gehostetes Zertifikat?</string>
<string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string> <string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string>
<string name="pref_selfoss_category">Selfoss Api</string> <string name="pref_selfoss_category">selfoss API</string>
<string name="pref_api_items_number_title">Loaded items number</string> <string name="pref_api_items_number_title">Loaded items number</string>
<string name="pref_hidden_tags">Hidden Tags</string>
<string name="read_debug_title">Read articles appearing as unread ?</string> <string name="read_debug_title">Read articles appearing as unread ?</string>
<string name="read_debug_off">No log when marking an item as read</string> <string name="read_debug_off">No log when marking an item as read</string>
<string name="read_debug_on">Api calls will be logged when marking an article as read</string> <string name="read_debug_on">Api calls will be logged when marking an article as read</string>
@ -153,13 +141,32 @@
<string name="pref_general_infinite_loading_title">Load more articles on scroll</string> <string name="pref_general_infinite_loading_title">Load more articles on scroll</string>
<string name="translation">Übersetzung</string> <string name="translation">Übersetzung</string>
<string name="cant_open_invalid_url">The item url is invalid. I\'m looking into solving this issue so the app won\'t crash.</string> <string name="cant_open_invalid_url">The item url is invalid. I\'m looking into solving this issue so the app won\'t crash.</string>
<string name="drawer_report_bug">Report a bug</string> <string name="drawer_report_bug">Melde einen Fehler</string>
<string name="items_number_should_be_number">The items number should be an integer.</string> <string name="items_number_should_be_number">The items number should be an integer.</string>
<string name="reader_action_more">Read more</string> <string name="reader_action_more">Read more</string>
<string name="reader_action_open">Open in browser</string> <string name="reader_action_open">Im Browser öffnen</string>
<string name="reader_action_share">Share</string> <string name="reader_action_share">Teilen</string>
<string name="pref_switch_actions_pager_scroll">Mark articles as read when swiping between articles.</string> <string name="pref_switch_actions_pager_scroll_on">Mark articles as read when swiping between articles.</string>
<string name="prefer_webview_in_article_viewer_title">Use a webview in the article viewer.</string> <string name="add_to_favs_reader">Zu Favoriten hinzufügen</string>
<string name="prefer_webview_in_article_viewer_on">Using the webview for the content of the article viewer. Urls, gifs and images will be better handled, but may be slower.</string> <string name="remove_to_favs_reader">Remove from favorites</string>
<string name="prefer_webview_in_article_viewer_off">The content of the article will be displayed as simple text with links and images. Urls may not work, and gifs won\'t load.</string> <string name="pref_content_reader_font_size">Article reader content font size</string>
<string name="pref_header_viewer">Article viewer</string>
<string name="refresh_dialog_message">This will refresh your Selfoss instance.</string>
<string name="markall_dialog_message">This will mark all the items as read.</string>
<string name="pref_switch_actions_pager_scroll">Mark as read on swipe</string>
<string name="pref_switch_actions_pager_scroll_off">Don\'t mark articles as read when swiping.</string>
<string name="gdpr_dialog_message">The app does not collect any personal data. Every analytics tools were removed. Crash reports sending is now optional, as is the debug logging. Keep in mind that debugging and crash reports are essential for the app development (You can configure everything in Settings &gt; Debug).</string>
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
<string name="crash_dialog_text">Something went wrong. Please send the report to the developer.</string>
<string name="crash_dialog_comment">You can add any helpful details in the comment bellow. Don\'t include any personal data in your comment. You could send me and email with your debug id, and I\'ll keep you posted when the issue is resolved.</string>
<string name="pref_acra_user_email">Contact email</string>
<string name="pref_acra_user_email_summary">Add an email so I can contact you about the crash reports you send.</string>
<string name="pref_acra_alwaysaccept">Automatically send crash reports</string>
<string name="pref_acra_alwaysaccept_enabled">Will send crash reports automatically</string>
<string name="pref_acra_alwaysaccept_disabled">Will ask everytime when sending crash reports.</string>
<string name="pref_debug_crash_reports">Crash reports</string>
<string name="pref_debug_debug_logs">Debug logging (these will be sent without a dialog)</string>
<string name="acra_login">Enable logging</string>
<string name="drawer_item_hidden_tags">Hidden Tags</string>
<string name="unmark">Mark item as unread</string>
</resources> </resources>

View File

@ -64,7 +64,6 @@
<string name="switch_unread_count">"Display the unread count as a badge for the bottom bar."</string> <string name="switch_unread_count">"Display the unread count as a badge for the bottom bar."</string>
<string name="switch_unread_count_title">"Display unread count"</string> <string name="switch_unread_count_title">"Display unread count"</string>
<string name="display_all_counts_title">"Display count for favorite and read"</string> <string name="display_all_counts_title">"Display count for favorite and read"</string>
<string name="menu_share_the_app">"Invite friends"</string>
<string name="invitation_title">"Try this app for your Selfoss RSS feeds !"</string> <string name="invitation_title">"Try this app for your Selfoss RSS feeds !"</string>
<string name="invitation_message">"I use this app for my Selfoss RSS feeds. You may like it too !"</string> <string name="invitation_message">"I use this app for my Selfoss RSS feeds. You may like it too !"</string>
<string name="invitation_cta">"Try the app"</string> <string name="invitation_cta">"Try the app"</string>
@ -118,19 +117,7 @@
<string name="base_url_error">There was an issue when trying to communicate with your Selfoss Instance. If the issue persists, please get in touch with me.</string> <string name="base_url_error">There was an issue when trying to communicate with your Selfoss Instance. If the issue persists, please get in touch with me.</string>
<string name="pref_header_theme">Themes</string> <string name="pref_header_theme">Themes</string>
<string name="default_theme">Default</string> <string name="default_theme">Default</string>
<string name="teal_orange_theme">Teal/Orange/Light</string>
<string name="cyan_pink_theme">Cyan/Pink/Light</string>
<string name="grey_orange_theme">Grey/Orange/Light</string>
<string name="blue_amber_theme">Blue/Amber/Light</string>
<string name="indigo_pink_theme">Indigo/Pink/Light</string>
<string name="red_teal_theme">Red/Teal/Light</string>
<string name="teal_orange_dark_theme">Teal/Orange/Dark</string>
<string name="cyan_pink_dark_theme">Cyan/Pink/Dark</string>
<string name="default_dark_theme">Default/Dark</string> <string name="default_dark_theme">Default/Dark</string>
<string name="grey_orange_dark_theme">Grey/Orange/Dark</string>
<string name="blue_amber_dark_theme">Blue/Amber/Dark</string>
<string name="indigo_pink_dark_theme">Indigo/Pink/Dark</string>
<string name="red_teal_dark_theme">Red/Teal/Dark</string>
<string name="pref_header_debug">Debug</string> <string name="pref_header_debug">Debug</string>
<string name="login_debug_title">Activate to log login errors</string> <string name="login_debug_title">Activate to log login errors</string>
<string name="login_debug_on">Any error on the login page will be logged</string> <string name="login_debug_on">Any error on the login page will be logged</string>
@ -140,6 +127,7 @@
<string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string> <string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string>
<string name="pref_selfoss_category">Selfoss Api</string> <string name="pref_selfoss_category">Selfoss Api</string>
<string name="pref_api_items_number_title">Loaded items number</string> <string name="pref_api_items_number_title">Loaded items number</string>
<string name="pref_hidden_tags">Hidden Tags</string>
<string name="read_debug_title">Read articles appearing as unread ?</string> <string name="read_debug_title">Read articles appearing as unread ?</string>
<string name="read_debug_off">No log when marking an item as read</string> <string name="read_debug_off">No log when marking an item as read</string>
<string name="read_debug_on">Api calls will be logged when marking an article as read</string> <string name="read_debug_on">Api calls will be logged when marking an article as read</string>
@ -158,8 +146,27 @@
<string name="reader_action_more">Read more</string> <string name="reader_action_more">Read more</string>
<string name="reader_action_open">Open in browser</string> <string name="reader_action_open">Open in browser</string>
<string name="reader_action_share">Share</string> <string name="reader_action_share">Share</string>
<string name="pref_switch_actions_pager_scroll">Mark articles as read when swiping between articles.</string> <string name="pref_switch_actions_pager_scroll_on">Mark articles as read when swiping between articles.</string>
<string name="prefer_webview_in_article_viewer_title">Use a webview in the article viewer.</string> <string name="add_to_favs_reader">Add to favorites</string>
<string name="prefer_webview_in_article_viewer_on">Using the webview for the content of the article viewer. Urls, gifs and images will be better handled, but may be slower.</string> <string name="remove_to_favs_reader">Remove from favorites</string>
<string name="prefer_webview_in_article_viewer_off">The content of the article will be displayed as simple text with links and images. Urls may not work, and gifs won\'t load.</string> <string name="pref_content_reader_font_size">Article reader content font size</string>
<string name="pref_header_viewer">Article viewer</string>
<string name="refresh_dialog_message">This will refresh your Selfoss instance.</string>
<string name="markall_dialog_message">This will mark all the items as read.</string>
<string name="pref_switch_actions_pager_scroll">Mark as read on swipe</string>
<string name="pref_switch_actions_pager_scroll_off">Don\'t mark articles as read when swiping.</string>
<string name="gdpr_dialog_message">The app does not collect any personal data. Every analytics tools were removed. Crash reports sending is now optional, as is the debug logging. Keep in mind that debugging and crash reports are essential for the app development (You can configure everything in Settings &gt; Debug).</string>
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
<string name="crash_dialog_text">Something went wrong. Please send the report to the developer.</string>
<string name="crash_dialog_comment">You can add any helpful details in the comment bellow. Don\'t include any personal data in your comment. You could send me and email with your debug id, and I\'ll keep you posted when the issue is resolved.</string>
<string name="pref_acra_user_email">Contact email</string>
<string name="pref_acra_user_email_summary">Add an email so I can contact you about the crash reports you send.</string>
<string name="pref_acra_alwaysaccept">Automatically send crash reports</string>
<string name="pref_acra_alwaysaccept_enabled">Will send crash reports automatically</string>
<string name="pref_acra_alwaysaccept_disabled">Will ask everytime when sending crash reports.</string>
<string name="pref_debug_crash_reports">Crash reports</string>
<string name="pref_debug_debug_logs">Debug logging (these will be sent without a dialog)</string>
<string name="acra_login">Enable logging</string>
<string name="drawer_item_hidden_tags">Hidden Tags</string>
<string name="unmark">Mark item as unread</string>
</resources> </resources>

View File

@ -1,165 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--Generated by crowdin.com-->
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="app_name">"Reader for Selfoss"</string>
<string name="title_activity_login">"Log in"</string>
<string name="prompt_password">"Password"</string>
<string name="prompt_http_password">"HTTP Password"</string>
<string name="action_sign_in">"Go"</string>
<string name="error_invalid_password">"Password not long enough"</string>
<string name="error_field_required">"Field required"</string>
<string name="prompt_url">"Url"</string>
<string name="withLoginSwitch">"Login required ?"</string>
<string name="withHttpLoginSwitch">"HTTP Login required ?"</string>
<string name="login_url_problem">"Oops. You may need to add a \"/\" at the end of the url."</string>
<string name="prompt_login">"Username"</string>
<string name="prompt_http_login">"HTTP Username"</string>
<string name="label_share">"Share"</string>
<string name="readAll">"Read all"</string>
<string name="action_disconnect">"Disconnect"</string>
<string name="title_activity_settings">"Settings"</string>
<string name="pref_header_general">"General"</string>
<string name="pref_switch_actions_tap_title">"Tap action on the articles"</string>
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
<string name="add_source_hint_url">"Link"</string>
<string name="add_source_hint_name">"Name"</string>
<string name="add_source">"Add a source"</string>
<string name="add_source_save">"Save"</string>
<string name="wrong_infos">"Check your details again."</string>
<string name="all_posts_not_read">"All posts weren't read"</string>
<string name="all_posts_read">"All posts were read"</string>
<string name="cant_get_favs">"Can't get favorites"</string>
<string name="cant_get_new_elements">"Can't get new articles"</string>
<string name="cant_get_read">"Can't get read articles"</string>
<string name="nothing_here">"Nothing here"</string>
<string name="tab_new">"New"</string>
<string name="tab_read">"All"</string>
<string name="tab_favs">"Favorites"</string>
<string name="action_about">"About"</string>
<string name="marked_as_read">"Item read"</string>
<string name="undo_string">"Undo"</string>
<string name="addStringNoUrl">"Log in to add sources."</string>
<string name="cant_get_sources">"Can't get sources list."</string>
<string name="cant_create_source">"Can't create source."</string>
<string name="cant_get_spouts">"Can't get spouts list."</string>
<string name="form_not_complete">"The form is not complete"</string>
<string name="pref_header_links">"Links"</string>
<string name="issue_tracker_link">"Issue Tracker"</string>
<string name="issue_tracker_summary">"Report a bug or ask for a new feature"</string>
<string name="warning_wrong_url">"WARNING"</string>
<string name="pref_switch_card_view_title">"Card View"</string>
<string name="cant_mark_favortie">"Can't mark article as favorite"</string>
<string name="cant_unmark_favortie">"Can't remove item from favorite"</string>
<string name="share">"Share"</string>
<string name="rating_prompt_title">"Enjoying the app ?"</string>
<string name="rating_prompt_yes">"Yes !"</string>
<string name="rating_prompt_no">"Not really …"</string>
<string name="rating_prompt_feedback_title">"Can you tell us why ?"</string>
<string name="rating_prompt_feedback_yes">"OK !"</string>
<string name="rating_prompt_feedback_no">"Not now."</string>
<string name="rating_prompt_rating_title">"Great ! Can you rate us on the Store ?"</string>
<string name="rating_prompt_rating_yes">"Sure !"</string>
<string name="rating_prompt_rating_no">"Not right now."</string>
<string name="rating_prompt_thanks">"Thanks, your feedback help enhance the app !"</string>
<string name="switch_unread_count">"Display the unread count as a badge for the bottom bar."</string>
<string name="switch_unread_count_title">"Display unread count"</string>
<string name="display_all_counts_title">"Display count for favorite and read"</string>
<string name="menu_share_the_app">"Invite friends"</string>
<string name="invitation_title">"Try this app for your Selfoss RSS feeds !"</string>
<string name="invitation_message">"I use this app for my Selfoss RSS feeds. You may like it too !"</string>
<string name="invitation_cta">"Try the app"</string>
<string name="text_wrong_url">"You seem to be trying to use an invalid URL. Make sure it is correct, and if the problem persists, contact me (via the store contact link). Please note that the app needs you to be using Selfoss. You can't access RSS feeds without it."</string>
<string name="pref_general_internal_browser_title">"Open links inside the app"</string>
<string name="pref_general_internal_browser_on">"Articles will open inside the app"</string>
<string name="pref_general_internal_browser_off">"Articles will open with your default browser"</string>
<string name="prefer_article_viewer_title">"Use the article viewer"</string>
<string name="prefer_article_viewer_on">"Will use the article viewer instead of the internal browser"</string>
<string name="prefer_article_viewer_off">"Will use the internal browser instead of the article viewer"</string>
<string name="pref_general_category_links">"Link handling"</string>
<string name="pref_general_category_displaying">"Displaying"</string>
<string name="pref_general_category_actions">"Actions"</string>
<string name="pref_switch_card_view_on">"The articles will be displayed as cards"</string>
<string name="pref_switch_card_view_off">"The articles will be displayed as a list"</string>
<string name="pref_switch_actions_tap_on">"Displays the action bar under the article"</string>
<string name="pref_switch_actions_tap_off">"When selecting an article it will open in your selected browser"</string>
<string name="menu_home_refresh">"Update remote"</string>
<string name="refresh_success_response">"The remote is updated, you can now reload the articles list"</string>
<string name="refresh_failer_message">"The update didn't work, try again later, or check your selfoss logs."</string>
<string name="refresh_in_progress">"Refresh in progress"</string>
<string name="new_apk_available_title">"A new APK is available."</string>
<string name="new_apk_available_message">"A new APK is available to download on the official repository."</string>
<string name="new_apk_available_get">"Download now"</string>
<string name="new_apk_available_no">"Ignore version"</string>
<string name="intro_hello_title">"Hi there !"</string>
<string name="intro_hello_message">"Thanks for downloading the app !"</string>
<string name="intro_needs_selfoss_title">"Before you start…"</string>
<string name="intro_needs_selfoss_message">"You can't use the app without a Selfoss instance."</string>
<string name="intro_needs_selfoss_link">"What is Selfoss ?"</string>
<string name="intro_all_set_title">"All set !"</string>
<string name="intro_all_set_message">"You are ready to use the app. Don't forget to go to the settings page to configure your app, and where you'll find some useful links."</string>
<string name="card_height_title">Full height cards</string>
<string name="card_height_on">Cards height will adjust to its content</string>
<string name="card_height_off">Card height will be fixed</string>
<string name="source_code">Source code</string>
<string name="cant_mark_read">Can\'t mark article as read</string>
<string name="drawer_error_loading_tags">Error loading tags…</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
<string name="drawer_item_filters">Filters</string>
<string name="drawer_action_clear">clear</string>
<string name="drawer_item_tags">Tags</string>
<string name="drawer_item_sources">Sources</string>
<string name="drawer_action_edit">edit</string>
<string name="cache_drawer_error" tools:keep="@string/cache_drawer_error">Couldn\'t cache your drawer data</string>
<string name="no_tags_loaded">No tags loaded</string>
<string name="no_sources_loaded">No sources loaded</string>
<string name="drawer_loading">Loading …</string>
<string name="menu_home_search">Search</string>
<string name="can_delete_source">Can\'t delete the source…</string>
<string name="base_url_error">There was an issue when trying to communicate with your Selfoss Instance. If the issue persists, please get in touch with me.</string>
<string name="pref_header_theme">Themes</string>
<string name="default_theme">Default</string>
<string name="teal_orange_theme">Teal/Orange/Light</string>
<string name="cyan_pink_theme">Cyan/Pink/Light</string>
<string name="grey_orange_theme">Grey/Orange/Light</string>
<string name="blue_amber_theme">Blue/Amber/Light</string>
<string name="indigo_pink_theme">Indigo/Pink/Light</string>
<string name="red_teal_theme">Red/Teal/Light</string>
<string name="teal_orange_dark_theme">Teal/Orange/Dark</string>
<string name="cyan_pink_dark_theme">Cyan/Pink/Dark</string>
<string name="default_dark_theme">Default/Dark</string>
<string name="grey_orange_dark_theme">Grey/Orange/Dark</string>
<string name="blue_amber_dark_theme">Blue/Amber/Dark</string>
<string name="indigo_pink_dark_theme">Indigo/Pink/Dark</string>
<string name="red_teal_dark_theme">Red/Teal/Dark</string>
<string name="pref_header_debug">Debug</string>
<string name="login_debug_title">Activate to log login errors</string>
<string name="login_debug_on">Any error on the login page will be logged</string>
<string name="login_debug_off">No log on the login page</string>
<string name="login_menu_debug">Debug</string>
<string name="self_hosted_cert_switch">Using a self hosted certificate ?</string>
<string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string>
<string name="pref_selfoss_category">Selfoss Api</string>
<string name="pref_api_items_number_title">Loaded items number</string>
<string name="read_debug_title">Read articles appearing as unread ?</string>
<string name="read_debug_off">No log when marking an item as read</string>
<string name="read_debug_on">Api calls will be logged when marking an article as read</string>
<string name="summary_debug_identifier">Debug identifier</string>
<string name="unique_id_to_clipboard">Identifier copied to your clipboard</string>
<string name="display_header_drawer_summary">Display a header with the selfoss instance url on the lateral drawer.</string>
<string name="display_header_drawer_title">Account header</string>
<string name="login_everything_title">Logging every api calls</string>
<string name="login_everything_on">This will log every api call for debug purpose.</string>
<string name="login_everything_off">No api call will be logged</string>
<string name="pref_general_infinite_loading_title">Load more articles on scroll</string>
<string name="translation">Translation</string>
<string name="cant_open_invalid_url">The item url is invalid. I\'m looking into solving this issue so the app won\'t crash.</string>
<string name="drawer_report_bug">Report a bug</string>
<string name="items_number_should_be_number">The items number should be an integer.</string>
<string name="reader_action_more">Read more</string>
<string name="reader_action_open">Open in browser</string>
<string name="reader_action_share">Share</string>
<string name="pref_switch_actions_pager_scroll">Mark articles as read when swiping between articles.</string>
<string name="prefer_webview_in_article_viewer_title">Use a webview in the article viewer.</string>
<string name="prefer_webview_in_article_viewer_on">Using the webview for the content of the article viewer. Urls, gifs and images will be better handled, but may be slower.</string>
<string name="prefer_webview_in_article_viewer_off">The content of the article will be displayed as simple text with links and images. Urls may not work, and gifs won\'t load.</string>
</resources>

View File

@ -64,7 +64,6 @@
<string name="switch_unread_count">"Mostrar el recuento no leído como una insignia de la barra inferior."</string> <string name="switch_unread_count">"Mostrar el recuento no leído como una insignia de la barra inferior."</string>
<string name="switch_unread_count_title">"Mostrar recuento no leído"</string> <string name="switch_unread_count_title">"Mostrar recuento no leído"</string>
<string name="display_all_counts_title">"Mostrar recuento de favoritos y leídos"</string> <string name="display_all_counts_title">"Mostrar recuento de favoritos y leídos"</string>
<string name="menu_share_the_app">"Invitar amigos"</string>
<string name="invitation_title">"¡Prueba esta aplicación para tu contenido RSS de Selfoss!"</string> <string name="invitation_title">"¡Prueba esta aplicación para tu contenido RSS de Selfoss!"</string>
<string name="invitation_message">"Yo uso esta aplicación para mi contenido RSS de Selfoss. ¡Te puede gusta también!"</string> <string name="invitation_message">"Yo uso esta aplicación para mi contenido RSS de Selfoss. ¡Te puede gusta también!"</string>
<string name="invitation_cta">"Probar la aplicación"</string> <string name="invitation_cta">"Probar la aplicación"</string>
@ -118,19 +117,7 @@
<string name="base_url_error">Hubo un problema al intentar comunicarse con su instancia de Selfoss. Si el problema persiste, póngase en contacto conmigo.</string> <string name="base_url_error">Hubo un problema al intentar comunicarse con su instancia de Selfoss. Si el problema persiste, póngase en contacto conmigo.</string>
<string name="pref_header_theme">Temas</string> <string name="pref_header_theme">Temas</string>
<string name="default_theme">Predeterminado</string> <string name="default_theme">Predeterminado</string>
<string name="teal_orange_theme">Turquesa/Naranja/Claro</string>
<string name="cyan_pink_theme">Cian/Rosa/Claro</string>
<string name="grey_orange_theme">Gris/Naranja/Claro</string>
<string name="blue_amber_theme">Azul/Ámbar/Claro</string>
<string name="indigo_pink_theme">Índigo/Rosa/Claro</string>
<string name="red_teal_theme">Rojo/Turquesa/Claro</string>
<string name="teal_orange_dark_theme">Turquesa/Naranja/Oscuro</string>
<string name="cyan_pink_dark_theme">Cian/Rosa/Oscuro</string>
<string name="default_dark_theme">Predeterminado/Oscuro</string> <string name="default_dark_theme">Predeterminado/Oscuro</string>
<string name="grey_orange_dark_theme">Gris/Naranja/Oscuro</string>
<string name="blue_amber_dark_theme">Azul/Ámbar/Oscuro</string>
<string name="indigo_pink_dark_theme">Índigo/Rosa/Oscuro</string>
<string name="red_teal_dark_theme">Rojo/Turquesa/Oscuro</string>
<string name="pref_header_debug">Depurar</string> <string name="pref_header_debug">Depurar</string>
<string name="login_debug_title">Activar para registrar errores de inicio de sesión</string> <string name="login_debug_title">Activar para registrar errores de inicio de sesión</string>
<string name="login_debug_on">Cualquier error en la página de inicio de sesión se registrará</string> <string name="login_debug_on">Cualquier error en la página de inicio de sesión se registrará</string>
@ -140,6 +127,7 @@
<string name="self_signed_cert_warning">Por razones de seguridad, los certificados propios no son compatibles por defecto. Activando esto, no seré responsable de cualquier problema de seguridad que encuentre.</string> <string name="self_signed_cert_warning">Por razones de seguridad, los certificados propios no son compatibles por defecto. Activando esto, no seré responsable de cualquier problema de seguridad que encuentre.</string>
<string name="pref_selfoss_category">Api de Selfoss</string> <string name="pref_selfoss_category">Api de Selfoss</string>
<string name="pref_api_items_number_title">Número de artículos cargados</string> <string name="pref_api_items_number_title">Número de artículos cargados</string>
<string name="pref_hidden_tags">Etiquetas ocultas</string>
<string name="read_debug_title">¿Leer los artículos que aparecen como no leídos?</string> <string name="read_debug_title">¿Leer los artículos que aparecen como no leídos?</string>
<string name="read_debug_off">Sin registro al marcar un elemento como leído</string> <string name="read_debug_off">Sin registro al marcar un elemento como leído</string>
<string name="read_debug_on">Llamadas a la Api se registrarán al marcar un artículo como leído</string> <string name="read_debug_on">Llamadas a la Api se registrarán al marcar un artículo como leído</string>
@ -158,8 +146,27 @@
<string name="reader_action_more">Leer más</string> <string name="reader_action_more">Leer más</string>
<string name="reader_action_open">Abrir en el navegador</string> <string name="reader_action_open">Abrir en el navegador</string>
<string name="reader_action_share">Compartir</string> <string name="reader_action_share">Compartir</string>
<string name="pref_switch_actions_pager_scroll">Mark articles as read when swiping between articles.</string> <string name="pref_switch_actions_pager_scroll_on">Marcar artículos como leidos al desplazarse entre ellos.</string>
<string name="prefer_webview_in_article_viewer_title">Use a webview in the article viewer.</string> <string name="add_to_favs_reader">Añadir a Favoritos</string>
<string name="prefer_webview_in_article_viewer_on">Using the webview for the content of the article viewer. Urls, gifs and images will be better handled, but may be slower.</string> <string name="remove_to_favs_reader">Eliminar de favoritos</string>
<string name="prefer_webview_in_article_viewer_off">The content of the article will be displayed as simple text with links and images. Urls may not work, and gifs won\'t load.</string> <string name="pref_content_reader_font_size">Tamaño de la fuente del lector</string>
<string name="pref_header_viewer">Visor de artículos</string>
<string name="refresh_dialog_message">Esto actualizará su instancia de Selfoss.</string>
<string name="markall_dialog_message">Esto marcará todos los artículos como leídos.</string>
<string name="pref_switch_actions_pager_scroll">Marcar artículos como leídos al deslizar con el dedo hacia los lados</string>
<string name="pref_switch_actions_pager_scroll_off">No marcar artículos como leídos al deslizar con el dedo hacia los lados.</string>
<string name="gdpr_dialog_message">La aplicación no recopila ningún dato personal. Todas las herramientas de analítica fueron eliminadas. El envío de informes de errores es opcional, así como los registros de depuración. Ten en cuenta que tanto los registros como los informes de errores son esenciales para el desarrollo de la aplicación (Puedes configurar todo en \"Configuración &gt; Depurar\").</string>
<string name="gdpr_dialog_title">La aplicación no comparte ningún dato personal sobre usted.</string>
<string name="crash_dialog_text">Algo salió mal. Envié el informe al desarrollador.</string>
<string name="crash_dialog_comment">Puede agregar cualquier información útil en el siguiente comentario. No incluya ningún dato personal en el comentario. Podría enviarme un correo electrónico con el id de depuración para ser notificado cuando el problema se resuelva.</string>
<string name="pref_acra_user_email">Correo electrónico de contacto</string>
<string name="pref_acra_user_email_summary">Añade un correo electrónico para que pueda ponerme en contacto contigo sobre los informes de fallos que has enviado.</string>
<string name="pref_acra_alwaysaccept">Enviar automáticamente informe de fallos</string>
<string name="pref_acra_alwaysaccept_enabled">Se enviaran automáticamente los informes de fallos</string>
<string name="pref_acra_alwaysaccept_disabled">Le preguntará al enviar informes de fallos.</string>
<string name="pref_debug_crash_reports">Informe de fallos</string>
<string name="pref_debug_debug_logs">Registro de depuración (éstos se enviarán sin diálogo)</string>
<string name="acra_login">Habilitar el registro</string>
<string name="drawer_item_hidden_tags">Etiquetas ocultas</string>
<string name="unmark">Marcar artículo como no leído</string>
</resources> </resources>

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