Compare commits

...

256 Commits

Author SHA1 Message Date
2564b19726 Changelog for v125020471 2025-02-16 14:43:28 +00:00
61c7bb20cc chore: no more docker-compose.
All checks were successful
Create tag / build (push) Successful in 21m1s
Create tag / createTagAndChangelog (push) Successful in 1m21s
Create tag / release (push) Successful in 9m3s
Check master code / build (push) Successful in 34m35s
2025-02-16 15:17:51 +01:00
6a0f5baf0a bump: gradle plugin.
Some checks failed
Check master code / build (push) Has been cancelled
2025-02-16 14:57:34 +01:00
39f9505c00 Merge pull request 'fix: check index exists.' (#183) from fix-index into master
All checks were successful
Check master code / build (push) Successful in 8m3s
Reviewed-on: #183
2025-02-16 13:37:42 +00:00
6a6d447456 fix: check index exists.
All checks were successful
Check PR code / Lint (pull_request) Successful in 3m57s
Check PR code / build (pull_request) Successful in 13m44s
2025-02-16 13:57:42 +01:00
0bb4fe6aed Changelog for v125020411 2025-02-10 20:16:56 +00:00
7df4c3368c Merge pull request 'bump' (#182) from bump into master
All checks were successful
Check master code / build (push) Successful in 11m6s
Create tag / build (push) Successful in 7m35s
Create tag / createTagAndChangelog (push) Successful in 44s
Create tag / release (push) Successful in 4m35s
Reviewed-on: #182
2025-02-10 19:35:40 +00:00
c69635b5ae chore: non transiant R classes.
All checks were successful
Check PR code / Lint (pull_request) Successful in 3m36s
Check PR code / build (pull_request) Successful in 16m13s
2025-02-09 22:27:53 +01:00
3a829df70e Merge pull request 'fix: One more missing context.' (#181) from fix-one-more-context into master
All checks were successful
Check master code / build (push) Successful in 13m26s
Reviewed-on: #181
2025-02-09 20:44:29 +00:00
7a0202689f bump
All checks were successful
Check PR code / Lint (pull_request) Successful in 4m42s
Check PR code / build (pull_request) Successful in 16m9s
2025-02-09 21:44:02 +01:00
b20f6888f5 fix: One more missing context.
All checks were successful
Check PR code / Lint (pull_request) Successful in 1m1s
Check PR code / build (pull_request) Successful in 15m1s
2025-02-09 14:42:17 +01:00
6b96eb358d Merge pull request 'chore: more context issues.' (#180) from context-again into master
All checks were successful
Check master code / build (push) Successful in 7m35s
Create tag / build (push) Successful in 6m45s
Create tag / createTagAndChangelog (push) Successful in 42s
Create tag / release (push) Successful in 5m7s
Reviewed-on: #180
2025-01-29 14:50:22 +00:00
dfc1bf9fa3 chore: more context issues.
All checks were successful
Check PR code / Lint (pull_request) Successful in 2m33s
Check PR code / build (pull_request) Successful in 11m27s
2025-01-29 13:43:35 +01:00
b173664ff0 Changelog for v125010241
All checks were successful
Check master code / build (push) Successful in 9m50s
2025-01-24 22:06:19 +00:00
bc20a421ae Merge pull request 'fix: Link not opening.' (#178) from fix-open-link into master
All checks were successful
Check master code / build (push) Successful in 7m47s
Create tag / build (push) Successful in 7m14s
Create tag / createTagAndChangelog (push) Successful in 44s
Create tag / release (push) Successful in 5m13s
Reviewed-on: #178
2025-01-24 21:48:14 +00:00
794500355a refactor: context fragments issues.
All checks were successful
Check PR code / Lint (pull_request) Successful in 3m9s
Check PR code / build (pull_request) Successful in 12m15s
2025-01-24 21:44:20 +01:00
44f9dd53d3 logs: Context issues. 2025-01-24 21:10:01 +01:00
717d6b664c fix: Handle empty url issue, again. 2025-01-24 21:04:15 +01:00
e23289a3dc fix: Link not opening.
Some checks failed
Check PR code / Lint (pull_request) Failing after 7m7s
Check PR code / build (pull_request) Has been skipped
2025-01-24 20:56:04 +01:00
2f5ebe2420 Changelog for v125010201
All checks were successful
Check master code / build (push) Successful in 13m45s
2025-01-20 07:41:09 +00:00
1893904135 fix: Handle empty url issue.
All checks were successful
Check master code / build (push) Successful in 10m53s
Create tag / build (push) Successful in 7m40s
Create tag / createTagAndChangelog (push) Successful in 38s
Create tag / release (push) Successful in 5m3s
2025-01-19 14:49:37 +01:00
a4cb28ba81 Merge pull request 'Removed the floating bar.' (#177) from floating-bar into master
All checks were successful
Check master code / build (push) Successful in 11m40s
Reviewed-on: #177
2025-01-19 13:35:18 +00:00
ae3cada1c7 chore: changing actions in reader fragment.
All checks were successful
Check PR code / Lint (pull_request) Successful in 3m21s
Check PR code / build (pull_request) Successful in 11m38s
2025-01-19 12:54:25 +00:00
309500276f Changelog for v125010131
All checks were successful
Check master code / build (push) Successful in 8m58s
2025-01-13 16:19:05 +00:00
ce255b23cd fix: reload the adapter when it's needed. Fixes #128. (#176)
All checks were successful
Check master code / build (push) Successful in 10m27s
Create tag / build (push) Successful in 7m41s
Create tag / createTagAndChangelog (push) Successful in 48s
Create tag / release (push) Successful in 5m28s
## Types of changes

- [ ] I have read the **CONTRIBUTING** document.
- [ ] My code follows the code style of this project.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.
- [ ] This is **NOT** translation related.

This closes issue #XXX

This is implements feature #YYY

This finishes chore #ZZZ

Reviewed-on: #176
Co-authored-by: Amine <amine.bouabdallaoui@pm.me>
Co-committed-by: Amine <amine.bouabdallaoui@pm.me>
2025-01-13 15:08:39 +00:00
3b3a575dae feat: basic auth and images loading. Fixes #172. (#175)
All checks were successful
Check master code / build (push) Successful in 9m58s
Check PR code / Lint (pull_request) Successful in 3m15s
Check PR code / build (pull_request) Successful in 10m34s
## Types of changes

- [ ] I have read the **CONTRIBUTING** document.
- [ ] My code follows the code style of this project.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.
- [ ] This is **NOT** translation related.

This closes issue #XXX

This is implements feature #YYY

This finishes chore #ZZZ

Reviewed-on: #175
Co-authored-by: Amine <amine.bouabdallaoui@pm.me>
Co-committed-by: Amine <amine.bouabdallaoui@pm.me>
2025-01-13 07:39:13 +00:00
7bcf4574b4 Changelog for v125010111
All checks were successful
Check master code / build (push) Successful in 9m15s
2025-01-11 20:54:28 +00:00
c79ab5e92b Debug trying to fix context issues. (#174)
All checks were successful
Check master code / build (push) Successful in 7m44s
Create tag / build (push) Successful in 6m27s
Create tag / createTagAndChangelog (push) Successful in 37s
Create tag / release (push) Successful in 4m27s
Reviewed-on: #174
Co-authored-by: Amine <amine.bouabdallaoui@pm.me>
Co-committed-by: Amine <amine.bouabdallaoui@pm.me>
2025-01-11 20:35:27 +00:00
54dbda76ab Changelog for v125010031
All checks were successful
Check master code / build (push) Successful in 15m56s
2025-01-03 09:09:18 +00:00
11c39ae87c Merge pull request 'Bump dependencies' (#173) from upgarde into master
All checks were successful
Check master code / build (push) Successful in 19m42s
Create tag / build (push) Successful in 27m4s
Create tag / createTagAndChangelog (push) Successful in 52s
Create tag / release (push) Successful in 7m41s
Reviewed-on: #173
2025-01-03 08:41:15 +00:00
6645902ec8 chore: "faster" action.
All checks were successful
Check PR code / Lint (pull_request) Successful in 5m59s
Check PR code / build (pull_request) Successful in 14m16s
2025-01-03 09:19:12 +01:00
0a07a5dfad fastlane: icon change.
Some checks failed
Check PR code / Lint (pull_request) Has been cancelled
Check PR code / build (pull_request) Has been cancelled
2025-01-03 09:18:06 +01:00
d88d38fd3b chore: ignoring a pixel issue.
All checks were successful
Check PR code / Lint (pull_request) Successful in 9m15s
Check PR code / build (pull_request) Successful in 23m30s
2025-01-02 20:53:00 +01:00
28fe38aa17 test: fixed an ui test issue.
All checks were successful
Check PR code / Lint (pull_request) Successful in 11m23s
Check PR code / build (pull_request) Successful in 19m41s
2025-01-02 20:32:53 +01:00
d524c30732 fix: center the loading thing.
All checks were successful
Check PR code / Lint (pull_request) Successful in 5m35s
Check PR code / build (pull_request) Successful in 18m15s
2024-12-31 15:31:47 +01:00
8c00aa65da test: items displaying.
All checks were successful
Check PR code / Lint (pull_request) Successful in 9m8s
Check PR code / build (pull_request) Successful in 36m8s
2024-12-31 15:23:26 +01:00
ae81261cb1 bump: sqldelight.
All checks were successful
Check PR code / Lint (pull_request) Successful in 1m47s
Check PR code / build (pull_request) Successful in 15m9s
2024-12-31 12:37:36 +01:00
03c567ee33 bump: material, desugar jdk, jsoup, kodein, settings, napier, mock.
All checks were successful
Check PR code / Lint (pull_request) Successful in 1m37s
Check PR code / build (pull_request) Successful in 13m29s
2024-12-31 11:45:11 +01:00
d23dd82fc2 bump: androix and coroutines.
All checks were successful
Check PR code / Lint (pull_request) Successful in 1m50s
Check PR code / build (pull_request) Successful in 13m29s
2024-12-31 11:06:36 +01:00
2e7a168424 bump: ktor. Closes #67.
All checks were successful
Check PR code / Lint (pull_request) Successful in 47s
Check PR code / build (pull_request) Successful in 15m22s
2024-12-31 10:03:21 +01:00
5bc2f614af Changelog for v124123651
All checks were successful
Check master code / build (push) Successful in 12m31s
2024-12-30 22:54:15 +00:00
934c112db5 Merge pull request 'Bugfixes' (#171) from bugfixes into master
All checks were successful
Check master code / build (push) Successful in 19m51s
Create tag / build (push) Successful in 21m4s
Create tag / createTagAndChangelog (push) Successful in 50s
Create tag / release (push) Successful in 3m39s
Reviewed-on: #171
2024-12-30 22:32:11 +00:00
ad7549a89f config: crowdin
All checks were successful
Check PR code / Lint (pull_request) Successful in 54s
Check PR code / build (pull_request) Successful in 10m35s
2024-12-30 23:20:03 +01:00
fb9ceecabd chore: can links be empty ?
All checks were successful
Check PR code / Lint (pull_request) Successful in 1m3s
Check PR code / build (pull_request) Successful in 11m14s
2024-12-30 22:45:18 +01:00
61b9fd30e0 fix: Context issues in article fragment. 2024-12-30 22:37:36 +01:00
806e56e20b fix: Context issues in fragment sheet.
All checks were successful
Check PR code / Lint (pull_request) Successful in 1m7s
Check PR code / build (pull_request) Successful in 12m15s
2024-12-30 22:12:38 +01:00
cd8b7aaf9d fix: build. 2024-12-30 22:12:21 +01:00
c25ad7621e chore: compile issue fix.
All checks were successful
Check PR code / Lint (pull_request) Successful in 1m10s
Check PR code / build (pull_request) Successful in 9m57s
2024-12-30 15:07:35 +01:00
63da3b9fe7 chore: filter some bugs. 2024-12-30 15:07:18 +01:00
1d99eeb633 bugfix: catch users using something other than selfoss. 2024-12-30 13:51:45 +01:00
162a350a8f bugfix: No browser, no link. 2024-12-30 13:51:14 +01:00
27c1bba146 translations
All checks were successful
Check PR code / Lint (pull_request) Successful in 54s
Check PR code / build (pull_request) Successful in 9m26s
2024-12-30 13:49:42 +01:00
b7f3a9877a chore: remove log. 2024-12-30 12:58:34 +01:00
47f78754dc translation
Some checks failed
Check PR code / Lint (pull_request) Successful in 46s
Check PR code / build (pull_request) Failing after 3m20s
2024-12-30 12:48:42 +01:00
1bdfb143ac Changelog for v124123641
All checks were successful
Check master code / coverage (push) Successful in 11m32s
Check master code / build (push) Successful in 15m2s
2024-12-29 22:02:12 +00:00
d81ced3964 Chore: no tests on build.
All checks were successful
Check master code / coverage (push) Successful in 9m42s
Check master code / build (push) Successful in 12m57s
Create tag / build (push) Successful in 9m8s
Create tag / createTagAndChangelog (push) Successful in 46s
Create tag / release (push) Successful in 6m34s
2024-12-29 22:37:59 +01:00
fbafece1fa Merge pull request 'testing' (#170) from testing into master
All checks were successful
Check master code / coverage (push) Successful in 10m16s
Check master code / build (push) Successful in 12m51s
Reviewed-on: #170
2024-12-29 21:23:52 +00:00
cbed8f07cb fix: Displaying fixes. Fixes #155
All checks were successful
Check PR code / Lint (pull_request) Successful in 1m24s
Check PR code / build (pull_request) Successful in 9m20s
2024-12-29 22:13:45 +01:00
f54fcc3ba1 test: coverage
All checks were successful
Check PR code / Lint (pull_request) Successful in 1m13s
Check PR code / build (pull_request) Successful in 9m43s
2024-12-29 21:35:12 +01:00
aminecmi
aad93ef722 chore: update and use multiplatform datetime 2024-12-21 20:32:58 +01:00
9e83af0302 Changelog for v124123421
All checks were successful
Check master code / build (push) Successful in 11m23s
2024-12-07 17:41:21 +00:00
aminecmi
24b86e66b4 fix: Trying to fix the serialization issue.
All checks were successful
Check master code / build (push) Successful in 9m40s
Create tag / build (push) Successful in 8m10s
Create tag / createTagAndChangelog (push) Successful in 32s
Create tag / release (push) Successful in 6m48s
2024-12-07 18:20:49 +01:00
641c444061 Changelog for v124113311 2024-11-26 20:40:47 +00:00
0902c61544 chore: update versions. (#165)
All checks were successful
Check master code / build (push) Successful in 8m8s
Create tag / build (push) Successful in 8m15s
Create tag / createTagAndChangelog (push) Successful in 1m0s
Create tag / release (push) Successful in 6m52s
## Types of changes

- [ ] I have read the **CONTRIBUTING** document.
- [ ] My code follows the code style of this project.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.
- [ ] This is **NOT** translation related.

This closes issue #XXX

This is implements feature #YYY

This finishes chore #ZZZ

Co-authored-by: aminecmi <aminecmi@gmail.com>
Reviewed-on: #165
Co-authored-by: administrateur <administrateur@hidden.hidden>
Co-committed-by: administrateur <administrateur@hidden.hidden>
2024-11-26 20:19:43 +00:00
aminecmi
6790152a0b chore: fastlane changelog.
All checks were successful
Check master code / build (push) Successful in 8m3s
2024-11-26 21:06:57 +01:00
aminecmi
46d1ba418e chore: fastlane fixes.
Some checks failed
Check master code / build (push) Has been cancelled
2024-11-26 21:03:31 +01:00
436373d0ad Changelog for v124113301
All checks were successful
Check master code / build (push) Successful in 7m42s
2024-11-25 22:23:23 +01:00
aminecmi
5b9b51c02d chore: Gitea Action
All checks were successful
Check master code / build (push) Successful in 7m51s
Create tag / build (push) Successful in 8m15s
Create tag / createTagAndChangelog (push) Successful in 46s
Create tag / release (push) Successful in 6m27s
2024-11-25 21:55:48 +01:00
b81abe384a Merge pull request 'chore: Gitea Action' (#164) from runner into master
Some checks failed
Push/PR Steps / BuildAndTest (push) Has been cancelled
Reviewed-on: #164
2024-11-23 14:14:45 +00:00
aminecmi
851f862dbe chore: Gitea Action
All checks were successful
Push/PR Steps / Lint (pull_request) Successful in 1m0s
Push/PR Steps / BuildAndTest (pull_request) Successful in 7m49s
2024-11-23 14:54:45 +01:00
aminecmi
8d7e302af8 chore: Readme update. 2024-11-20 20:45:06 +01:00
236e1cca90 Fix recycleview article positions (#163)
Reviewed-on: #163
Reviewed-by: Amine Bouabdallaoui <amineb@hidden.hidden>
2024-11-20 09:32:29 +01:00
3a33cb4510 Provide method to update items in the home
Removed in the previous commit, the item adapter accepts a method to update the articles list in the home page.
Renamed the method to make its objective more clear.
Removed a debugging log.
Reverted change to function name.
2024-11-20 01:28:47 +01:00
0bf9ca9a49 Fix recycleview article positions
The articles were being opened by setting a click listener on the binding.
In card view this was being done through a function and as such it used the overall viewbind of the view rather than the binding of the item viewholder.
Now the functions are more explicit to avoid future errors.
Pulled up a few members from ItemCardAdapter and ItemListAdapter to ItemAdapter.
2024-11-19 01:50:58 +01:00
aminecmi
61e0087894 Changelog for v124041081 [CI SKIP] 2024-04-17 10:57:12 +00:00
aminecmi
1ec05d9913 chore: comment.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is passing
continuous-integration/drone/tag Build is passing
2024-04-17 12:39:45 +02:00
aminecmi
859bd91bbb fix: Last time fixing the parsing date hack before moving it to os version. 2024-04-17 12:22:33 +02:00
aminecmi
204b736c53 Changelog for v124030731 [CI SKIP] 2024-03-13 19:51:14 +00:00
aminecmi
f24609c143 fix: Basic auth and password can have non whitspace characters. Fixes 142.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is passing
2024-03-13 20:31:24 +01:00
aminecmi
b94d7dc537 Changelog for v124020451 [CI SKIP] 2024-02-14 19:54:05 +00:00
aminecmi
41910cc4cd fix: Fixed handling of position in card adapter.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is passing
2024-02-13 21:31:02 +01:00
aminecmi
db166ca9d4 Changelog for v124010301 [CI SKIP] 2024-01-30 19:36:37 +00:00
aminecmi
db0d5a4a85 fix: This may fix the oom errors.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is passing
2024-01-29 20:55:29 +01:00
aminecmi
3bc0d7cf95 Changelog for v124010191 [CI SKIP] 2024-01-19 21:16:23 +00:00
aminecmi
8f464d95fd fix: moving listeners.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is passing
2024-01-19 21:32:43 +01:00
aminecmi
5ccd6a3368 chore: removed a useless log. 2024-01-19 21:32:43 +01:00
aminecmi
cdbded246e Changelog for v124010032 [CI SKIP] 2024-01-03 22:16:35 +00:00
aminecmi
750c7758bd fix: Another date format thing.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is passing
2024-01-03 23:07:57 +01:00
aminecmi
22f8b14ecd Changelog for v124010031 [CI SKIP] 2024-01-03 21:46:48 +00:00
aminecmi
6e27d6d4e6 fix: Checking if selfoss instance.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is passing
2024-01-03 22:28:57 +01:00
aminecmi
14ff4dbd05 fix: handle three characters lenght hexcode colors. 2024-01-03 21:35:58 +01:00
aminecmi
390c2d0cf3 Changelog for v123113311 [CI SKIP] 2023-11-27 20:48:32 +00:00
aminecmi
e58914ef58 chore: Source tracker url in the menu.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is passing
2023-11-27 21:31:46 +01:00
aminecmi
a03f08fca1 fix: Handle kodein proguard rules. 2023-11-27 21:31:26 +01:00
aminecmi
8e9b87f00c Changelog for v123102961 [CI SKIP] 2023-10-23 21:11:15 +00:00
aminecmi
f765224a86 chore: domain changes.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is passing
2023-10-21 23:41:17 +02:00
aminecmi
14d2219eb8 Changelog for v123102852 [CI SKIP] 2023-10-12 18:48:06 +00:00
aminecmi
137580ccf9 chore: lint cleaning.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is passing
2023-10-12 20:38:01 +02:00
aminecmi
f101d22f54 Changelog for v123102841 [CI SKIP] 2023-10-12 20:13:17 +02:00
aminecmi
68aedb7641 chore: verbose.
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is failing
2023-10-11 21:13:10 +02:00
Amine Louveau
754d526b49 chore: cleaning ci steps and upgrading dependencies.
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone/promote/production Build is failing
## Types of changes

- [ ] I have read the **CONTRIBUTING** document.
- [ ] My code follows the code style of this project.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] All new and existing tests passed.
- [ ] This is **NOT** translation related.

This closes issue #XXX

This is implements feature #YYY

This finishes chore #ZZZ

Co-authored-by: davidoskky <davidoskky@yahoo.it>
Co-authored-by: aminecmi <aminecmi@gmail.com>
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/150
2023-10-10 20:52:26 +00:00
Amine Louveau
c458871569 feat: Self signed ssl support.
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/141
2023-09-17 18:28:47 +00:00
056825aa0c Revert xmlns changes
All checks were successful
continuous-integration/drone/pr Build is passing
2023-09-17 12:01:32 +02:00
16b19fc5ce Revert version upgrades 2023-09-17 11:59:08 +02:00
4ad4a23ed8 Revert to private functions
Some checks failed
continuous-integration/drone/pr Build is failing
2023-09-12 00:38:37 +02:00
d8c215eacc Reintroduce removed parameter 2023-09-12 00:36:56 +02:00
2b446ab22b Revert dependency version changes 2023-09-12 00:36:04 +02:00
a029d8a7dc Move api client creation function within api class
All checks were successful
continuous-integration/drone/pr Build is passing
2023-09-10 21:33:28 +02:00
4482234e1a Remove unused strings file
All checks were successful
continuous-integration/drone/pr Build is passing
2023-09-10 21:15:04 +02:00
b5de30f561 Add translations 2023-09-10 21:14:43 +02:00
70ad5f322c Handle most HTTP client creation in common code 2023-09-10 21:14:43 +02:00
d167092c83 Add a login switch to disable SSL verification 2023-09-10 20:24:22 +02:00
c4f4bafe85 Add a switch in the login screen to disable SSL 2023-07-13 14:55:48 +02:00
ed06b22a77 Tentative self signed ssl support 2023-07-13 14:55:48 +02:00
aminecmi
172362b533 Changelog for v123061811 [CI SKIP] 2023-06-30 19:06:44 +00:00
aminecmi
ad72cb6f56 feat: Added confirmation dialog for disconnect item menu.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-06-30 20:37:43 +02:00
aminecmi
9057ee0052 Changelog for v123061651 [CI SKIP] 2023-06-14 18:23:59 +00:00
aminecmi
50d0b44315 i18n: Translation update.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-06-13 20:52:42 +02:00
aminecmi
21b08ed384 i18n: Translation update.
Some checks are pending
continuous-integration/drone/push Build is running
2023-06-13 20:26:36 +02:00
aminecmi
993c4d2ee9 i18n: Translation update.
All checks were successful
continuous-integration/drone/push Build is passing
2023-06-12 20:50:26 +02:00
aminecmi
57a9d51027 fix: avoid trying to open invalid image urls.
Some checks are pending
continuous-integration/drone/push Build is running
2023-06-12 20:34:35 +02:00
aminecmi
673f0edb8b Changelog for v123051471 [CI SKIP] 2023-05-27 19:25:35 +00:00
aminecmi
7f96798f13 fix: images could be null.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-05-27 21:06:56 +02:00
aminecmi
6e5704a45b fix: Check if color is not empty before parsing it. 2023-05-27 21:02:25 +02:00
aminecmi
495591159f chore: Removed unused log. 2023-05-27 21:01:54 +02:00
aminecmi
718fe7c5ee Changelog for v123051331 [CI SKIP] 2023-05-13 20:24:57 +00:00
aminecmi
ecd23213f9 fix: illegal input.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-05-13 22:14:25 +02:00
aminecmi
e6baed8cb4 Changelog for v123051321 [CI SKIP] 2023-05-12 19:19:35 +00:00
aminecmi
c87abec0b9 debug: Debug null context.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-05-12 20:31:40 +02:00
Amine Louveau
0aba41d8bf Changelog for v123051301 [CI SKIP] 2023-05-10 19:36:31 +00:00
Amine Louveau
2a2d1047b4 feat: Basic auth from url. Fixes #142 (#143)
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
Co-authored-by: aminecmi <aminecmi@gmail.com>
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/143
2023-05-10 19:19:11 +00:00
aminecmi
66ef1ccf32 debug: Debug index out of bound exception.
Some checks failed
continuous-integration/drone/push Build is failing
2023-05-10 20:50:13 +02:00
aminecmi
677ede5bc7 Changelog for v123051211 [CI SKIP] 2023-05-01 18:26:12 +00:00
aminecmi
996a7ed22c fix: Sometimes url isn't even defined.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-04-30 18:41:15 +02:00
Amine Louveau
85208c4e5a Changelog for v123041021 [CI SKIP] 2023-04-12 19:01:21 +00:00
Amine Louveau
5cfec50cba fix: 'Enable Core Library Desugaring to support older Android versions' (#138) from davidoskky/ReaderForSelfoss-multiplatform:desugaring into master
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/138
2023-04-12 18:26:37 +00:00
76ad71e1dc Enable Core Library Desugaring to support older Android versions
Some checks failed
continuous-integration/drone/pr Build is failing
2023-04-12 16:29:47 +02:00
0277fb507c Changelog for v123030851 [CI SKIP] 2023-03-26 18:21:43 +00:00
8d7d3174aa chore: replace textDrawable library (#136)
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
## Types of changes

- [x] I have read the **CONTRIBUTING** document.
- [x] My code follows the code style of this project.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [x] All new and existing tests passed.
- [x] This is **NOT** translation related.

This closes issue #120

Removed the dependency `com.amulyakhare.textdrawable` and slightly simplified the logic required to set circular images.

Co-authored-by: davidoskky <davidoskky@yahoo.it>
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/136
Co-authored-by: davidoskky <davidoskky@hidden.hidden>
Co-committed-by: davidoskky <davidoskky@hidden.hidden>
2023-03-26 11:12:01 +00:00
aminecmi
00eb3333fe refactor: Remove slow login check. Closes #135.
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-25 20:28:30 +01:00
aminecmi
629ca01d99 ci: send the mapping file after a release.
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-17 10:10:38 +01:00
aminecmi
c2d8681ce8 Changelog for v123030751 [CI SKIP] 2023-03-16 19:41:34 +00:00
aminecmi
08f79cb148 debug: added a lot to pinpoint the url issue.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-03-16 20:26:36 +01:00
e21906e70d feat: Use /sources/stats in the home (#133)
All checks were successful
continuous-integration/drone/push Build is passing
## Types of changes

- [x] I have read the **CONTRIBUTING** document.
- [x] My code follows the code style of this project.
- [ ] I have updated the documentation accordingly.
- [x] I have added tests to cover my changes.
- [x] All new and existing tests passed.
- [x] This is **NOT** translation related.

This is implements feature #131 and it will allow implementing #132
With this, public mode functions perfectly and also has source filtering.

Co-authored-by: davidoskky <davidoskky@yahoo.it>
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/133
Co-authored-by: davidoskky <davidoskky@hidden.hidden>
Co-committed-by: davidoskky <davidoskky@hidden.hidden>
2023-03-13 16:26:54 +00:00
aminecmi
9d2cc32bc9 Changelog for v123030681 [CI SKIP] 2023-03-09 20:27:08 +00:00
aminecmi
d9d057c8dc fix: Unread and starred can be null.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-03-09 20:46:09 +01:00
aminecmi
1f3fa0c4a6 Fixed version number issue.
Some checks failed
continuous-integration/drone/push Build is failing
2023-03-05 21:02:03 +01:00
aminecmi
dea3def385 Changelog for v123030621 [CI SKIP] 2023-03-03 20:07:52 +00:00
aminecmi
f72ef2f5d4 fix: url required issue.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-03-03 20:39:33 +01:00
aminecmi
f28cb759df fix: Canvas reused issue. 2023-03-03 20:39:20 +01:00
aminecmi
b9d69c3e64 Changelog for v123020572 [CI SKIP] 2023-02-26 19:13:41 +00:00
aminecmi
c2a1c9eaac fix: requirecontext issues ?
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-02-26 16:05:26 +01:00
aminecmi
bf37209a15 debug: activity not found exception. 2023-02-26 15:40:58 +01:00
aminecmi
2c558fe6fd Changelog for v123020571 [CI SKIP] 2023-02-26 07:49:16 +00:00
aminecmi
ad88011454 chore: remove errors logging.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-02-25 22:57:59 +01:00
aminecmi
559c17bc1d fix: quickfix for url param not provided for some sources. 2023-02-25 22:46:04 +01:00
Amine Louveau
ab9c46f0eb Update 'CHANGELOG.md'
All checks were successful
continuous-integration/drone/push Build is passing
2023-02-21 15:00:37 +00:00
Amine Louveau
aa799d2ca8 Changelog for v123020523 [CI SKIP] 2023-02-21 14:49:14 +00:00
Amine Louveau
177c978474 fix: Git changelog.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-02-21 14:05:32 +00:00
Amine Louveau
39b9991413 Changelog for v123020522 [CI SKIP] 2023-02-21 13:25:11 +00:00
Amine Louveau
b303f110f1 fix: still fixing changelog versions.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-02-21 12:59:38 +00:00
Amine Louveau
f851941a6a Changelog for v123020521 [CI SKIP] 2023-02-21 11:31:18 +00:00
Amine Louveau
a313552976 fix: change changelog version on release.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-02-21 11:20:43 +00:00
aminecmi
6ac97ed3fe Changelog for v123020491 [CI SKIP] 2023-02-20 09:39:40 +00:00
aminecmi
d583b937b7 fix: Fixed acra bug reporting.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-02-20 10:29:00 +01:00
aminecmi
15b9a2d935 Changelog for v123010301 [CI SKIP] 2023-02-18 20:01:03 +00:00
aminecmi
5a8ce15961 Chore: acra config.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-02-18 20:38:06 +01:00
e1c64cef46 Changelog for v123010281 [CI SKIP] 2023-01-30 19:55:23 +00:00
ee064f3cb4 improvement: Improve right to left support (#130)
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
Co-authored-by: davidoskky <davidoskky@hidden.hidden>
Co-committed-by: davidoskky <davidoskky@hidden.hidden>
2023-01-29 12:55:28 +00:00
3e46e2ff29 Changelog for v123010261 [CI SKIP] 2023-01-28 10:57:06 +00:00
f28e702549 feat: Handle public instances (#126)
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
Co-authored-by: davidoskky <davidoskky@hidden.hidden>
Co-committed-by: davidoskky <davidoskky@hidden.hidden>
2023-01-28 10:25:28 +00:00
aminecmi
fc31a4399c ci: Pull request should trigger ci.
Some checks are pending
continuous-integration/drone/push Build is running
2023-01-28 11:23:00 +01:00
Amine Louveau
9b23053b66 fix: Complete the disconnection before redirecting to the login screen
Some checks are pending
continuous-integration/drone/push Build is running
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/129
2023-01-28 11:20:19 +01:00
389a04d250 Complete the disconnection before redirecting to the login screen 2023-01-27 14:21:33 +01:00
Amine Louveau
40e1d1478b Changelog for v123010241 [CI SKIP] 2023-01-26 10:55:43 +00:00
Amine Louveau
2154ff3c33 Merge pull request 'feat: swipe down to close images' (#122) from davidoskky/ReaderForSelfoss-multiplatform:swipe_down into master
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/122
2023-01-26 10:42:15 +00:00
2245565f95 Remove unnecessary definition 2023-01-25 21:25:32 +01:00
014858f06b Remove unused import 2023-01-25 20:40:20 +01:00
3f1f86a78e Adjust the image closing animation 2023-01-25 10:34:03 +01:00
a549169a7c Add a dark hue to the underlying article when swiping to close images 2023-01-25 10:27:38 +01:00
be7cae365a Rename activity style to avoid interferences 2023-01-25 02:09:41 +01:00
cef3b2e593 Adapt the style of the image activity to the rest of the application 2023-01-25 01:54:35 +01:00
ae927ebc57 Resolve issues when swiping down to close images 2023-01-25 00:46:43 +01:00
Amine Louveau
90532cf501 Changelog for v123010041 [CI SKIP] 2023-01-24 12:41:52 +00:00
Amine Louveau
ab0678d61e Merge pull request 'scroll-tag-filters' (#124) from scroll-tag-filters into master
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/124
2023-01-23 21:42:42 +00:00
aminecmi
a1b7d22d26 fix: added POST_NOTIFICATIONS to fix notifications issues.
All checks were successful
continuous-integration/drone/push Build is passing
2023-01-23 22:32:52 +01:00
aminecmi
29eae4b1f6 fix: scrollable filter sheet.
Some checks failed
continuous-integration/drone/push Build is failing
2023-01-23 21:13:10 +01:00
aminecmi
f5bbc63481 enhancement: Ellipsize chips text. 2023-01-23 21:12:56 +01:00
ddc72d85b0 Close the image fragment only if the image has been dragged down 2023-01-21 16:37:25 +01:00
68bbf5b2d3 Animate swipe down to close images 2023-01-20 16:36:52 +01:00
aminecmi
95e76a55da Cleaning.
All checks were successful
continuous-integration/drone/push Build is passing
2023-01-12 22:01:36 +01:00
2b6659f4ec Swipe down to close images 2023-01-11 22:28:14 +01:00
aminecmi
e0c118a73e Changelog for v122123641 [CI SKIP] 2023-01-04 19:27:56 +00:00
aminecmi
4e61b2aed6 feat: Disable the failing source in the filter sheet.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-01-03 21:35:21 +01:00
aminecmi
ba2758c0a3 feat: Display the source error in the sources list. 2023-01-03 21:28:40 +01:00
aminecmi
c718b966a1 Changelog for v122123631 [CI SKIP] 2022-12-30 19:24:48 +00:00
aminecmi
99438e142f build: Added back maven repos (see 1fb9d60dc5 (note_1223925153))
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-12-30 20:10:08 +01:00
aminecmi
4d8076c3cf build: Added back maven repos (see 1fb9d60dc5 (note_1223925153))
Some checks failed
continuous-integration/drone/push Build is failing
2022-12-29 21:34:13 +01:00
aminecmi
db75c5b74a debug: trying to resolve Canvas: trying to use a recycled bitmap.
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-29 20:40:39 +01:00
aminecmi
966a082147 fix: NPE may be caused by the binding or the title that was null.
Some checks are pending
continuous-integration/drone/push Build is running
2022-12-29 20:35:03 +01:00
aminecmi
cd20a5ec29 chore: Skip drone pipeline on changelog push.
Some checks reported errors
continuous-integration/drone/push Build was killed
2022-12-29 14:11:29 +01:00
aminecmi
cc4c1c9201 Changelog for v122123621
Some checks reported errors
continuous-integration/drone/push Build was killed
2022-12-29 13:08:23 +00:00
aminecmi
ff021d572c fix: Automatic CHANGELOG generation.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-12-29 13:11:23 +01:00
Amine Louveau
89992967be Merge pull request 'Sources Upsert' (#119) from sources-edit into master
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is failing
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/119
2022-12-28 21:31:49 +00:00
aminecmi
3c68bde62b Source update screen.
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-28 22:19:11 +01:00
aminecmi
c38251f5b3 Sources menu. 2022-12-28 21:45:00 +01:00
aminecmi
a01f6d2322 chore: Automatic CHANGELOG generation.
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-28 21:43:56 +01:00
Amine Louveau
417a33eb25 Merge pull request 'Running migrations.' (#118) from fix-migration into master
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/118
2022-12-28 14:49:11 +00:00
aminecmi
2e7f7f23b3 No duplicate builds for PRs.
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-28 15:34:22 +01:00
aminecmi
e5e182761e Running migrations.
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone/pr Build was killed
2022-12-28 15:27:17 +01:00
Amine Louveau
a094d88799 Merge pull request 'Make the author field nullable' (#117) from davidoskky/ReaderForSelfoss-multiplatform:author into master
Some checks reported errors
continuous-integration/drone/push Build was killed
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/117
2022-12-28 14:25:36 +00:00
e51915d1cd Include author field when updating the database
All checks were successful
continuous-integration/drone/pr Build is passing
2022-12-28 14:25:56 +01:00
3a654f6ede Migrate the database table 2022-12-28 14:25:34 +01:00
5227751dca Make the author field nullable
All checks were successful
continuous-integration/drone/pr Build is passing
2022-12-28 11:02:43 +01:00
aminecmi
27eafe4ff4 Delete sources from DB and reload items on source deletion.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-12-26 22:27:28 +01:00
aminecmi
8c83a9408b Drone should work better.
Some checks reported errors
continuous-integration/drone/push Build was killed
2022-12-26 22:26:28 +01:00
aminecmi
fe2410f719 Handling author field.
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is failing
2022-12-26 21:49:55 +01:00
aminecmi
a5e86bfb77 Date format issues.
Some checks failed
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is failing
2022-12-26 15:02:19 +01:00
aminecmi
23be633798 Add api version to the reports.
Some checks are pending
continuous-integration/drone/push Build is running
2022-12-25 22:45:12 +01:00
aminecmi
813e0707d8 Date format issue.
Some checks are pending
continuous-integration/drone/push Build is running
2022-12-25 22:41:34 +01:00
aminecmi
9ed9bf07fc Items in repository.
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-23 22:53:16 +01:00
aminecmi
47265c10d0 Trying nexus build.
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-23 14:59:58 +01:00
aminecmi
5cc633246a Debugging images issues.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-12-22 20:28:49 +01:00
aminecmi
1f40385786 Context should not be null, but handle the case for now.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-12-19 22:08:28 +01:00
aminecmi
eb2876324a This seems to be needed.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-12-19 20:47:04 +01:00
aminecmi
633b817d76 Remonving matomo.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-12-18 21:07:42 +01:00
aminecmi
2cfaa9b285 Logout fix.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-12-18 20:42:13 +01:00
aminecmi
f42ae97326 Explicitly failing for non selfoss rss files.
Some checks are pending
continuous-integration/drone/push Build is running
2022-12-18 20:41:17 +01:00
aminecmi
3b0028164b Glide update + trying requests.
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-17 22:25:23 +01:00
aminecmi
7420adeb5c Do not ignore git version.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-12-14 21:42:56 +01:00
aminecmi
316027ca3b Tag for build.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-12-14 21:26:33 +01:00
aminecmi
9d58fba5c9 Cleaning.
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone Build is passing
2022-12-14 21:07:03 +01:00
aminecmi
284c19ef89 More cleaning.
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-14 20:54:48 +01:00
aminecmi
7cfd17231a Cleaning.
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-13 22:22:31 +01:00
aminecmi
527830a5ae Merge branch 'sonar-qube'
Some checks are pending
continuous-integration/drone/push Build is running
2022-12-13 21:53:10 +01:00
aminecmi
c4ed30f594 Fixes #112.
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-13 21:32:48 +01:00
aminecmi
156c1681cf Fixes #111.
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-13 21:19:05 +01:00
aminecmi
3593fbca78 Sonar scanner.
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-13 21:11:38 +01:00
aminecmi
430fc8e8cb Fixes #110.
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-13 20:40:50 +01:00
aminecmi
4fce19bad4 Trying to set code coverage.
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-13 20:29:51 +01:00
Amine Louveau
49f5848e7b Merge pull request 'Fixes #108.' (#109) from bug/lateinit into master
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/109
2022-12-12 20:15:44 +00:00
aminecmi
90452100a4 Fixes #108.
Some checks reported errors
continuous-integration/drone/push Build was killed
continuous-integration/drone/pr Build was killed
2022-12-12 21:11:26 +01:00
aminecmi
bf1196dd0f Translations.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-12-09 20:27:38 +01:00
aminecmi
4316dc6516 Removing hidden tags.
Some checks are pending
continuous-integration/drone/push Build is running
2022-12-09 20:23:01 +01:00
aminecmi
9833a66a64 Cleaning tags duplications.
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-12-08 14:02:30 +01:00
aminecmi
797bf06a9c Retry to fix post login issues.
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-08 13:09:02 +01:00
Amine Louveau
d98b00533d Merge pull request 'filters' (#107) from filters into master
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/pulls/107
2022-12-07 19:13:46 +00:00
aminecmi
bf8f7d8667 Cleaning.
All checks were successful
continuous-integration/drone/pr Build is passing
continuous-integration/drone/push Build is passing
2022-12-07 12:11:58 +01:00
aminecmi
89c570f34f Fixing tests.
Some checks failed
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2022-12-06 22:36:15 +01:00
aminecmi
d6a562863a Big cleaning.
Some checks failed
continuous-integration/drone/push Build is failing
continuous-integration/drone/pr Build is failing
2022-12-06 22:18:38 +01:00
aminecmi
a02f06fe2e Big drawer cleaning. 2022-12-06 21:39:41 +01:00
aminecmi
7b088d7bb4 Hidden tags. 2022-12-06 21:39:41 +01:00
aminecmi
477883ed39 Tags sources reset. 2022-12-06 21:39:41 +01:00
aminecmi
748ed41096 Filters working. 2022-12-06 21:39:41 +01:00
aminecmi
86c50d4881 Loading sources and tags. 2022-12-06 21:39:41 +01:00
aminecmi
c4c92e6dd9 No need to login without password and username.
All checks were successful
continuous-integration/drone/push Build is passing
2022-12-06 21:39:32 +01:00
173 changed files with 8752 additions and 5015 deletions

View File

@ -1,124 +0,0 @@
kind: pipeline
type: docker
name: test
steps:
- name: AnylyseBuildTest
image: mingc/android-build-box:latest
commands:
- echo "---------------------------------------------------------"
- echo "Configure gradle..."
- mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=true\npushCache=false\nsystemProp.org.gradle.internal.http.connectionTimeout=180000\nsystemProp.org.gradle.internal.http.socketTimeout=180000" >> ~/.gradle/gradle.properties
- echo "---------------------------------------------------------"
- echo "Analysing..."
- ./gradlew sonarqube -Dsonar.projectKey=RFS2 -Dsonar.host.url=$SONAR_HOST_URL -Dsonar.login=$SONAR_LOGIN
- echo "---------------------------------------------------------"
- echo "Building..."
- ./gradlew build
- echo "---------------------------------------------------------"
- echo "Testing..."
- echo "---------------------------------------------------------"
- ./gradlew test
environment:
SONAR_HOST_URL:
from_secret: sonarScannerHostUrl
SONAR_LOGIN:
from_secret: sonarScannerLogin
trigger:
event:
- push
- pull_request
---
kind: pipeline
type: docker
name: Publish
steps:
- name: createTag
image: ubuntu:latest
commands:
- apt-get update && apt-get install -y git
- ./build.sh --publish --from-ci
- git remote add pushing https://$GITEA_USR:$GITEA_PASS@gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform.git
- git push pushing --tags
environment:
GITEA_USR:
from_secret: giteaUsr
GITEA_PASS:
from_secret: giteaPass
- name: scpFiles
image: appleboy/drone-scp
settings:
host: amine-louveau.fr
username: ubuntu
key:
from_secret: privateKey
port: 22
target: /home/ubuntu/
source: version.txt
- name: deploy
image: appleboy/drone-ssh
settings:
host: amine-louveau.fr
user: ubuntu
key:
from_secret: privateKey
command_timeout: 2m
script:
- cd /home/ubuntu
- sudo rm -rf /var/www/amine/version.txt
- sudo chown www-data:www-data ./version.txt
- sudo mv version.txt /var/www/amine/
trigger:
event:
- promote
target:
- production
---
kind: pipeline
type: docker
name: Release
steps:
- name: build
image: mingc/android-build-box:latest
commands:
- echo "---------------------------------------------------------"
- echo "Configure gradle..."
- mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=true\npushCache=false\nsystemProp.org.gradle.internal.http.connectionTimeout=180000\nsystemProp.org.gradle.internal.http.socketTimeout=180000" >> ~/.gradle/gradle.properties
- echo "---------------------------------------------------------"
- echo "Generate APK"
- ./gradlew :androidApp:assembleGithubConfigRelease -P pushCache=false
- echo "---------------------------------------------------------"
- echo "Get Key"
- wget https://amine-louveau.fr/key
- echo "---------------------------------------------------------"
- echo "Zipalign"
- $ANDROID_HOME/build-tools/31.0.0/zipalign -f -v 4 androidApp/build/outputs/apk/githubConfig/release/androidApp-githubConfig-release-unsigned.apk androidApp/build/outputs/apk/githubConfig/release/android-prod-released-ziped.apk
- echo "---------------------------------------------------------"
- echo "Sign"
- $ANDROID_HOME/build-tools/31.0.0/apksigner sign -v --out signed.apk --ks ./key --ks-key-alias $YOUR_KEY_ALIAS --ks-pass pass:$YOUR_KEYSTORE_PASSWORD --v1-signing-enabled true --v2-signing-enabled true androidApp/build/outputs/apk/githubConfig/release/android-prod-released-ziped.apk
- echo "---------------------------------------------------------"
- echo "Verify"
- $ANDROID_HOME/build-tools/31.0.0/apksigner verify signed.apk
environment:
YOUR_KEYSTORE_PASSWORD:
from_secret: keyPass
YOUR_KEY_ALIAS:
from_secret: keyAlias
- name: gitea_release
image: plugins/gitea-release
settings:
api_key:
from_secret: giteaAPI
base_url: https://gitea.amine-louveau.fr
files: signed.apk
trigger:
event:
- tag

36
.editorconfig Normal file
View File

@ -0,0 +1,36 @@
root = true
[*]
insert_final_newline = true
[.editorconfig]
insert_final_newline = false
ij_kotlin_line_break_after_multiline_when_entry = false
[*.{kt,kts}]
# Disable wildcard imports entirely
ij_kotlin_name_count_to_use_star_import = 2147483647
ij_kotlin_name_count_to_use_star_import_for_members = 2147483647
end_of_line = lf
ij_kotlin_allow_trailing_comma = true
ij_kotlin_allow_trailing_comma_on_call_site = true
ij_kotlin_imports_layout = *, java.**, javax.**, kotlin.**, ^
ij_kotlin_indent_before_arrow_on_new_line = false
ij_kotlin_line_break_after_multiline_when_entry = true
ij_kotlin_packages_to_use_import_on_demand = unset
indent_size = 4
indent_style = space
ktlint_argument_list_wrapping_ignore_when_parameter_count_greater_or_equal_than = unset
ktlint_chain_method_rule_force_multiline_when_chain_operator_count_greater_or_equal_than = 4
ktlint_class_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = 1
ktlint_code_style = ktlint_official
ktlint_enum_entry_name_casing = upper_or_camel_cases
ktlint_function_naming_ignore_when_annotated_with = unset
ktlint_function_signature_body_expression_wrapping = multiline
ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than = 2
ktlint_ignore_back_ticked_identifier = false
ktlint_property_naming_constant_naming = screaming_snake_case
max_line_length = 140
[**/build]
ktlint = disabled

View File

@ -0,0 +1,10 @@
version: '3'
services:
selfoss:
container_name: selfoss
image: rsprta/selfoss
network_mode: "host"
ports:
- "8888:8888"

View File

@ -0,0 +1,47 @@
name: Build
on:
workflow_call:
jobs:
BuildAndTestAndCoverage:
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fetch tags
run: git fetch --tags -p
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
cache: gradle
- uses: gradle/actions/setup-gradle@v3
- uses: android-actions/setup-android@v3
- name: Configure gradle...
run: mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=true" >> ~/.gradle/gradle.properties
- name: Build and test
run: ./gradlew build -x testReleaseUnitTest -x testDebugUnitTest -x testGithubConfigReleaseUnitTest -x testGithubConfigDebugUnitTest # These tests will be done
- uses: KengoTODA/actions-setup-docker-compose@v1
with:
version: "2.23.3"
# TESTS ARE RUN LOCALLY
# - name: run selfoss
# run: |
# docker compose -f .gitea/workflows/assets/docker-compose.yml up -d
- name: coverage
run: |
./gradlew :koverHtmlReport
- uses: actions/upload-artifact@v3
with:
name: coverage
path: build/reports/kover/html
retention-days: 1
overwrite: true
include-hidden-files: true
# TESTS ARE RUN LOCALLY
# - name: Clean
# if: always()
# run: |
# docker compose -f .gitea/workflows/assets/docker-compose.yml stop

View File

@ -0,0 +1,128 @@
name: Create tag
on:
push:
branches:
- release
workflow_dispatch:
jobs:
build:
uses: ./.gitea/workflows/common_build.yml
createTagAndChangelog:
runs-on: ubuntu-latest
needs: build
steps:
- name: Check out repository code
uses: actions/checkout@v4
with:
fetch-depth: 0
ref: master
- name: Config git
run: |
git config --global user.email aminecmi+giteadrone@pm.me
git config --global user.name giteadrone
- name: Creating the tag and generate changelog
run: |
git fetch --tags -p
PREV=$(git describe --tags --abbrev=0)
./build.sh --publish --from-ci
VER=$(git describe --tags --abbrev=0)
CHANGELOG=$(git log $PREV..HEAD --pretty="- %s")
echo "**$VER
$CHANGELOG
--------------------------------------------------------------------
$(cat CHANGELOG.md)" > CHANGELOG.md
git add CHANGELOG.md
touch ./fastlane/metadata/android/en\-US/changelogs/$VER.txt
echo "**$VER**
$CHANGELOG" > ./fastlane/metadata/android/en\-US/changelogs/$VER.txt
git add ./fastlane/metadata/android/en\-US/changelogs/$VER.txt
git commit -m "Changelog for $VER"
- name: Push changes
uses: appleboy/git-push-action@v1.0.0
with:
author_name: giteadrone
author_email: aminecmi+giteadrone@pm.me
remote: ${{ secrets.REMOTE_URL }}
followtags: true
ssh_key: ${{ secrets.PRIVATE_KEY }}
tags: true
branch: master
- name: copy file via ssh password
uses: appleboy/scp-action@v0.1.7
with:
host: amine-bouabdallaoui.fr
username: ubuntu
key: ${{ secrets.PRIVATE_KEY }}
source: "version.txt"
target: "/home/ubuntu/"
- name: deploy version file
uses: appleboy/ssh-action@v1.2.0
with:
host: amine-bouabdallaoui.fr
username: ubuntu
key: ${{ secrets.PRIVATE_KEY }}
script: cd /home/ubuntu && sudo rm -rf /var/www/amine/version.txt && sudo chown www-data:www-data ./version.txt && sudo mv version.txt /var/www/amine/
release:
runs-on: ubuntu-latest
needs: createTagAndChangelog
steps:
- name: Check out repository code
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Fetch tags
id: version
run: |
git fetch --tags -p
PREV=$(git describe --tags --abbrev=0)
echo $PREV
echo "VERSION=$PREV" >> $GITHUB_OUTPUT
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
cache: gradle
- name: Setup Android SDK
uses: android-actions/setup-android@v3
- name: Configure gradle...
run: mkdir -p ~/.gradle && echo "org.gradle.daemon=false\nignoreGitVersion=false\nsystemProp.org.gradle.internal.http.connectionTimeout=180000\nsystemProp.org.gradle.internal.http.socketTimeout=180000" >> ~/.gradle/gradle.properties
- name: setup go
uses: https://github.com/actions/setup-go@v4
with:
go-version: '>=1.20.1'
- name: Generate APK
run: ./gradlew :androidApp:assembleGithubConfigRelease
- name: Get Key
run: wget ${{ secrets.KEY_URL }}
- name: Zippalign
run: |
sdkmanager "build-tools;31.0.0"
ls $ANDROID_HOME/build-tools
$ANDROID_HOME/build-tools/31.0.0/zipalign -f -v 4 androidApp/build/outputs/apk/githubConfig/release/androidApp-githubConfig-release-unsigned.apk androidApp/build/outputs/apk/githubConfig/release/android-prod-released-ziped.apk
- name: Sigh
run: $ANDROID_HOME/build-tools/31.0.0/apksigner sign -v --out signed.apk --ks ./key --ks-key-alias ${{ secrets.KEY_ALIAS }} --ks-pass pass:${{ secrets.KEYSTORE_PASSWORD }} --v1-signing-enabled true --v2-signing-enabled true androidApp/build/outputs/apk/githubConfig/release/android-prod-released-ziped.apk
- name: Verify
run: $ANDROID_HOME/build-tools/31.0.0/apksigner verify signed.apk
- name: Release
uses: https://gitea.com/actions/gitea-release-action@main
with:
files: signed.apk
token: ${{ secrets.API_KEY }}
tag_name: ${{ steps.version.outputs.VERSION }}
name: ${{ steps.version.outputs.VERSION }}
- name: Send mail
uses: https://github.com/dawidd6/action-send-mail@v4
with:
connection_url: ${{ secrets.MAIL_CONNECTION }}
to: ${{ secrets.MAIL_TO }}
from: ${{ secrets.MAIL_FROM }}
subject: Mapping file
priority: high
convert_markdown: true
body: Nouveau fichier de mapping pour la version ${{ steps.version.outputs.VERSION }}
attachments: androidApp/build/outputs/mapping/githubConfigRelease/mapping.txt

View File

@ -0,0 +1,28 @@
name: Check PR code
on:
pull_request:
branches:
- master
jobs:
Lint:
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: 'temurin'
java-version: '17'
cache: gradle
- name: Install klint
run: curl -sSLO https://github.com/pinterest/ktlint/releases/download/1.5.0/ktlint && chmod a+x ktlint && mv ktlint /usr/local/bin/
- name: Install detekt
run: curl -sSLO https://github.com/detekt/detekt/releases/download/v1.23.7/detekt-cli-1.23.7.zip && unzip detekt-cli-1.23.7.zip
- name: Linting...
run: ktlint 'shared/**/*.kt' 'androidApp/**/*.kt' '!shared/build'
- name: Detecting...
run: ./detekt-cli-1.23.7/bin/detekt-cli -c detekt.yml --excludes '**/shared/build/**/*.kt'
build:
needs: Lint
uses: ./.gitea/workflows/common_build.yml

View File

@ -0,0 +1,9 @@
name: Check master code
on:
push:
branches:
- master
jobs:
build:
uses: ./.gitea/workflows/common_build.yml

View File

@ -10,7 +10,7 @@ Please read the guidelines before contributing, and follow them (or try to) when
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. 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://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/issues?q=is%3Aissue+is%3Aopen+label%3A%22Up+For+Grabs%22) or [develop new things](https://gitea.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform/issues) You can fork the repository, and [help me solve some issues](https://gitea.amine-bouabdallaoui.fr/Louvorg/ReaderForSelfoss-multiplatform/issues?q=is%3Aissue+is%3Aopen+label%3A%22Up+For+Grabs%22) or [develop new things](https://gitea.amine-bouabdallaoui.fr/Louvorg/ReaderForSelfoss-multiplatform/issues)
### What I can't help you with. ### What I can't help you with.

5
.gitignore vendored
View File

@ -321,3 +321,8 @@ fabric.properties
crowdin.properties crowdin.properties
.kotlin/
build-cache/
act

View File

@ -1,3 +1,411 @@
**v125020471
- chore: no more docker-compose.
- bump: gradle plugin.
- Merge pull request 'fix: check index exists.' (#183) from fix-index into master
- fix: check index exists.
- Changelog for v125020411
--------------------------------------------------------------------
**v125020411
- Merge pull request 'bump' (#182) from bump into master
- chore: non transiant R classes.
- Merge pull request 'fix: One more missing context.' (#181) from fix-one-more-context into master
- bump
- fix: One more missing context.
--------------------------------------------------------------------
**v125010241
- Merge pull request 'fix: Link not opening.' (#178) from fix-open-link into master
- refactor: context fragments issues.
- logs: Context issues.
- fix: Handle empty url issue, again.
- fix: Link not opening.
- Changelog for v125010201
--------------------------------------------------------------------
**v125010201
- fix: Handle empty url issue.
- Merge pull request 'Removed the floating bar.' (#177) from floating-bar into master
- chore: changing actions in reader fragment.
- Changelog for v125010131
--------------------------------------------------------------------
**v125010131
- fix: reload the adapter when it's needed. Fixes #128. (#176)
- feat: basic auth and images loading. Fixes #172. (#175)
- Changelog for v125010111
--------------------------------------------------------------------
**v125010111
- Debug trying to fix context issues. (#174)
- Changelog for v125010031
--------------------------------------------------------------------
**v125010031
- Merge pull request 'Bump dependencies' (#173) from upgarde into master
- chore: "faster" action.
- fastlane: icon change.
- chore: ignoring a pixel issue.
- test: fixed an ui test issue.
- fix: center the loading thing.
- test: items displaying.
- bump: sqldelight.
- bump: material, desugar jdk, jsoup, kodein, settings, napier, mock.
- bump: androix and coroutines.
- bump: ktor. Closes #67.
- Changelog for v124123651
--------------------------------------------------------------------
**v124123651
- Merge pull request 'Bugfixes' (#171) from bugfixes into master
- config: crowdin
- chore: can links be empty ?
- fix: Context issues in article fragment.
- fix: Context issues in fragment sheet.
- fix: build.
- chore: compile issue fix.
- chore: filter some bugs.
- bugfix: catch users using something other than selfoss.
- bugfix: No browser, no link.
- translations
- chore: remove log.
- translation
- Changelog for v124123641
--------------------------------------------------------------------
**v124123641
- Chore: no tests on build.
- Merge pull request 'testing' (#170) from testing into master
- fix: Displaying fixes. Fixes #155
- test: coverage
- chore: update and use multiplatform datetime
- Changelog for v124123421
--------------------------------------------------------------------
**v124123421
- fix: Trying to fix the serialization issue.
- Changelog for v124113311
--------------------------------------------------------------------
**v124113311
- chore: update versions. (#165)
- chore: fastlane changelog.
- chore: fastlane fixes.
- Changelog for v124113301
--------------------------------------------------------------------
**v124113301**
- chore: Gitea Action
- Merge pull request 'chore: Gitea Action' (#164) from runner into master
- chore: Gitea Action
- chore: Readme update.
--------------------------------------------------------------------
**v124041081**
- chore: comment.
- fix: Last time fixing the parsing date hack before moving it to os version.
- Changelog for v124030731 [CI SKIP]
--------------------------------------------------------------------
**v124030731**
- fix: Basic auth and password can have non whitspace characters. Fixes 142.
- Changelog for v124020451 [CI SKIP]
--------------------------------------------------------------------
**v124020451**
- fix: Fixed handling of position in card adapter.
- Changelog for v124010301 [CI SKIP]
--------------------------------------------------------------------
**v124010301**
- fix: This may fix the oom errors.
- Changelog for v124010191 [CI SKIP]
--------------------------------------------------------------------
**v124010191**
- fix: moving listeners.
- chore: removed a useless log.
- Changelog for v124010032 [CI SKIP]
--------------------------------------------------------------------
**v124010032**
- fix: Another date format thing.
- Changelog for v124010031 [CI SKIP]
--------------------------------------------------------------------
**v124010031**
- fix: Checking if selfoss instance.
- fix: handle three characters lenght hexcode colors.
- Changelog for v123113311 [CI SKIP]
--------------------------------------------------------------------
**v123113311**
- chore: Source tracker url in the menu.
- fix: Handle kodein proguard rules.
- Changelog for v123102961 [CI SKIP]
--------------------------------------------------------------------
**v123102961**
- chore: domain changes.
- Changelog for v123102852 [CI SKIP]
--------------------------------------------------------------------
**v123102852**
- chore: lint cleaning.
- Changelog for v123102841 [CI SKIP]
--------------------------------------------------------------------
**v123102841**
- chore: cleaning ci steps and upgrading dependencies.
- feat: Self signed ssl support.
- Changelog for v123061811 [CI SKIP]
--------------------------------------------------------------------
**v123061811**
- feat: Added confirmation dialog for disconnect item menu.
- Changelog for v123061651 [CI SKIP]
--------------------------------------------------------------------
**v123061651**
- i18n: Translation update.
- i18n: Translation update.
- i18n: Translation update.
- fix: avoid trying to open invalid image urls.
- Changelog for v123051471 [CI SKIP]
--------------------------------------------------------------------
**v123051471**
- fix: images could be null.
- fix: Check if color is not empty before parsing it.
- chore: Removed unused log.
- Changelog for v123051331 [CI SKIP]
--------------------------------------------------------------------
**v123051331**
- fix: illegal input.
- Changelog for v123051321 [CI SKIP]
--------------------------------------------------------------------
**v123051321**
- debug: Debug null context.
- Changelog for v123051301 [CI SKIP]
--------------------------------------------------------------------
**v123051301**
- feat: Basic auth from url. Fixes #142 (#143)
- debug: Debug index out of bound exception.
- Changelog for v123051211 [CI SKIP]
--------------------------------------------------------------------
**v123051211**
- fix: Sometimes url isn't even defined.
- Changelog for v123041021 [CI SKIP]
--------------------------------------------------------------------
**v123041021**
- fix: 'Enable Core Library Desugaring to support older Android versions' (#138) from davidoskky/ReaderForSelfoss-multiplatform:desugaring into master
- Enable Core Library Desugaring to support older Android versions
- Changelog for v123030851 [CI SKIP]
--------------------------------------------------------------------
**v123030851**
- chore: replace textDrawable library (#136)
- refactor: Remove slow login check. Closes #135.
- ci: send the mapping file after a release.
- Changelog for v123030751 [CI SKIP]
--------------------------------------------------------------------
**v123030751**
- debug: added a lot to pinpoint the url issue.
- feat: Use /sources/stats in the home (#133)
- Changelog for v123030681 [CI SKIP]
--------------------------------------------------------------------
**v123030681**
- fix: Unread and starred can be null.
- Fixed version number issue.
- Changelog for v123030621 [CI SKIP]
--------------------------------------------------------------------
**v123030621**
- fix: url required issue.
- fix: Canvas reused issue.
- Changelog for v123020572 [CI SKIP]
--------------------------------------------------------------------
**v123020572**
- fix: requirecontext issues ?
- debug: activity not found exception.
- Changelog for v123020571 [CI SKIP]
--------------------------------------------------------------------
**v123020571**
- chore: remove errors logging.
- fix: quickfix for url param not provided for some sources.
- Update 'CHANGELOG.md'
- Changelog for v123020523 [CI SKIP]
--------------------------------------------------------------------
**v123020523**
- fix: Git changelog.
--------------------------------------------------------------------
**v123020491**
- fix: Fixed acra bug reporting.
--------------------------------------------------------------------
**v123010301**
- Chore: acra config.
--------------------------------------------------------------------
**v123010281**
- improvement: Improve right to left support (#130) Co-authored-by: davidoskky <davidoskky@hidden.hidden> Co-committed-by: davidoskky <davidoskky@hidden.hidden>
--------------------------------------------------------------------
**v123010261**
- feat: Handle public instances (#126) Co-authored-by: davidoskky <davidoskky@hidden.hidden> Co-committed-by: davidoskky <davidoskky@hidden.hidden>
- ci: Pull request should trigger ci.
- fix: Complete the disconnection before redirecting to the login screen
- Complete the disconnection before redirecting to the login screen
--------------------------------------------------------------------
**v123010241**
- Merge pull request 'feat: swipe down to close images' (#122) from davidoskky/ReaderForSelfoss-multiplatform:swipe_down into master
- Remove unnecessary definition
- Remove unused import
- Adjust the image closing animation
- Add a dark hue to the underlying article when swiping to close images
- Rename activity style to avoid interferences
- Adapt the style of the image activity to the rest of the application
- Resolve issues when swiping down to close images
- Close the image fragment only if the image has been dragged down
- Animate swipe down to close images
- Swipe down to close images
--------------------------------------------------------------------
**v123010041**
- Merge pull request 'scroll-tag-filters' (#124) from scroll-tag-filters into master
- fix: added POST_NOTIFICATIONS to fix notifications issues.
- fix: scrollable filter sheet.
- enhancement: Ellipsize chips text.
- Cleaning.
--------------------------------------------------------------------
**v122123641**
- feat: Disable the failing source in the filter sheet.
- feat: Display the source error in the sources list.
--------------------------------------------------------------------
**v122123631**
- build: Added back maven repos (see https://gitlab.com/fdroid/fdroiddata/-/commit/1fb9d60dc58511abc2bb4eb321977922a0682c8b#note_1223925153)
- build: Added back maven repos (see https://gitlab.com/fdroid/fdroiddata/-/commit/1fb9d60dc58511abc2bb4eb321977922a0682c8b#note_1223925153)
- debug: trying to resolve `Canvas: trying to use a recycled bitmap`.
- fix: NPE may be caused by the binding or the title that was null.
- chore: Skip drone pipeline on changelog push.
--------------------------------------------------------------------
**v122123621**
- fix: Automatic CHANGELOG generation.
- Merge pull request 'Sources Upsert' (#119) from sources-edit into master
- Source update screen.
- Sources menu.
- chore: Automatic CHANGELOG generation.
--------------------------------------------------------------------
# V2/Multiplatform rewrite # V2/Multiplatform rewrite
**v1** **v1**

View File

@ -1,4 +1,4 @@
# ReaderForSelfoss-multiplatform [![Build Status](https://build.amine-louveau.fr/api/badges/Louvorg/ReaderForSelfoss-multiplatform/status.svg)](https://build.amine-louveau.fr/Louvorg/ReaderForSelfoss-multiplatform) # ReaderForSelfoss-multiplatform [![Build Status](https://gitea.amine-bouabdallaoui.fr/Louvorg/ReaderForSelfoss-multiplatform/actions/workflows/on_push.yml/badge.svg)](https://gitea.amine-bouabdallaoui.fr/Louvorg/ReaderForSelfoss-multiplatform/actions?workflow=on_push.yml&actor=0&status=0)
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/readerforselfoss/localized.svg)](https://crowdin.com/project/readerforselfoss) [![Crowdin](https://d322cqt584bo4o.cloudfront.net/readerforselfoss/localized.svg)](https://crowdin.com/project/readerforselfoss)
@ -10,10 +10,6 @@ If you are a user, you can still create new issues. I'll fix them when I can.
<a href="https://f-droid.org/packages/bou.amine.apps.readerforselfossv2.android"><img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="100"></a> <a href="https://f-droid.org/packages/bou.amine.apps.readerforselfossv2.android"><img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="100"></a>
## Screen captures
<img src="res//fr-card.png?raw=true" alt="card view" width="400"/> <img src="res//fr-list.png?raw=true" alt="list view" width="400"/>
## Like my app ? ## Like my app ?
<a href="https://www.buymeacoffee.com/aminecmi" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/lato-orange.png" alt="Buy Me A Coffee" style="height: 51px !important;width: 217px !important;" ></a> <a href="https://www.buymeacoffee.com/aminecmi" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/lato-orange.png" alt="Buy Me A Coffee" style="height: 51px !important;width: 217px !important;" ></a>
@ -22,15 +18,15 @@ If you are a user, you can still create new issues. I'll fix them when I can.
1. **You'll have to have a Selfoss instance running.** You'll find everything you need to install it [here](https://selfoss.aditu.de/). 1. **You'll have to have a Selfoss instance running.** You'll find everything you need to install it [here](https://selfoss.aditu.de/).
2. Check the [Contribution guide](https://gitea.amine-louveau.fr/Louvorg/ReaderforSelfoss-multiplatform/src/branch/master/.github/CONTRIBUTING.md). 2. Check the [Contribution guide](https://gitea.amine-bouabdallaoui.fr/Louvorg/ReaderforSelfoss-multiplatform/src/branch/master/.github/CONTRIBUTING.md).
3. Build the project by following [these steps](https://gitea.amine-louveau.fr/Louvorg/ReaderforSelfoss-multiplatform/src/branch/master/.github/CONTRIBUTING.md#build-the-project) (you should have read them after the contribution guide) 3. Build the project by following [these steps](https://gitea.amine-bouabdallaoui.fr/Louvorg/ReaderforSelfoss-multiplatform/src/branch/master/.github/CONTRIBUTING.md#build-the-project) (you should have read them after the contribution guide)
## Useful links ## Useful links
- [Check what changed](https://gitea.amine-louveau.fr/Louvorg/ReaderforSelfoss-multiplatform/src/branch/master/CHANGELOG.md) - [Check what changed](https://gitea.amine-bouabdallaoui.fr/Louvorg/ReaderforSelfoss-multiplatform/src/branch/master/CHANGELOG.md)
- [See what I'm doing](https://gitea.amine-louveau.fr/Louvorg/ReaderforSelfoss-multiplatform/projects/1) - [See what I'm doing](https://gitea.amine-bouabdallaoui.fr/Louvorg/ReaderforSelfoss-multiplatform/projects/1)
- [Create an issue, or request a new feature](https://gitea.amine-louveau.fr/Louvorg/ReaderforSelfoss-multiplatform/issues) - [Create an issue, or request a new feature](https://gitea.amine-bouabdallaoui.fr/Louvorg/ReaderforSelfoss-multiplatform/issues)
- [Help translation the app](https://crowdin.com/project/readerforselfoss) - [Help translation the app](https://crowdin.com/project/readerforselfoss)
## Contributors (V1) (Alphabetical order) ❤️ ## Contributors (V1) (Alphabetical order) ❤️

View File

@ -1 +1,2 @@
/build /build
.kotlin/

View File

@ -1,21 +1,23 @@
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
val ignoreGitVersion: String by project val ignoreGitVersion: String by project
val acraVersion = "5.9.7" val acraVersion = "5.12.0"
plugins { plugins {
id("com.android.application") id("com.android.application")
kotlin("android") kotlin("android")
kotlin("kapt") kotlin("kapt")
id("com.mikepenz.aboutlibraries.plugin") id("com.mikepenz.aboutlibraries.plugin")
id("org.jetbrains.kotlinx.kover")
id("app.cash.sqldelight") version "2.0.2"
} }
fun Project.execWithOutput(cmd: String, ignore: Boolean = false): String { fun Project.execWithOutput(cmd: String, ignore: Boolean = false): String {
var result: String = ByteArrayOutputStream().use { outputStream -> val result: String = ByteArrayOutputStream().use { outputStream ->
project.exec { project.exec {
commandLine = cmd.split(" ") commandLine = cmd.split(" ")
standardOutput = outputStream standardOutput = outputStream
isIgnoreExitValue = ignore ?: false isIgnoreExitValue = ignore
} }
outputStream.toString() outputStream.toString()
} }
@ -23,11 +25,10 @@ fun Project.execWithOutput(cmd: String, ignore: Boolean = false): String {
} }
fun gitVersion(): String { fun gitVersion(): String {
var process = ""
val maybeTagOfCurrentCommit = execWithOutput("git -C ../ describe --contains HEAD", true) val maybeTagOfCurrentCommit = execWithOutput("git -C ../ describe --contains HEAD", true)
process = if (maybeTagOfCurrentCommit.isEmpty()) { val process = if (maybeTagOfCurrentCommit.isEmpty()) {
println("No tag on current commit. Will take the latest one.") println("No tag on current commit. Will take the latest one.")
execWithOutput("git -C ../ for-each-ref refs/tags --sort=-authordate --format='%(refname:short)' --count=1") execWithOutput("git -C ../ for-each-ref refs/tags --sort=-refname --format='%(refname:short)' --count=1")
} else { } else {
println("Tag found on current commit") println("Tag found on current commit")
execWithOutput("git -C ../ describe --contains HEAD") execWithOutput("git -C ../ describe --contains HEAD")
@ -55,24 +56,24 @@ fun versionNameFromGit(): String {
android { android {
compileOptions { compileOptions {
isCoreLibraryDesugaringEnabled = true
// Flag to enable support for the new language APIs // Flag to enable support for the new language APIs
sourceCompatibility = JavaVersion.VERSION_11 sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_11 targetCompatibility = JavaVersion.VERSION_17
} }
// For Kotlin projects // For Kotlin projects
kotlinOptions { kotlinOptions {
jvmTarget = "11" jvmTarget = "17"
} }
compileSdk = 33 compileSdk = 35
buildToolsVersion = "31.0.0"
buildFeatures { buildFeatures {
viewBinding = true viewBinding = true
} }
defaultConfig { defaultConfig {
applicationId = "bou.amine.apps.readerforselfossv2.android" applicationId = "bou.amine.apps.readerforselfossv2.android"
minSdk = 21 minSdk = 25
targetSdk = 33 targetSdk = 34 // 35 when edge-to-edge is handled
versionCode = versionCodeFromGit() versionCode = versionCodeFromGit()
versionName = versionNameFromGit() versionName = versionNameFromGit()
@ -84,8 +85,9 @@ android {
// tests // tests
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunnerArguments["clearPackageData"] = "true"
} }
packagingOptions { packaging {
resources { resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}" excludes += "/META-INF/{AL2.0,LGPL2.1}"
} }
@ -107,31 +109,37 @@ android {
} }
} }
namespace = "bou.amine.apps.readerforselfossv2.android" namespace = "bou.amine.apps.readerforselfossv2.android"
testOptions {
animationsDisabled = true
execution = "ANDROIDX_TEST_ORCHESTRATOR"
unitTests {
isIncludeAndroidResources = true
}
}
} }
dependencies { dependencies {
implementation(project(":shared")) coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
implementation("com.google.android.material:material:1.5.0")
implementation("androidx.appcompat:appcompat:1.4.1")
implementation("androidx.constraintlayout:constraintlayout:2.1.3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0")
implementation("androidx.preference:preference-ktx:1.1.1") implementation(project(":shared"))
implementation("androidx.appcompat:appcompat:1.7.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.10.1")
implementation("androidx.preference:preference-ktx:1.2.1")
implementation(fileTree(mapOf("include" to listOf("*.jar"), "dir" to "libs"))) implementation(fileTree(mapOf("include" to listOf("*.jar"), "dir" to "libs")))
// Android Support // Android Support
implementation("androidx.appcompat:appcompat:1.4.1") implementation("com.google.android.material:material:1.12.0")
implementation("com.google.android.material:material:1.5.0") implementation("androidx.recyclerview:recyclerview:1.4.0-rc01")
implementation("androidx.recyclerview:recyclerview:1.3.0-alpha01")
implementation("androidx.legacy:legacy-support-v4:1.0.0") implementation("androidx.legacy:legacy-support-v4:1.0.0")
implementation("androidx.vectordrawable:vectordrawable:1.2.0-alpha02") implementation("androidx.vectordrawable:vectordrawable:1.2.0")
implementation("androidx.cardview:cardview:1.0.0") implementation("androidx.cardview:cardview:1.0.0")
implementation("androidx.annotation:annotation:1.3.0") implementation("androidx.annotation:annotation:1.9.1")
implementation("androidx.work:work-runtime-ktx:2.7.1") implementation("androidx.work:work-runtime-ktx:2.10.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.3") implementation("androidx.constraintlayout:constraintlayout:2.2.0")
implementation("org.jsoup:jsoup:1.14.3") implementation("org.jsoup:jsoup:1.18.3")
//multidex //multidex
implementation("androidx.multidex:multidex:2.0.1") implementation("androidx.multidex:multidex:2.0.1")
@ -142,37 +150,33 @@ dependencies {
// Material-ish things // Material-ish things
implementation("com.ashokvarma.android:bottom-navigation-bar:2.2.0") implementation("com.ashokvarma.android:bottom-navigation-bar:2.2.0")
implementation("com.amulyakhare:com.amulyakhare.textdrawable:1.0.1")
// glide // glide
kapt("com.github.bumptech.glide:compiler:4.11.0") kapt("com.github.bumptech.glide:compiler:4.16.0")
implementation("com.github.bumptech.glide:okhttp3-integration:4.1.1") implementation("com.github.bumptech.glide:okhttp3-integration:4.16.0")
// Drawer
implementation("com.mikepenz:materialdrawer:8.4.5")
// Themes // Themes
implementation("com.github.rubensousa:floatingtoolbar:1.5.1") implementation("com.leinardi.android:speed-dial:3.3.0")
// Pager // Pager
implementation("me.relex:circleindicator:2.1.6") implementation("me.relex:circleindicator:2.1.6")
implementation("androidx.viewpager2:viewpager2:1.1.0-beta01") implementation("androidx.viewpager2:viewpager2:1.1.0")
//Dependency Injection //Dependency Injection
implementation("org.kodein.di:kodein-di:7.14.0") implementation("org.kodein.di:kodein-di:7.23.1")
implementation("org.kodein.di:kodein-di-framework-android-x:7.14.0") implementation("org.kodein.di:kodein-di-framework-android-x:7.23.1")
implementation("org.kodein.di:kodein-di-framework-android-x-viewmodel:7.14.0") implementation("org.kodein.di:kodein-di-framework-android-x-viewmodel:7.23.1")
//Settings //Settings
implementation("com.russhwolf:multiplatform-settings-no-arg:0.9") implementation("com.russhwolf:multiplatform-settings-no-arg:1.3.0")
//Logging //Logging
implementation("io.github.aakira:napier:2.6.1") implementation("io.github.aakira:napier:2.7.1")
//PhotoView //PhotoView
implementation("com.github.chrisbanes:PhotoView:2.3.0") implementation("com.github.chrisbanes:PhotoView:2.3.0")
implementation("androidx.core:core-ktx:1.8.0") implementation("androidx.core:core-ktx:1.15.0")
implementation("androidx.lifecycle:lifecycle-extensions:2.2.0") implementation("androidx.lifecycle:lifecycle-extensions:2.2.0")
@ -180,19 +184,25 @@ dependencies {
implementation("com.github.ln-12:multiplatform-connectivity-status:1.3.0") implementation("com.github.ln-12:multiplatform-connectivity-status:1.3.0")
// SQLDELIGHT // SQLDELIGHT
implementation("com.squareup.sqldelight:android-driver:1.5.4") implementation("app.cash.sqldelight:android-driver:2.0.2")
//test //test
testImplementation("junit:junit:4.13.2") testImplementation("junit:junit:4.13.2")
testImplementation("io.mockk:mockk:1.12.0") testImplementation("io.mockk:mockk:1.13.14")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0") testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.1")
implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0") implementation("org.jetbrains.kotlinx:kotlinx-datetime:0.6.1")
androidTestImplementation("androidx.test:runner:1.6.2")
androidTestImplementation("androidx.test:rules:1.6.1")
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
implementation("androidx.test.espresso:espresso-idling-resource:3.6.1")
androidTestImplementation("androidx.test.ext:junit-ktx:1.2.1")
androidTestUtil("androidx.test:orchestrator:1.5.1")
testImplementation("org.robolectric:robolectric:4.14.1")
testImplementation("androidx.test:core-ktx:1.6.1")
implementation("ch.acra:acra-http:$acraVersion") implementation("ch.acra:acra-http:$acraVersion")
implementation("ch.acra:acra-toast:$acraVersion") implementation("ch.acra:acra-toast:$acraVersion")
implementation("com.google.auto.service:auto-service:1.1.1")
// Matomo
implementation("com.github.matomo-org:matomo-sdk-android:4.1.4")
} }
tasks.withType<Test> { tasks.withType<Test> {

View File

@ -55,6 +55,7 @@
# maybe remove later ? # maybe remove later ?
-keep class * extends androidx.fragment.app.Fragment -keep class * extends androidx.fragment.app.Fragment
-dontwarn org.slf4j.impl.StaticLoggerBinder
# Keep `Companion` object fields of serializable classes. # Keep `Companion` object fields of serializable classes.
# This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects. # This avoids serializer lookup through `getDeclaredClasses` as done for named companion objects.
@ -85,3 +86,12 @@
-dontwarn io.mockk.** -dontwarn io.mockk.**
-keep class io.mockk.** { *; } -keep class io.mockk.** { *; }
# Kodein
-keep, allowobfuscation, allowoptimization class org.kodein.type.TypeReference
-keep, allowobfuscation, allowoptimization class org.kodein.type.JVMAbstractTypeToken$Companion$WrappingTest
-keep, allowobfuscation, allowoptimization class * extends org.kodein.type.TypeReference
-keep, allowobfuscation, allowoptimization class * extends org.kodein.type.JVMAbstractTypeToken$Companion$WrappingTest

View File

@ -0,0 +1,121 @@
package bou.amine.apps.readerforselfossv2.android
import android.content.Context
import androidx.annotation.ArrayRes
import androidx.test.espresso.Espresso.onData
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.replaceText
import androidx.test.espresso.action.ViewActions.typeTextIntoFocusedView
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isChecked
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isNotChecked
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.Matchers.hasToString
fun performLogin(someUrl: String? = null) {
onView(withId(R.id.urlView)).perform(click()).perform(
typeTextIntoFocusedView(
if (!someUrl.isNullOrEmpty()) someUrl else "http://10.0.2.2:8888",
),
)
onView(withId(R.id.signInButton)).perform(click())
}
fun loginAndInitHome() {
performLogin()
onView(withText(R.string.gdpr_dialog_title)).check(matches(isDisplayed()))
onView(withText("OK")).perform(click())
}
fun changeAndCancelSetting(
oldValue: String,
newValue: String,
openSettingItem: () -> Unit,
) {
openSettingItem()
onView(
withId(android.R.id.edit),
).perform(replaceText(newValue))
onView(
withId(android.R.id.button2),
).perform(click())
openSettingItem()
onView(
withId(android.R.id.edit),
).check(matches(withText(oldValue)))
onView(
withText(newValue),
).check(doesNotExist())
onView(
withId(android.R.id.button2),
).perform(click())
}
fun changeAndSaveSetting(
oldValue: String,
newValue: String,
openSettingItem: () -> Unit,
) {
openSettingItem()
onView(
withId(android.R.id.edit),
).perform(replaceText(newValue))
onView(
withId(android.R.id.button1),
).perform(click())
openSettingItem()
onView(
withId(android.R.id.edit),
).check(matches(withText(newValue)))
if (oldValue.isNotEmpty()) {
onView(
withText(oldValue),
).check(doesNotExist())
}
onView(
withId(android.R.id.button2),
).perform(click())
}
fun testPreferencesFromArray(
context: Context,
@ArrayRes arrayRes: Int,
openSettingItem: () -> Unit,
) {
openSettingItem()
context.resources.getStringArray(arrayRes).forEach { res ->
onView(withText(res)).check(matches(allOf(isDisplayed(), isNotChecked())))
onView(withText(res)).perform(click())
onView(withText(res)).check(doesNotExist())
openSettingItem()
onView(withText(res)).check(matches(allOf(isDisplayed(), isChecked())))
}
}
fun testAddSourceWithUrl(
url: String,
sourceName: String,
) {
onView(withId(R.id.fab))
.perform(click())
onView(withId(R.id.nameInput))
.perform(click())
.perform(typeTextIntoFocusedView(sourceName))
onView(withId(R.id.sourceUri))
.perform(click())
.perform(typeTextIntoFocusedView(url))
onView(withId(R.id.tags))
.perform(click())
.perform(typeTextIntoFocusedView("tag1,tag2,tag3"))
onView(withId(R.id.spoutsSpinner))
.perform(click())
onData(hasToString("RSS Feed")).perform(click())
onView(withId(R.id.saveBtn))
.perform(click())
onView(withText(sourceName)).check(matches(isDisplayed()))
}

View File

@ -0,0 +1,110 @@
package bou.amine.apps.readerforselfossv2.android
import android.content.Context
import android.view.View
import android.widget.EditText
import android.widget.ImageView
import android.widget.RelativeLayout
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.core.graphics.drawable.toBitmap
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
import androidx.test.espresso.Root
import androidx.test.espresso.matcher.RootMatchers.isPlatformPopup
import androidx.test.espresso.matcher.ViewMatchers.hasSibling
import androidx.test.espresso.matcher.ViewMatchers.withChild
import androidx.test.espresso.matcher.ViewMatchers.withClassName
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withParent
import androidx.test.espresso.matcher.ViewMatchers.withResourceName
import androidx.test.espresso.matcher.ViewMatchers.withText
import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.Description
import org.hamcrest.Matcher
import org.hamcrest.Matchers
import org.hamcrest.TypeSafeMatcher
fun withError(
@StringRes id: Int,
): TypeSafeMatcher<View?> {
return object : TypeSafeMatcher<View?>() {
override fun matchesSafely(view: View?): Boolean {
if (view != null && (view !is EditText || view.error == null)) {
return false
}
val context = view!!.context
return (view as EditText).error.toString() == context.getString(id)
}
override fun describeTo(description: Description?) {
// Nothing
}
}
}
fun isPopupWindow(): Matcher<Root> = isPlatformPopup()
fun withDrawable(
@DrawableRes id: Int,
) = object : TypeSafeMatcher<View>() {
override fun describeTo(description: Description) {
description.appendText("ImageView with drawable same as drawable with id $id")
}
@Suppress("detekt:SwallowedException")
override fun matchesSafely(view: View): Boolean {
val context = view.context
val expectedBitmap = context.getDrawable(id)!!.toBitmap()
try {
return view is ImageView && view.drawable.toBitmap().sameAs(expectedBitmap)
} catch (e: Exception) {
return false
}
}
}
fun hasBottombarItemText(
@StringRes id: Int,
): Matcher<View>? =
allOf(
withResourceName("fixed_bottom_navigation_icon"),
withParent(
allOf(
withResourceName("fixed_bottom_navigation_icon_container"),
hasSibling(withText(id)),
),
),
)
fun withSettingsCheckboxWidget(
@StringRes id: Int,
): Matcher<View>? =
allOf(
withId(android.R.id.switch_widget),
withParent(
withSettingsCheckboxFrame(id),
),
)
fun withSettingsCheckboxFrame(
@StringRes id: Int,
): Matcher<View>? =
allOf(
withId(android.R.id.widget_frame),
hasSibling(
allOf(
withClassName(Matchers.equalTo(RelativeLayout::class.java.name)),
withChild(
withText(id),
),
),
),
)
fun openMenu() {
openActionBarOverflowOrOptionsMenu(
ApplicationProvider.getApplicationContext<Context>(),
)
}

View File

@ -0,0 +1,118 @@
package bou.amine.apps.readerforselfossv2.android
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isClickable
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isFocused
import androidx.test.espresso.matcher.ViewMatchers.isRoot
import androidx.test.espresso.matcher.ViewMatchers.isSelected
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import org.hamcrest.CoreMatchers.not
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@LargeTest
class HomeActivityTest {
@get:Rule
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
@Before
fun init() {
loginAndInitHome()
}
@Test
fun testMenu() {
onView(withId(R.id.action_search)).check(matches(isDisplayed())).check(
matches(
isClickable(),
),
)
onView(withId(R.id.action_filter)).check(matches(isDisplayed())).check(
matches(
isClickable(),
),
)
openMenu()
onView(withText(R.string.readAll)).check(matches(isDisplayed()))
onView(withText(R.string.menu_home_sources)).check(matches(isDisplayed()))
onView(withText(R.string.title_activity_settings)).check(matches(isDisplayed()))
onView(withText(R.string.menu_home_refresh)).check(matches(isDisplayed()))
onView(withText(R.string.issue_tracker_link)).check(matches(isDisplayed()))
onView(withText(R.string.action_disconnect)).check(matches(isDisplayed()))
}
@Test
fun testMenuActions() {
onView(withId(R.id.action_search)).perform(click())
onView(
withId(com.google.android.material.R.id.search_src_text),
).check(matches(isFocused()))
onView(isRoot()).perform(ViewActions.pressBack())
onView(withId(R.id.action_filter)).perform(click())
onView(
withText(R.string.filter_item_sources),
).check(matches(isDisplayed()))
onView(
withText(R.string.filter_item_tags),
).check(matches(isDisplayed()))
onView(
withId(R.id.floatingActionButton2),
).check(matches(isDisplayed()))
onView(isRoot()).perform(ViewActions.pressBack())
openMenu()
onView(withText(R.string.readAll)).perform(click())
onView(withText(R.string.markall_dialog_message)).check(matches(isDisplayed()))
onView(isRoot()).perform(ViewActions.pressBack())
openMenu()
onView(withText(R.string.menu_home_sources)).perform(click())
onView(withId(R.id.fab)).check(matches(isDisplayed()))
onView(isRoot()).perform(ViewActions.pressBack())
openMenu()
onView(withText(R.string.title_activity_settings)).perform(click())
onView(withText(R.string.pref_header_general)).check(matches(isDisplayed()))
onView(isRoot()).perform(ViewActions.pressBack())
openMenu()
onView(withText(R.string.menu_home_refresh)).perform(click())
onView(withText(R.string.refresh_dialog_message)).check(matches(isDisplayed()))
onView(isRoot()).perform(ViewActions.pressBack())
openMenu()
/*onView(withText(R.string.issue_tracker_link)).perform(click())
onView(withText(R.string.markall_dialog_message)).check(matches(isDisplayed()))
onView(isRoot()).perform(ViewActions.pressBack())
openMenu()*/
onView(withText(R.string.action_disconnect)).perform(click())
onView(withText(R.string.confirm_disconnect_title)).check(matches(isDisplayed()))
}
@Test
fun testEmptyView() {
onView(withId(R.id.emptyText)).check(matches(isDisplayed()))
onView(
hasBottombarItemText(R.string.tab_new),
).check(matches(isDisplayed())).check(matches(isSelected()))
onView(
hasBottombarItemText(R.string.tab_read),
).check(matches(isDisplayed())).check(matches(not(isSelected())))
onView(
hasBottombarItemText(R.string.tab_favs),
).check(matches(isDisplayed())).check(matches(not(isSelected())))
}
}

View File

@ -0,0 +1,79 @@
package bou.amine.apps.readerforselfossv2.android
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.IdlingRegistry
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isClickable
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isNotChecked
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import bou.amine.apps.readerforselfossv2.android.testing.CountingIdlingResourceSingleton
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@LargeTest
class LoginActivityTest {
@get:Rule
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
@Before
fun registerIdlingResource() {
IdlingRegistry
.getInstance()
.register(CountingIdlingResourceSingleton.countingIdlingResource)
}
@After
fun unregisterIdlingResource() {
IdlingRegistry
.getInstance()
.unregister(CountingIdlingResourceSingleton.countingIdlingResource)
}
@Test
fun viewIsInitialized() {
onView(withId(R.id.urlView)).check(matches(isDisplayed()))
onView(withId(R.id.selfSigned))
.check(matches(isDisplayed()))
.check(matches(isNotChecked()))
.check(
matches(isClickable()),
)
onView(withId(R.id.withLogin))
.check(matches(isDisplayed()))
.check(matches(isNotChecked()))
.check(
matches(isClickable()),
)
}
@Test
fun urlError() {
performLogin("10.0.2.2:8888")
onView(withId(R.id.urlView)).perform(click())
onView(withId(R.id.urlView)).check(matches(withError(R.string.wrong_infos)))
}
@Test
fun multiError() {
onView(withId(R.id.signInButton)).perform(click())
onView(withId(R.id.signInButton)).perform(click())
onView(withId(R.id.signInButton)).perform(click())
onView(withText(R.string.warning_wrong_url)).check(matches(isDisplayed()))
}
@Test
fun connect() {
performLogin()
onView(withText(R.string.gdpr_dialog_title)).check(matches(isDisplayed()))
}
}

View File

@ -0,0 +1,155 @@
package bou.amine.apps.readerforselfossv2.android
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.replaceText
import androidx.test.espresso.action.ViewActions.swipeUp
import androidx.test.espresso.action.ViewActions.typeTextIntoFocusedView
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isChecked
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isEnabled
import androidx.test.espresso.matcher.ViewMatchers.isFocused
import androidx.test.espresso.matcher.ViewMatchers.isRoot
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.not
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@LargeTest
class SettingsActivityGeneralTest {
@get:Rule
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
@Before
fun init() {
loginAndInitHome()
openActionBarOverflowOrOptionsMenu(
ApplicationProvider.getApplicationContext(),
)
onView(withText(R.string.title_activity_settings)).perform(click())
onView(withText(R.string.pref_header_general)).perform(click())
}
@Suppress("detekt:LongMethod")
@Test
fun testGeneral() {
onView(withText(R.string.pref_api_items_number_title)).check(matches(isDisplayed()))
onView(
withSettingsCheckboxWidget(R.string.pref_general_infinite_loading_title),
).check(
matches(
allOf(
isDisplayed(),
not(isChecked()),
),
),
)
onView(withText(R.string.pref_general_category_links)).check(matches(isDisplayed()))
onView(withSettingsCheckboxWidget(R.string.pref_article_viewer_title)).check(
matches(
allOf(
isDisplayed(),
isChecked(),
),
),
)
onView(withText(R.string.pref_general_category_displaying)).check(matches(isDisplayed()))
onView(withSettingsCheckboxWidget(R.string.pref_switch_card_view_title)).check(
matches(
allOf(
isDisplayed(),
not(isChecked()),
),
),
)
onView(withSettingsCheckboxWidget(R.string.card_height_title)).check(
matches(
allOf(
isDisplayed(),
not(isChecked()),
),
),
)
onView(withSettingsCheckboxFrame(R.string.card_height_title)).check(
matches(
not(isEnabled()),
),
)
onView(withSettingsCheckboxWidget(R.string.switch_unread_count_title)).check(
matches(
allOf(
isDisplayed(),
isChecked(),
),
),
)
onView(withId(R.id.settings)).perform(swipeUp())
onView(withSettingsCheckboxWidget(R.string.display_all_counts_title)).check(
matches(
allOf(
isDisplayed(),
not(isChecked()),
),
),
)
}
@Suppress("detekt:ForbiddenComment")
@Test
fun testGeneralActionsNumberItems() {
onView(withText(R.string.pref_api_items_number_title)).perform(click())
onView(withId(android.R.id.edit)).check(matches(isFocused()))
// Value check
onView(
withId(android.R.id.edit),
).perform(replaceText("AVC"))
.check(matches(withText("")))
// TODO: should check message error. Not working for api level 30+
onView(
withId(android.R.id.edit),
).perform(replaceText("-1"))
.check(matches(withText("")))
// TODO: should check message error. Not working for api level 30+
onView(
withId(android.R.id.edit),
).perform(replaceText("300"))
.check(matches(withText("")))
onView(
withId(android.R.id.edit),
).perform(typeTextIntoFocusedView("300"))
.check(matches(withText("30")))
onView(
withId(android.R.id.edit),
).perform(replaceText("10"))
.check(matches(withText("10")))
onView(isRoot()).perform(ViewActions.pressBack())
// Value saving
changeAndCancelSetting("20", "10") {
onView(withText(R.string.pref_api_items_number_title)).perform(click())
}
changeAndSaveSetting("20", "10") {
onView(withText(R.string.pref_api_items_number_title)).perform(click())
}
}
@Test
fun testGeneralActionsCheckboxes() {
onView(withSettingsCheckboxFrame(R.string.card_height_title)).check(matches(not(isEnabled())))
onView(withSettingsCheckboxWidget(R.string.pref_switch_card_view_title)).perform(click())
onView(withSettingsCheckboxFrame(R.string.card_height_title)).check(matches(isEnabled()))
}
}

View File

@ -0,0 +1,174 @@
package bou.amine.apps.readerforselfossv2.android
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isChecked
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isEnabled
import androidx.test.espresso.matcher.ViewMatchers.isNotEnabled
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.not
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@LargeTest
class SettingsActivityOfflineTest {
@get:Rule
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
lateinit var context: Context
@Before
fun init() {
activityRule.scenario.onActivity { activity ->
context = activity.window.context
}
loginAndInitHome()
openActionBarOverflowOrOptionsMenu(
ApplicationProvider.getApplicationContext(),
)
onView(withText(R.string.title_activity_settings)).perform(click())
onView(withText(R.string.pref_header_offline)).perform(click())
}
@Suppress("detekt:LongMethod")
@Test
fun testOffline() {
onView(withSettingsCheckboxWidget(R.string.pref_switch_periodic_refresh)).check(
matches(
allOf(
isDisplayed(),
not(isChecked()),
),
),
)
onView(withSettingsCheckboxWidget(R.string.pref_switch_items_caching)).check(
matches(
allOf(
isDisplayed(),
not(isChecked()),
),
),
)
onView(withSettingsCheckboxFrame(R.string.pref_switch_items_caching)).check(
matches(
isEnabled(),
),
)
onView(withText(R.string.pref_periodic_refresh_minutes_title)).check(
matches(
allOf(isNotEnabled(), isDisplayed()),
),
)
onView(withSettingsCheckboxWidget(R.string.pref_switch_refresh_when_charging)).check(
matches(
allOf(
isDisplayed(),
not(isChecked()),
),
),
)
onView(withSettingsCheckboxFrame(R.string.pref_switch_refresh_when_charging)).check(
matches(
isNotEnabled(),
),
)
onView(withSettingsCheckboxWidget(R.string.pref_switch_notify_new_items)).check(
matches(
allOf(
isDisplayed(),
not(isChecked()),
),
),
)
onView(withSettingsCheckboxFrame(R.string.pref_switch_notify_new_items)).check(
matches(
isNotEnabled(),
),
)
onView(withSettingsCheckboxWidget(R.string.pref_switch_update_sources)).check(
matches(
allOf(
isDisplayed(),
isChecked(),
),
),
)
}
@Suppress("detekt:LongMethod")
@Test
fun testOfflineActions() {
onView(withText(R.string.pref_switch_items_caching_off)).check(matches(isDisplayed()))
onView(withText(R.string.pref_switch_items_caching)).perform(click())
onView(withText(R.string.pref_switch_items_caching_on)).check(matches(isDisplayed()))
onView(withSettingsCheckboxFrame(R.string.pref_switch_items_caching)).check(
matches(
isEnabled(),
),
)
onView(withText(R.string.pref_periodic_refresh_minutes_title)).check(
matches(
isNotEnabled(),
),
)
onView(withSettingsCheckboxFrame(R.string.pref_switch_refresh_when_charging)).check(
matches(
isNotEnabled(),
),
)
onView(withSettingsCheckboxFrame(R.string.pref_switch_notify_new_items)).check(
matches(
isNotEnabled(),
),
)
onView(withText(R.string.pref_switch_periodic_refresh_off)).check(
matches(
isDisplayed(),
),
)
onView(withSettingsCheckboxWidget(R.string.pref_switch_periodic_refresh)).perform(click())
onView(withText(R.string.pref_switch_periodic_refresh_on)).check(
matches(
isDisplayed(),
),
)
onView(withSettingsCheckboxFrame(R.string.pref_periodic_refresh_minutes_title)).check(
matches(
isEnabled(),
),
)
onView(withSettingsCheckboxFrame(R.string.pref_switch_refresh_when_charging)).check(
matches(
isEnabled(),
),
)
onView(withSettingsCheckboxFrame(R.string.pref_switch_notify_new_items)).check(
matches(
isEnabled(),
),
)
changeAndCancelSetting("360", "123") {
onView(withText(R.string.pref_periodic_refresh_minutes_title)).perform(click())
}
changeAndSaveSetting("360", "123") {
onView(withText(R.string.pref_periodic_refresh_minutes_title)).perform(click())
}
onView(withSettingsCheckboxFrame(R.string.pref_switch_refresh_when_charging)).perform(click())
onView(withSettingsCheckboxFrame(R.string.pref_switch_notify_new_items)).perform(click())
onView(withSettingsCheckboxWidget(R.string.pref_switch_update_sources)).perform(click())
}
}

View File

@ -0,0 +1,85 @@
package bou.amine.apps.readerforselfossv2.android
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isChecked
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.not
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@LargeTest
class SettingsActivityReaderTest {
@get:Rule
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
lateinit var context: Context
@Before
fun init() {
activityRule.scenario.onActivity { activity ->
context = activity.window.context
}
loginAndInitHome()
openActionBarOverflowOrOptionsMenu(
ApplicationProvider.getApplicationContext(),
)
onView(withText(R.string.title_activity_settings)).perform(click())
onView(withText(R.string.pref_header_viewer)).perform(click())
}
@Test
fun testReader() {
onView(withSettingsCheckboxFrame(R.string.pref_switch_actions_pager_scroll)).check(
matches(
allOf(
isDisplayed(),
not(
isChecked(),
),
),
),
)
onView(withText(R.string.pref_content_reader_font_size)).check(matches(isDisplayed()))
onView(withText(R.string.settings_reader_font)).check(matches(isDisplayed()))
}
@Test
fun testReaderActions() {
onView(withText(R.string.pref_switch_actions_pager_scroll_off)).check(
matches(
isDisplayed(),
),
)
onView(withSettingsCheckboxFrame(R.string.pref_switch_actions_pager_scroll)).perform(click())
onView(withText(R.string.pref_switch_actions_pager_scroll_on)).check(
matches(
isDisplayed(),
),
)
onView(withText(R.string.pref_content_reader_font_size)).perform(click())
changeAndCancelSetting("16", "10") {
onView(withText(R.string.pref_content_reader_font_size)).perform(click())
}
changeAndSaveSetting("16", "10") {
onView(withText(R.string.pref_content_reader_font_size)).perform(click())
}
testPreferencesFromArray(context, R.array.preloaded_fonts_values) {
onView(withText(R.string.settings_reader_font)).perform(click())
}
}
}

View File

@ -0,0 +1,92 @@
package bou.amine.apps.readerforselfossv2.android
import android.content.Context
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isSelected
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import org.hamcrest.CoreMatchers.allOf
import org.hamcrest.CoreMatchers.not
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
@LargeTest
class SettingsActivityTest {
@get:Rule
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
lateinit var context: Context
@Before
fun init() {
activityRule.scenario.onActivity { activity ->
context = activity.window.context
}
loginAndInitHome()
openMenu()
onView(withText(R.string.title_activity_settings)).perform(click())
}
@Test
fun testAllSettings() {
onView(withText(R.string.pref_header_general)).check(matches(isDisplayed()))
onView(withText(R.string.pref_header_viewer)).check(matches(isDisplayed()))
onView(withText(R.string.pref_header_offline)).check(matches(isDisplayed()))
onView(withText(R.string.pref_header_theme)).check(matches(isDisplayed()))
onView(withText(R.string.pref_header_links)).check(matches(isDisplayed()))
onView(withText(R.string.pref_switch_disable_acra)).check(
matches(
allOf(
isDisplayed(),
not(isSelected()),
),
),
)
onView(withText(R.string.action_about)).check(matches(isDisplayed()))
}
@Test
fun testThemes() {
testPreferencesFromArray(context, R.array.ModeTitles) {
onView(withText(R.string.pref_header_theme)).perform(click())
}
}
@Test
fun testExperimentail() {
onView(withText(R.string.pref_header_experimental)).perform(click())
changeAndCancelSetting("", "10") {
onView(withText(R.string.pref_api_timeout)).perform(click())
}
changeAndSaveSetting("", "10") {
onView(withText(R.string.pref_api_timeout)).perform(click())
}
}
@Test
fun testBugReports() {
onView(withText(R.string.pref_switch_disable_acra)).perform(click())
}
@Test
fun testLinks() {
onView(withText(R.string.pref_header_links)).perform(click())
onView(withText(R.string.issue_tracker_link)).check(matches(isDisplayed()))
onView(withText(R.string.issue_tracker_summary)).check(matches(isDisplayed()))
onView(withText(R.string.source_code)).check(matches(isDisplayed()))
onView(withText(R.string.translation)).check(matches(isDisplayed()))
}
@Test
fun testAbout() {
onView(withText(R.string.action_about)).perform(click())
onView(withText("ACRA")).check(matches(isDisplayed()))
}
}

View File

@ -0,0 +1,82 @@
package bou.amine.apps.readerforselfossv2.android
import androidx.test.espresso.AmbiguousViewMatcherException
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.action.ViewActions.click
import androidx.test.espresso.action.ViewActions.swipeDown
import androidx.test.espresso.assertion.ViewAssertions.doesNotExist
import androidx.test.espresso.assertion.ViewAssertions.matches
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.isRoot
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import java.util.UUID
@RunWith(AndroidJUnit4::class)
@LargeTest
class SourcesActivityTest {
@get:Rule
val activityRule = ActivityScenarioRule(LoginActivity::class.java)
lateinit var sourceName: String
@Before
fun init() {
sourceName = UUID.randomUUID().toString().substring(0, 15)
loginAndInitHome()
goToSources()
}
@Test
fun addSource() {
testAddSourceWithUrl(
"https://lorem-rss.herokuapp.com/feed?unit=year&interval=1&length=10",
sourceName,
)
}
@Suppress("detekt:SwallowedException")
@Test
fun addSourceCheckContent() {
testAddSourceWithUrl("https://news.google.com/rss?hl=en-US&gl=US&ceid=US:en", sourceName)
onView(isRoot()).perform(ViewActions.pressBack())
openMenu()
onView(withText(R.string.menu_home_refresh)).perform(click())
onView(withText(R.string.refresh_dialog_message)).check(matches(isDisplayed()))
onView(
withId(android.R.id.button1),
).perform(click())
Thread.sleep(10000)
onView(withId(R.id.swipeRefreshLayout)).perform(swipeDown())
Thread.sleep(2000)
try {
onView(withId(R.id.sourceTitleAndDate)).check(matches(isDisplayed()))
} catch (e: AmbiguousViewMatcherException) {
assert(true)
}
goToSources()
}
@After
fun deleteTheCreatedSource() {
onView(withText(sourceName)).check(matches(isDisplayed()))
onView(withId(R.id.deleteBtn)).perform(click())
onView(withText(sourceName)).check(doesNotExist())
}
private fun goToSources() {
openMenu()
onView(withText(R.string.menu_home_sources))
.perform(click())
}
}

View File

@ -2,6 +2,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
@ -52,7 +53,7 @@
android:value=".HomeActivity" /> android:value=".HomeActivity" />
</activity> </activity>
<activity <activity
android:name=".AddSourceActivity" android:name=".UpsertSourceActivity"
android:parentActivityName=".SourcesActivity" android:parentActivityName=".SourcesActivity"
android:exported="true"> android:exported="true">
<meta-data <meta-data
@ -69,7 +70,8 @@
android:name=".ReaderActivity"> android:name=".ReaderActivity">
</activity> </activity>
<activity <activity
android:name=".ImageActivity"> android:name=".ImageActivity"
android:theme="@style/Theme.AppCompat.ImageActivity">
</activity> </activity>
<meta-data android:name="android.webkit.WebView.MetricsOptOut" <meta-data android:name="android.webkit.WebView.MetricsOptOut"

View File

@ -1,172 +0,0 @@
package bou.amine.apps.readerforselfossv2.android
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.*
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityAddSourceBinding
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlInvalid
import bou.amine.apps.readerforselfossv2.model.NetworkUnavailableException
import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.kodein.di.DIAware
import org.kodein.di.android.closestDI
import org.kodein.di.instance
class AddSourceActivity : AppCompatActivity(), DIAware {
private var mSpoutsValue: String? = null
private lateinit var binding: ActivityAddSourceBinding
override val di by closestDI()
private val repository : Repository by instance()
private val appSettingsService : AppSettingsService by instance()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityAddSourceBinding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true)
maybeGetDetailsFromIntentSharing(intent, binding.sourceUri, binding.nameInput)
binding.saveBtn.setOnClickListener {
handleSaveSource(
binding.tags,
binding.nameInput.text.toString(),
binding.sourceUri.text.toString()
)
}
}
override fun onResume() {
super.onResume()
val baseUrl = appSettingsService.getBaseUrl()
if (baseUrl.isEmpty() || baseUrl.isBaseUrlInvalid()) {
mustLoginToAddSource()
} else {
handleSpoutsSpinner(binding.spoutsSpinner, binding.progress, binding.formContainer)
}
}
private fun handleSpoutsSpinner(
spoutsSpinner: Spinner,
mProgress: ProgressBar,
formContainer: ConstraintLayout
) {
val spoutsKV = HashMap<String, String>()
spoutsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(adapterView: AdapterView<*>, view: View?, i: Int, l: Long) {
if (view != null) {
val spoutName = (view as TextView).text.toString()
mSpoutsValue = spoutsKV[spoutName]
}
}
override fun onNothingSelected(adapterView: AdapterView<*>) {
mSpoutsValue = null
}
}
fun handleSpoutFailure(networkIssue: Boolean = false) {
Toast.makeText(
this@AddSourceActivity,
if (networkIssue) R.string.cant_get_spouts_no_network else R.string.cant_get_spouts,
Toast.LENGTH_SHORT
).show()
mProgress.visibility = View.GONE
}
CoroutineScope(Dispatchers.Main).launch {
try {
val items = repository.getSpouts()
if (items.isNotEmpty()) {
val itemsStrings = items.map { it.value.name }
for ((key, value) in items) {
spoutsKV[value.name] = key
}
mProgress.visibility = View.GONE
formContainer.visibility = View.VISIBLE
val spinnerArrayAdapter =
ArrayAdapter(
this@AddSourceActivity,
android.R.layout.simple_spinner_item,
itemsStrings
)
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spoutsSpinner.adapter = spinnerArrayAdapter
} else {
handleSpoutFailure()
}
} catch (e: NetworkUnavailableException) {
handleSpoutFailure(networkIssue = true)
}
}
}
private fun maybeGetDetailsFromIntentSharing(
intent: Intent,
sourceUri: EditText,
nameInput: EditText
) {
if (Intent.ACTION_SEND == intent.action && "text/plain" == intent.type) {
sourceUri.setText(intent.getStringExtra(Intent.EXTRA_TEXT))
nameInput.setText(intent.getStringExtra(Intent.EXTRA_TITLE))
}
}
private fun mustLoginToAddSource() {
Toast.makeText(this, getString(R.string.addStringNoUrl), Toast.LENGTH_SHORT).show()
val i = Intent(this, LoginActivity::class.java)
startActivity(i)
finish()
}
private fun handleSaveSource(tags: EditText, title: String, url: String) {
val sourceDetailsUnavailable =
title.isEmpty() || url.isEmpty() || mSpoutsValue == null || mSpoutsValue!!.isEmpty()
when {
sourceDetailsUnavailable -> {
Toast.makeText(this, R.string.form_not_complete, Toast.LENGTH_SHORT).show()
}
else -> {
CoroutineScope(Dispatchers.Main).launch {
val successfullyAddedSource = repository.createSource(
title,
url,
mSpoutsValue!!,
tags.text.toString(),
"",
)
if (successfullyAddedSource) {
finish()
} else {
Toast.makeText(
this@AddSourceActivity,
R.string.cant_create_source,
Toast.LENGTH_SHORT
).show()
}
}
}
}
}
}

View File

@ -3,6 +3,7 @@ package bou.amine.apps.readerforselfossv2.android
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.motion.widget.MotionLayout
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.adapter.FragmentStateAdapter
@ -10,8 +11,8 @@ import bou.amine.apps.readerforselfossv2.android.databinding.ActivityImageBindin
import bou.amine.apps.readerforselfossv2.android.fragments.ImageFragment import bou.amine.apps.readerforselfossv2.android.fragments.ImageFragment
class ImageActivity : AppCompatActivity() { class ImageActivity : AppCompatActivity() {
private lateinit var allImages : ArrayList<String> private lateinit var allImages: ArrayList<String>
private var position : Int = 0 private var position: Int = 0
private lateinit var binding: ActivityImageBinding private lateinit var binding: ActivityImageBinding
@ -23,7 +24,6 @@ class ImageActivity : AppCompatActivity() {
setContentView(view) setContentView(view)
setSupportActionBar(binding.toolBar) setSupportActionBar(binding.toolBar)
supportActionBar?.setDisplayShowTitleEnabled(false)
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
allImages = intent.getStringArrayListExtra("allImages") as ArrayList<String> allImages = intent.getStringArrayListExtra("allImages") as ArrayList<String>
@ -31,12 +31,52 @@ class ImageActivity : AppCompatActivity() {
binding.pager.adapter = ScreenSlidePagerAdapter(this) binding.pager.adapter = ScreenSlidePagerAdapter(this)
binding.pager.setCurrentItem(position, false) binding.pager.setCurrentItem(position, false)
val transitionListener =
object : MotionLayout.TransitionListener {
override fun onTransitionStarted(
motionLayout: MotionLayout?,
startId: Int,
endId: Int,
) {
// Nothing
}
override fun onTransitionChange(
motionLayout: MotionLayout?,
startId: Int,
endId: Int,
progress: Float,
) {
// Nothing
}
override fun onTransitionCompleted(
motionLayout: MotionLayout?,
currentId: Int,
) {
if (motionLayout?.currentState == binding.root.endState) {
onBackPressedDispatcher.onBackPressed()
overridePendingTransition(0, 0)
}
}
override fun onTransitionTrigger(
motionLayout: MotionLayout?,
triggerId: Int,
positive: Boolean,
progress: Float,
) {
// Nothing
}
}
binding.root.setTransitionListener(transitionListener)
} }
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) { when (item.itemId) {
android.R.id.home -> { android.R.id.home -> {
onBackPressed() onBackPressedDispatcher.onBackPressed()
return true return true
} }
} }
@ -44,8 +84,9 @@ class ImageActivity : AppCompatActivity() {
return super.onOptionsItemSelected(item) return super.onOptionsItemSelected(item)
} }
private inner class ScreenSlidePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) { private inner class ScreenSlidePagerAdapter(
fa: FragmentActivity,
) : FragmentStateAdapter(fa) {
override fun getItemCount(): Int = allImages.size override fun getItemCount(): Int = allImages.size
override fun createFragment(position: Int): Fragment = ImageFragment.newInstance(allImages[position]) override fun createFragment(position: Int): Fragment = ImageFragment.newInstance(allImages[position])

View File

@ -4,6 +4,7 @@ import android.animation.Animator
import android.animation.AnimatorListenerAdapter import android.animation.AnimatorListenerAdapter
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.Intent import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.text.TextUtils import android.text.TextUtils
import android.view.Menu import android.view.Menu
@ -11,10 +12,12 @@ import android.view.MenuItem
import android.view.View import android.view.View
import android.view.inputmethod.EditorInfo import android.view.inputmethod.EditorInfo
import android.widget.TextView import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.app.AppCompatDelegate import androidx.appcompat.app.AppCompatDelegate
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityLoginBinding import bou.amine.apps.readerforselfossv2.android.databinding.ActivityLoginBinding
import bou.amine.apps.readerforselfossv2.android.testing.CountingIdlingResourceSingleton
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlInvalid import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlInvalid
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.service.AppSettingsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
@ -22,36 +25,28 @@ import com.mikepenz.aboutlibraries.LibsBuilder
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.acra.ACRA
import org.kodein.di.DIAware import org.kodein.di.DIAware
import org.kodein.di.android.closestDI import org.kodein.di.android.closestDI
import org.kodein.di.instance import org.kodein.di.instance
import org.matomo.sdk.Tracker
import org.matomo.sdk.extra.DimensionQueue
import org.matomo.sdk.extra.DownloadTracker
import org.matomo.sdk.extra.TrackHelper
import java.security.MessageDigest
private const val MAX_INVALID_LOGIN_BEFORE_ALERT_DISPLAYED = 3
class LoginActivity : AppCompatActivity(), DIAware { class LoginActivity :
AppCompatActivity(),
DIAware {
private var inValidCount: Int = 0 private var inValidCount: Int = 0
private var isWithLogin = false private var isWithLogin = false
private lateinit var binding: ActivityLoginBinding private lateinit var binding: ActivityLoginBinding
override val di by closestDI() override val di by closestDI()
private val repository : Repository by instance() private val repository: Repository by instance()
private val appSettingsService : AppSettingsService by instance() private val appSettingsService: AppSettingsService by instance()
private val tracker : Tracker by instance()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
TrackHelper.track().download().identifier(DownloadTracker.Extra.ApkChecksum(applicationContext))
.with(tracker)
TrackHelper.track().screen("/login").with(tracker)
handleTheme() handleTheme()
binding = ActivityLoginBinding.inflate(layoutInflater) binding = ActivityLoginBinding.inflate(layoutInflater)
@ -64,6 +59,7 @@ class LoginActivity : AppCompatActivity(), DIAware {
handleBaseUrlFail() handleBaseUrlFail()
if (appSettingsService.getBaseUrl().isNotEmpty()) { if (appSettingsService.getBaseUrl().isNotEmpty()) {
showProgress(true)
goToMain() goToMain()
} }
@ -76,7 +72,6 @@ class LoginActivity : AppCompatActivity(), DIAware {
} }
private fun handleActions() { private fun handleActions() {
binding.passwordView.setOnEditorActionListener( binding.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) {
@ -84,7 +79,7 @@ class LoginActivity : AppCompatActivity(), DIAware {
return@OnEditorActionListener true return@OnEditorActionListener true
} }
false false
} },
) )
binding.signInButton.setOnClickListener { attemptLogin() } binding.signInButton.setOnClickListener { attemptLogin() }
@ -105,24 +100,21 @@ class LoginActivity : AppCompatActivity(), DIAware {
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()
} }
} }
private fun goToMain() { private fun goToMain() {
CountingIdlingResourceSingleton.increment()
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
repository.updateApiVersion() repository.updateApiInformation()
ACRA.errorReporter.putCustomData(
val messageDigest: MessageDigest = MessageDigest.getInstance("SHA-256") "SELFOSS_API_VERSION",
messageDigest.update(appSettingsService.getBaseUrl().toByteArray()) appSettingsService.getApiVersion().toString(),
tracker.userId = String(messageDigest.digest()) )
CountingIdlingResourceSingleton.decrement()
val mDimensionQueue = DimensionQueue(tracker)
mDimensionQueue.add(1, appSettingsService.getApiVersion().toString())
tracker.isOptOut = !appSettingsService.isAnalyticsEnabled()
} }
val intent = Intent(this, HomeActivity::class.java) val intent = Intent(this, HomeActivity::class.java)
startActivity(intent) startActivity(intent)
@ -138,70 +130,116 @@ class LoginActivity : AppCompatActivity(), DIAware {
} }
private fun attemptLogin() { private fun attemptLogin() {
// Reset errors. // Reset errors.
binding.urlView.error = null binding.urlView.error = null
binding.loginView.error = null binding.loginView.error = null
binding.passwordView.error = null binding.passwordView.error = null
// Store values at the time of the login attempt. // Store values at the time of the login attempt.
val url = binding.urlView.text.toString().trim() val url =
val login = binding.loginView.text.toString().trim() binding.urlView.text
val password = binding.passwordView.text.toString().trim() .toString()
.trim()
val login =
binding.loginView.text
.toString()
.trim()
val password =
binding.passwordView.text
.toString()
.trim()
failInvalidUrl(url)
failLoginDetails(password, login)
showProgress(true)
appSettingsService.updateSelfSigned(binding.selfSigned.isChecked)
repository.refreshLoginInformation(url, login, password)
CountingIdlingResourceSingleton.increment()
CoroutineScope(Dispatchers.Main).launch {
try {
repository.updateApiInformation()
} catch (e: Exception) {
if (e.message?.startsWith("No transformation found") == true) {
Toast
.makeText(
applicationContext,
R.string.application_selfoss_only,
Toast.LENGTH_LONG,
).show()
preferenceError()
showProgress(false)
}
}
val result = repository.login()
if (result) {
val errorFetching = repository.checkIfFetchFails()
if (!errorFetching) {
goToMain()
} else {
preferenceError()
}
} else {
preferenceError()
}
showProgress(false)
CountingIdlingResourceSingleton.decrement()
}
}
private fun failLoginDetails(
password: String,
login: String,
) {
var lastFocusedView: View? = null
var cancel = false var cancel = false
var focusView: View? = null
if (url.isBaseUrlInvalid()) {
binding.urlView.error = getString(R.string.login_url_problem)
focusView = binding.urlView
cancel = true
inValidCount++
if (inValidCount == 3) {
val alertDialog = AlertDialog.Builder(this).create()
alertDialog.setTitle(getString(R.string.warning_wrong_url))
alertDialog.setMessage(getString(R.string.text_wrong_url))
alertDialog.setButton(
AlertDialog.BUTTON_NEUTRAL,
"OK"
) { dialog, _ -> dialog.dismiss() }
alertDialog.show()
inValidCount = 0
}
}
if (isWithLogin) { if (isWithLogin) {
if (TextUtils.isEmpty(password)) { if (TextUtils.isEmpty(password)) {
binding.passwordView.error = getString(R.string.error_invalid_password) binding.passwordView.error = getString(R.string.error_invalid_password)
focusView = binding.passwordView lastFocusedView = binding.passwordView
cancel = true cancel = true
} }
if (TextUtils.isEmpty(login)) { if (TextUtils.isEmpty(login)) {
binding.loginView.error = getString(R.string.error_field_required) binding.loginView.error = getString(R.string.error_field_required)
focusView = binding.loginView lastFocusedView = binding.loginView
cancel = true cancel = true
} }
} }
maybeCancelAndFocusView(cancel, lastFocusedView)
}
private fun failInvalidUrl(url: String) {
val focusView = binding.urlView
var cancel = false
if (url.isBaseUrlInvalid()) {
cancel = true
binding.urlView.error = getString(R.string.login_url_problem)
inValidCount++
if (inValidCount == MAX_INVALID_LOGIN_BEFORE_ALERT_DISPLAYED) {
val alertDialog = AlertDialog.Builder(this).create()
alertDialog.setTitle(getString(R.string.warning_wrong_url))
alertDialog.setMessage(getString(R.string.text_wrong_url))
alertDialog.setButton(
AlertDialog.BUTTON_NEUTRAL,
"OK",
) { dialog, _ -> dialog.dismiss() }
alertDialog.show()
inValidCount = 0
}
}
maybeCancelAndFocusView(cancel, focusView)
}
private fun maybeCancelAndFocusView(
cancel: Boolean,
focusView: View?,
) {
if (cancel) { if (cancel) {
focusView?.requestFocus() focusView?.requestFocus()
} else {
showProgress(true)
repository.refreshLoginInformation(url, login, password)
CoroutineScope(Dispatchers.IO).launch {
val result = repository.login()
if (result) {
goToMain()
} else {
CoroutineScope(Dispatchers.Main).launch {
preferenceError()
}
}
}
showProgress(false)
} }
} }
@ -213,12 +251,13 @@ class LoginActivity : AppCompatActivity(), DIAware {
.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) {
binding.loginForm.visibility = if (show) View.GONE else View.VISIBLE binding.loginForm.visibility = if (show) View.GONE else View.VISIBLE
} }
} },
) )
binding.loginProgress.visibility = if (show) View.VISIBLE else View.GONE binding.loginProgress.visibility = if (show) View.VISIBLE else View.GONE
@ -226,12 +265,13 @@ class LoginActivity : AppCompatActivity(), DIAware {
.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) {
binding.loginProgress.visibility = if (show) View.VISIBLE else View.GONE binding.loginProgress.visibility = if (show) View.VISIBLE else View.GONE
} }
} },
) )
} }
@ -242,13 +282,25 @@ class LoginActivity : AppCompatActivity(), DIAware {
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) { return when (item.itemId) {
R.id.issue_tracker -> {
val browserIntent =
Intent(Intent.ACTION_VIEW, Uri.parse(AppSettingsService.BUG_URL))
startActivity(browserIntent)
return true
}
R.id.about -> { R.id.about -> {
LibsBuilder() LibsBuilder()
.withAboutIconShown(true) .withAboutIconShown(true)
.withAboutVersionShown(true) .withAboutVersionShown(true)
.withAboutSpecial2("Bug reports")
.withAboutSpecial2Description(AppSettingsService.BUG_URL)
.withAboutSpecial1("Project Page")
.withAboutSpecial1Description(AppSettingsService.SOURCE_URL)
.start(this) .start(this)
true true
} }
else -> super.onOptionsItemSelected(item) else -> super.onOptionsItemSelected(item)
} }
} }

View File

@ -8,7 +8,6 @@ import bou.amine.apps.readerforselfossv2.android.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater) binding = ActivityMainBinding.inflate(layoutInflater)

View File

@ -3,26 +3,20 @@ package bou.amine.apps.readerforselfossv2.android
import android.app.NotificationChannel import android.app.NotificationChannel
import android.app.NotificationManager import android.app.NotificationManager
import android.content.Context import android.content.Context
import android.graphics.drawable.Drawable
import android.net.Uri
import android.os.Build import android.os.Build
import android.widget.ImageView
import android.widget.Toast import android.widget.Toast
import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ProcessLifecycleOwner import androidx.lifecycle.ProcessLifecycleOwner
import androidx.multidex.MultiDexApplication import androidx.multidex.MultiDexApplication
import bou.amine.apps.readerforselfossv2.DI.networkModule import bou.amine.apps.readerforselfossv2.android.testing.TestingHelper
import bou.amine.apps.readerforselfossv2.android.viewmodel.AppViewModel import bou.amine.apps.readerforselfossv2.android.viewmodel.AppViewModel
import bou.amine.apps.readerforselfossv2.dao.DriverFactory import bou.amine.apps.readerforselfossv2.dao.DriverFactory
import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB
import bou.amine.apps.readerforselfossv2.di.networkModule
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.service.AppSettingsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
import com.github.ln_12.library.ConnectivityStatus import com.github.ln_12.library.ConnectivityStatus
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader
import com.mikepenz.materialdrawer.util.DrawerImageLoader
import io.github.aakira.napier.DebugAntilog import io.github.aakira.napier.DebugAntilog
import io.github.aakira.napier.Napier import io.github.aakira.napier.Napier
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -36,22 +30,31 @@ import org.acra.config.toast
import org.acra.data.StringFormat import org.acra.data.StringFormat
import org.acra.ktx.initAcra import org.acra.ktx.initAcra
import org.acra.sender.HttpSender import org.acra.sender.HttpSender
import org.kodein.di.* import org.kodein.di.DI
import org.matomo.sdk.Matomo import org.kodein.di.DIAware
import org.matomo.sdk.Tracker import org.kodein.di.bind
import org.matomo.sdk.TrackerBuilder import org.kodein.di.instance
import org.kodein.di.singleton
class MyApp : MultiDexApplication(), DIAware {
class MyApp :
MultiDexApplication(),
DIAware {
override val di by DI.lazy { override val di by DI.lazy {
bind<AppSettingsService>() with singleton { AppSettingsService(ACRA.isACRASenderServiceProcess() || TestingHelper().isUnitTest()) }
import(networkModule) import(networkModule)
bind<DriverFactory>() with singleton { DriverFactory(applicationContext) } bind<DriverFactory>() with singleton { DriverFactory(applicationContext) }
bind<ReaderForSelfossDB>() with singleton { ReaderForSelfossDB(driverFactory.createDriver()) } bind<ReaderForSelfossDB>() with singleton { ReaderForSelfossDB(driverFactory.createDriver()) }
bind<Repository>() with singleton { Repository(instance(), instance(), isConnectionAvailable, instance()) } bind<Repository>() with
singleton {
Repository(
instance(),
instance(),
isConnectionAvailable,
instance(),
)
}
bind<ConnectivityStatus>() with singleton { ConnectivityStatus(applicationContext) } bind<ConnectivityStatus>() with singleton { ConnectivityStatus(applicationContext) }
bind<AppViewModel>() with singleton { AppViewModel(repository = instance()) } bind<AppViewModel>() with singleton { AppViewModel(repository = instance()) }
bind<Tracker>() with singleton { TrackerBuilder.createDefault("https://matomo.amine-louveau.fr/matomo.php", if (BuildConfig.DEBUG) 4 else 5).build(
Matomo.getInstance(applicationContext)) }
} }
private val repository: Repository by instance() private val repository: Repository by instance()
@ -59,6 +62,7 @@ class MyApp : MultiDexApplication(), DIAware {
private val connectivityStatus: ConnectivityStatus by instance() private val connectivityStatus: ConnectivityStatus by instance()
private val driverFactory: DriverFactory by instance() private val driverFactory: DriverFactory by instance()
@Suppress("detekt:ForbiddenComment")
// TODO: handle with the "previous" way // TODO: handle with the "previous" way
private val isConnectionAvailable: MutableStateFlow<Boolean> = MutableStateFlow(true) private val isConnectionAvailable: MutableStateFlow<Boolean> = MutableStateFlow(true)
@ -67,31 +71,38 @@ class MyApp : MultiDexApplication(), DIAware {
Napier.base(DebugAntilog()) Napier.base(DebugAntilog())
if (!ACRA.isACRASenderServiceProcess()) { if (!ACRA.isACRASenderServiceProcess()) {
initDrawerImageLoader()
tryToHandleBug() tryToHandleBug()
handleNotificationChannels() handleNotificationChannels()
ProcessLifecycleOwner.get().lifecycle.addObserver(AppLifeCycleObserver(connectivityStatus, repository)) ProcessLifecycleOwner.get().lifecycle.addObserver(
AppLifeCycleObserver(
connectivityStatus,
repository,
),
)
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
viewModel.networkAvailableProvider.collect { networkAvailable -> viewModel.networkAvailableProvider.collect { networkAvailable ->
val toastMessage = if (networkAvailable) { val toastMessage =
if (networkAvailable) {
repository.handleDBActions() repository.handleDBActions()
R.string.network_connectivity_retrieved R.string.network_connectivity_retrieved
} else { } else {
R.string.network_connectivity_lost R.string.network_connectivity_lost
} }
Toast.makeText( Toast
.makeText(
applicationContext, applicationContext,
toastMessage, toastMessage,
Toast.LENGTH_SHORT Toast.LENGTH_SHORT,
).show() ).show()
} }
} }
} }
repository.migrate(driverFactory)
} }
override fun attachBaseContext(base: Context?) { override fun attachBaseContext(base: Context?) {
@ -99,23 +110,40 @@ class MyApp : MultiDexApplication(), DIAware {
initAcra { initAcra {
reportFormat = StringFormat.JSON reportFormat = StringFormat.JSON
reportContent = listOf( reportContent =
ReportField.REPORT_ID, ReportField.INSTALLATION_ID, listOf(
ReportField.APP_VERSION_CODE, ReportField.APP_VERSION_NAME, ReportField.REPORT_ID,
ReportField.BUILD, ReportField.ANDROID_VERSION, ReportField.BRAND, ReportField.PHONE_MODEL, ReportField.INSTALLATION_ID,
ReportField.AVAILABLE_MEM_SIZE, ReportField.TOTAL_MEM_SIZE, ReportField.APP_VERSION_CODE,
ReportField.STACK_TRACE, ReportField.APPLICATION_LOG, ReportField.LOGCAT, ReportField.APP_VERSION_NAME,
ReportField.INITIAL_CONFIGURATION, ReportField.CRASH_CONFIGURATION, ReportField.IS_SILENT, ReportField.BUILD,
ReportField.USER_APP_START_DATE, ReportField.USER_COMMENT, ReportField.USER_CRASH_DATE, ReportField.USER_EMAIL, ReportField.CUSTOM_DATA) 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,
)
toast { toast {
//required // required
text = getString(R.string.crash_toast_text) text = getString(R.string.crash_toast_text)
length = Toast.LENGTH_SHORT length = Toast.LENGTH_SHORT
} }
httpSender { httpSender {
uri = "https://bugs.amine-louveau.fr/report" /*best guess, you may need to adjust this*/ uri =
basicAuthLogin = "LMTlLZuazADohTCm" "https://bugs.amine-bouabdallaoui.fr/report" // best guess, you may need to adjust this
basicAuthPassword = "he6ghHp83F0PYPfh" basicAuthLogin = "qMEscjj89Gwt6cPR"
basicAuthPassword = "Yo58QFlGzFaWlBzP"
httpMethod = HttpSender.Method.POST httpMethod = HttpSender.Method.POST
} }
} }
@ -127,51 +155,42 @@ class MyApp : MultiDexApplication(), DIAware {
val name = getString(R.string.notification_channel_sync) val name = getString(R.string.notification_channel_sync)
val importance = NotificationManager.IMPORTANCE_LOW val importance = NotificationManager.IMPORTANCE_LOW
val mChannel = NotificationChannel(AppSettingsService.syncChannelId, name, importance) val mChannel = NotificationChannel(AppSettingsService.SYNC_CHANNEL_ID, name, importance)
val newItemsChannelname = getString(R.string.new_items_channel_sync) val newItemsChannelname = getString(R.string.new_items_channel_sync)
val newItemsChannelimportance = NotificationManager.IMPORTANCE_DEFAULT val newItemsChannelimportance = NotificationManager.IMPORTANCE_DEFAULT
val newItemsChannelmChannel = NotificationChannel(AppSettingsService.newItemsChannelId, newItemsChannelname, newItemsChannelimportance) val newItemsChannelmChannel =
NotificationChannel(
AppSettingsService.NEW_ITEMS_CHANNEL,
newItemsChannelname,
newItemsChannelimportance,
)
notificationManager.createNotificationChannel(mChannel) notificationManager.createNotificationChannel(mChannel)
notificationManager.createNotificationChannel(newItemsChannelmChannel) notificationManager.createNotificationChannel(newItemsChannelmChannel)
} }
} }
private fun initDrawerImageLoader() {
DrawerImageLoader.init(object : AbstractDrawerImageLoader() {
override fun set(imageView: ImageView, uri: Uri, placeholder: Drawable, tag: String?) {
Glide.with(imageView.context)
.load(uri.toString())
.apply(RequestOptions.fitCenterTransform().placeholder(placeholder))
.into(imageView)
}
override fun cancel(imageView: ImageView) {
Glide.with(imageView.context).clear(imageView)
}
override fun placeholder(ctx: Context, tag: String?): Drawable {
return baseContext.resources.getDrawable(R.mipmap.ic_launcher)
}
})
}
private fun tryToHandleBug() { private fun tryToHandleBug() {
val oldHandler = Thread.getDefaultUncaughtExceptionHandler() val oldHandler = Thread.getDefaultUncaughtExceptionHandler()
Thread.setDefaultUncaughtExceptionHandler { thread, e -> Thread.setDefaultUncaughtExceptionHandler { thread, e ->
if (e is NoClassDefFoundError && e.stackTrace.asList().any { if (e is NoClassDefFoundError &&
e.stackTrace.asList().any {
it.toString().contains("android.view.ViewDebug") it.toString().contains("android.view.ViewDebug")
}) { }
) {
// Nothing
} else { } else {
oldHandler.uncaughtException(thread, e) oldHandler.uncaughtException(thread, e)
} }
} }
} }
class AppLifeCycleObserver(val connectivityStatus: ConnectivityStatus, val repository: Repository) : DefaultLifecycleObserver { class AppLifeCycleObserver(
val connectivityStatus: ConnectivityStatus,
val repository: Repository,
) : DefaultLifecycleObserver {
override fun onResume(owner: LifecycleOwner) { override fun onResume(owner: LifecycleOwner) {
super.onResume(owner) super.onResume(owner)
repository.connectionMonitored = true repository.connectionMonitored = true

View File

@ -22,14 +22,17 @@ import org.kodein.di.DIAware
import org.kodein.di.android.closestDI import org.kodein.di.android.closestDI
import org.kodein.di.instance import org.kodein.di.instance
class ReaderActivity : AppCompatActivity(), DIAware { class ReaderActivity :
AppCompatActivity(),
DIAware {
private var currentItem: Int = 0 private var currentItem: Int = 0
private lateinit var toolbarMenu: Menu private lateinit var toolbarMenu: Menu
private lateinit var binding: ActivityReaderBinding private lateinit var binding: ActivityReaderBinding
private var allItems: ArrayList<SelfossModel.Item> = ArrayList()
override val di by closestDI() override val di by closestDI()
private val repository: Repository by instance() private val repository: Repository by instance()
private val appSettingsService: AppSettingsService by instance() private val appSettingsService: AppSettingsService by instance()
@ -50,6 +53,7 @@ class ReaderActivity : AppCompatActivity(), DIAware {
showMenuItem(false) showMenuItem(false)
} }
@Suppress("detekt:SwallowedException")
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = ActivityReaderBinding.inflate(layoutInflater) binding = ActivityReaderBinding.inflate(layoutInflater)
@ -61,13 +65,19 @@ class ReaderActivity : AppCompatActivity(), DIAware {
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true)
if (allItems.isEmpty()) { currentItem = intent.getIntExtra("currentItem", 0)
allItems = repository.getReaderItems()
if (allItems.isEmpty() || currentItem > allItems.size) {
finish() finish()
} }
currentItem = intent.getIntExtra("currentItem", 0) try {
readItem(allItems[currentItem]) readItem(allItems[currentItem])
} catch (e: IndexOutOfBoundsException) {
finish()
}
binding.pager.adapter = ScreenSlidePagerAdapter(this) binding.pager.adapter = ScreenSlidePagerAdapter(this)
binding.pager.setCurrentItem(currentItem, false) binding.pager.setCurrentItem(currentItem, false)
@ -80,7 +90,7 @@ class ReaderActivity : AppCompatActivity(), DIAware {
} }
private fun readItem(item: SelfossModel.Item) { private fun readItem(item: SelfossModel.Item) {
if (appSettingsService.isMarkOnScrollEnabled()) { if (appSettingsService.isMarkOnScrollEnabled() && !appSettingsService.getPublicAccess()) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
repository.markAsRead(item) repository.markAsRead(item)
} }
@ -92,35 +102,37 @@ class ReaderActivity : AppCompatActivity(), DIAware {
oldInstanceState.clear() oldInstanceState.clear()
} }
private inner class ScreenSlidePagerAdapter(fa: FragmentActivity) : private inner class ScreenSlidePagerAdapter(
FragmentStateAdapter(fa) { fa: FragmentActivity,
) : FragmentStateAdapter(fa) {
override fun getItemCount(): Int = allItems.size override fun getItemCount(): Int = allItems.size
override fun createFragment(position: Int): Fragment = override fun createFragment(position: Int): Fragment = ArticleFragment.newInstance(allItems[position])
ArticleFragment.newInstance(allItems[position])
} }
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { override fun onKeyDown(
return when (keyCode) { keyCode: Int,
event: KeyEvent?,
): Boolean =
when (keyCode) {
KeyEvent.KEYCODE_VOLUME_DOWN -> { KeyEvent.KEYCODE_VOLUME_DOWN -> {
val currentFragment = val currentFragment =
supportFragmentManager.findFragmentByTag("f" + binding.pager.currentItem) as ArticleFragment supportFragmentManager.findFragmentByTag("f" + binding.pager.currentItem) as ArticleFragment
currentFragment.scrollDown() currentFragment.volumeButtonScrollDown()
true true
} }
KeyEvent.KEYCODE_VOLUME_UP -> { KeyEvent.KEYCODE_VOLUME_UP -> {
val currentFragment = val currentFragment =
supportFragmentManager.findFragmentByTag("f" + binding.pager.currentItem) as ArticleFragment supportFragmentManager.findFragmentByTag("f" + binding.pager.currentItem) as ArticleFragment
currentFragment.scrollUp() currentFragment.volumeButtonScrollUp()
true true
} }
else -> { else -> {
super.onKeyDown(keyCode, event) super.onKeyDown(keyCode, event)
} }
} }
}
private fun alignmentMenu() { private fun alignmentMenu() {
val showJustify = appSettingsService.getActiveAllignment() == AppSettingsService.ALIGN_LEFT val showJustify = appSettingsService.getActiveAllignment() == AppSettingsService.ALIGN_LEFT
@ -133,19 +145,23 @@ class ReaderActivity : AppCompatActivity(), DIAware {
inflater.inflate(R.menu.reader_menu, menu) inflater.inflate(R.menu.reader_menu, menu)
toolbarMenu = menu toolbarMenu = menu
alignmentMenu()
if (appSettingsService.getPublicAccess()) {
menu.removeItem(R.id.star)
} else {
if (allItems.isNotEmpty() && allItems[currentItem].starred) { if (allItems.isNotEmpty() && allItems[currentItem].starred) {
canRemoveFromFavorite() canRemoveFromFavorite()
} else { } else {
canFavorite() canFavorite()
} }
alignmentMenu()
binding.pager.registerOnPageChangeCallback( binding.pager.registerOnPageChangeCallback(
object : ViewPager2.OnPageChangeCallback() { object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) { override fun onPageSelected(position: Int) {
super.onPageSelected(position) super.onPageSelected(position)
if (!allItems.isNullOrEmpty() && allItems.size >= position) {
if (allItems[position].starred) { if (allItems[position].starred) {
canRemoveFromFavorite() canRemoveFromFavorite()
} else { } else {
@ -154,7 +170,9 @@ class ReaderActivity : AppCompatActivity(), DIAware {
readItem(allItems[position]) readItem(allItems[position])
} }
} }
},
) )
}
return true return true
} }
@ -173,28 +191,29 @@ class ReaderActivity : AppCompatActivity(), DIAware {
when (item.itemId) { when (item.itemId) {
android.R.id.home -> { android.R.id.home -> {
onBackPressed() onBackPressedDispatcher.onBackPressed()
return true return true
} }
R.id.star -> { R.id.star -> {
if (allItems[binding.pager.currentItem].starred) { if (allItems[binding.pager.currentItem].starred) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
repository.unstarr(allItems[binding.pager.currentItem]) repository.unstarr(allItems[binding.pager.currentItem])
// TODO: Handle failure
} }
afterUnsave() afterUnsave()
} else { } else {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
repository.starr(allItems[binding.pager.currentItem]) repository.starr(allItems[binding.pager.currentItem])
// TODO: Handle failure
} }
afterSave() afterSave()
} }
} }
R.id.align_left -> { R.id.align_left -> {
switchAlignmentSetting(AppSettingsService.ALIGN_LEFT) switchAlignmentSetting(AppSettingsService.ALIGN_LEFT)
refreshFragment() refreshFragment()
} }
R.id.align_justify -> { R.id.align_justify -> {
switchAlignmentSetting(AppSettingsService.JUSTIFY) switchAlignmentSetting(AppSettingsService.JUSTIFY)
refreshFragment() refreshFragment()
@ -214,8 +233,4 @@ class ReaderActivity : AppCompatActivity(), DIAware {
startActivity(intent) startActivity(intent)
overridePendingTransition(0, 0) overridePendingTransition(0, 0)
} }
companion object {
var allItems: ArrayList<SelfossModel.Item> = ArrayList()
}
} }

View File

@ -8,6 +8,7 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import bou.amine.apps.readerforselfossv2.android.adapters.SourcesListAdapter import bou.amine.apps.readerforselfossv2.android.adapters.SourcesListAdapter
import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySourcesBinding import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySourcesBinding
import bou.amine.apps.readerforselfossv2.android.testing.CountingIdlingResourceSingleton
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
@ -17,12 +18,13 @@ import org.kodein.di.DIAware
import org.kodein.di.android.closestDI import org.kodein.di.android.closestDI
import org.kodein.di.instance import org.kodein.di.instance
class SourcesActivity : AppCompatActivity(), DIAware { class SourcesActivity :
AppCompatActivity(),
DIAware {
private lateinit var binding: ActivitySourcesBinding private lateinit var binding: ActivitySourcesBinding
override val di by closestDI() override val di by closestDI()
private val repository : Repository by instance() private val repository: Repository by instance()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
binding = ActivitySourcesBinding.inflate(layoutInflater) binding = ActivitySourcesBinding.inflate(layoutInflater)
@ -37,7 +39,8 @@ class SourcesActivity : AppCompatActivity(), DIAware {
supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true)
binding.fab.rippleColor = resources.getColor(R.color.colorAccentDark) binding.fab.rippleColor = resources.getColor(R.color.colorAccentDark)
binding.fab.backgroundTintList = ColorStateList.valueOf(resources.getColor(R.color.colorAccent)) binding.fab.backgroundTintList =
ColorStateList.valueOf(resources.getColor(R.color.colorAccent))
} }
override fun onStop() { override fun onStop() {
@ -49,31 +52,36 @@ class SourcesActivity : AppCompatActivity(), DIAware {
super.onResume() super.onResume()
val mLayoutManager = LinearLayoutManager(this) val mLayoutManager = LinearLayoutManager(this)
var items: ArrayList<SelfossModel.Source> var items: ArrayList<SelfossModel.SourceDetail>
binding.recyclerView.setHasFixedSize(true) binding.recyclerView.setHasFixedSize(true)
binding.recyclerView.layoutManager = mLayoutManager binding.recyclerView.layoutManager = mLayoutManager
CountingIdlingResourceSingleton.increment()
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
val response = repository.getSources() val response = repository.getSourcesDetails()
if (response.isNotEmpty()) { if (response.isNotEmpty()) {
items = response items = response
val mAdapter = SourcesListAdapter( val mAdapter =
this@SourcesActivity, items SourcesListAdapter(
this@SourcesActivity,
items,
) )
binding.recyclerView.adapter = mAdapter binding.recyclerView.adapter = mAdapter
mAdapter.notifyDataSetChanged() mAdapter.notifyDataSetChanged()
} else { } else {
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()
} }
CountingIdlingResourceSingleton.decrement()
} }
binding.fab.setOnClickListener { binding.fab.setOnClickListener {
startActivity(Intent(this@SourcesActivity, AddSourceActivity::class.java)) startActivity(Intent(this@SourcesActivity, UpsertSourceActivity::class.java))
} }
} }
} }

View File

@ -0,0 +1,216 @@
package bou.amine.apps.readerforselfossv2.android
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.AdapterView
import android.widget.ArrayAdapter
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import bou.amine.apps.readerforselfossv2.android.databinding.ActivityUpsertSourceBinding
import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlInvalid
import bou.amine.apps.readerforselfossv2.model.NetworkUnavailableException
import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.kodein.di.DIAware
import org.kodein.di.android.closestDI
import org.kodein.di.instance
class UpsertSourceActivity :
AppCompatActivity(),
DIAware {
private var existingSource: SelfossModel.SourceDetail? = null
private var mSpoutsValue: String? = null
private lateinit var binding: ActivityUpsertSourceBinding
override val di by closestDI()
private val repository: Repository by instance()
private val appSettingsService: AppSettingsService by instance()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityUpsertSourceBinding.inflate(layoutInflater)
val view = binding.root
existingSource = repository.getSelectedSource()
if (existingSource != null) {
binding.formContainer.visibility = View.GONE
binding.progress.visibility = View.VISIBLE
}
val title = if (existingSource == null) R.string.add_source else R.string.update_source
supportFragmentManager.addOnBackStackChangedListener {
if (supportFragmentManager.backStackEntryCount == 0) {
setTitle(title)
}
}
setContentView(view)
setSupportActionBar(binding.toolbar)
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setDisplayShowHomeEnabled(true)
supportActionBar?.title = resources.getString(title)
maybeGetDetailsFromIntentSharing(intent)
binding.saveBtn.setOnClickListener {
handleSaveSource()
}
}
private fun initFields(items: Map<String, SelfossModel.Spout>) {
binding.nameInput.setText(existingSource!!.title)
binding.tags.setText(existingSource!!.tags?.joinToString(", "))
binding.sourceUri.setText(existingSource!!.params?.url)
binding.spoutsSpinner.setSelection(items.keys.indexOf(existingSource!!.spout))
binding.progress.visibility = View.GONE
binding.formContainer.visibility = View.VISIBLE
}
override fun onResume() {
super.onResume()
val baseUrl = appSettingsService.getBaseUrl()
if (baseUrl.isEmpty() || baseUrl.isBaseUrlInvalid()) {
mustLoginToAddSource()
} else {
handleSpoutsSpinner()
}
}
@Suppress("detekt:SwallowedException")
private fun handleSpoutsSpinner() {
val spoutsKV = HashMap<String, String>()
binding.spoutsSpinner.onItemSelectedListener =
object : AdapterView.OnItemSelectedListener {
override fun onItemSelected(
adapterView: AdapterView<*>,
view: View?,
i: Int,
l: Long,
) {
if (view != null) {
val spoutName = (view as TextView).text.toString()
mSpoutsValue = spoutsKV[spoutName]
}
}
override fun onNothingSelected(adapterView: AdapterView<*>) {
mSpoutsValue = null
}
}
fun handleSpoutFailure(networkIssue: Boolean = false) {
Toast
.makeText(
this@UpsertSourceActivity,
if (networkIssue) R.string.cant_get_spouts_no_network else R.string.cant_get_spouts,
Toast.LENGTH_SHORT,
).show()
binding.progress.visibility = View.GONE
}
CoroutineScope(Dispatchers.Main).launch {
try {
val items = repository.getSpouts()
if (items.isNotEmpty()) {
val itemsStrings = items.map { it.value.name }
for ((key, value) in items) {
spoutsKV[value.name] = key
}
binding.progress.visibility = View.GONE
binding.formContainer.visibility = View.VISIBLE
val spinnerArrayAdapter =
ArrayAdapter(
this@UpsertSourceActivity,
android.R.layout.simple_spinner_item,
itemsStrings,
)
spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
binding.spoutsSpinner.adapter = spinnerArrayAdapter
if (existingSource != null) {
initFields(items)
}
} else {
handleSpoutFailure()
}
} catch (e: NetworkUnavailableException) {
handleSpoutFailure(networkIssue = true)
}
}
}
private fun maybeGetDetailsFromIntentSharing(intent: Intent) {
if (Intent.ACTION_SEND == intent.action && "text/plain" == intent.type) {
binding.sourceUri.setText(intent.getStringExtra(Intent.EXTRA_TEXT))
binding.nameInput.setText(intent.getStringExtra(Intent.EXTRA_TITLE))
}
}
private fun mustLoginToAddSource() {
Toast.makeText(this, getString(R.string.addStringNoUrl), Toast.LENGTH_SHORT).show()
val i = Intent(this, LoginActivity::class.java)
startActivity(i)
finish()
}
private fun handleSaveSource() {
val url = binding.sourceUri.text.toString()
val sourceDetailsUnavailable =
title.isEmpty() || url.isEmpty() || mSpoutsValue == null || mSpoutsValue!!.isEmpty()
when {
sourceDetailsUnavailable -> {
Toast.makeText(this, R.string.form_not_complete, Toast.LENGTH_SHORT).show()
}
else -> {
CoroutineScope(Dispatchers.Main).launch {
val successfullyAddedSource =
if (existingSource != null) {
repository.updateSource(
existingSource!!.id,
binding.nameInput.text.toString(),
url,
mSpoutsValue!!,
binding.tags.text.toString(),
)
} else {
repository.createSource(
binding.nameInput.text.toString(),
url,
mSpoutsValue!!,
binding.tags.text.toString(),
)
}
if (successfullyAddedSource) {
finish()
} else {
Toast
.makeText(
this@UpsertSourceActivity,
R.string.cant_create_source,
Toast.LENGTH_SHORT,
).show()
}
}
}
}
}
override fun onDestroy() {
super.onDestroy()
repository.unsetSelectedSource()
}
}

View File

@ -1,7 +1,6 @@
package bou.amine.apps.readerforselfossv2.android.adapters package bou.amine.apps.readerforselfossv2.android.adapters
import android.app.Activity import android.app.Activity
import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
@ -9,12 +8,11 @@ import android.widget.ImageView.ScaleType
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.databinding.CardItemBinding import bou.amine.apps.readerforselfossv2.android.databinding.CardItemBinding
import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString
import bou.amine.apps.readerforselfossv2.android.utils.LinkOnTouchListener import bou.amine.apps.readerforselfossv2.android.utils.LinkOnTouchListener
import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcraWithName
import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapCenterCrop import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapCenterCrop
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable import bou.amine.apps.readerforselfossv2.android.utils.glide.circularDrawable
import bou.amine.apps.readerforselfossv2.android.utils.openInBrowserAsNewTask import bou.amine.apps.readerforselfossv2.android.utils.openItemUrlInBrowserAsNewTask
import bou.amine.apps.readerforselfossv2.android.utils.openItemUrl
import bou.amine.apps.readerforselfossv2.android.utils.shareLink import bou.amine.apps.readerforselfossv2.android.utils.shareLink
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
@ -22,8 +20,6 @@ import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
import bou.amine.apps.readerforselfossv2.utils.getIcon import bou.amine.apps.readerforselfossv2.utils.getIcon
import bou.amine.apps.readerforselfossv2.utils.getThumbnail import bou.amine.apps.readerforselfossv2.utils.getThumbnail
import com.amulyakhare.textdrawable.TextDrawable
import com.amulyakhare.textdrawable.util.ColorGenerator
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -34,11 +30,10 @@ import org.kodein.di.instance
class ItemCardAdapter( class ItemCardAdapter(
override val app: Activity, override val app: Activity,
override var items: ArrayList<SelfossModel.Item>, override val items: ArrayList<SelfossModel.Item>,
override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit override val updateHomeItems: (ArrayList<SelfossModel.Item>) -> Unit,
) : ItemsAdapter<ItemCardAdapter.ViewHolder>() { ) : ItemsAdapter<ItemCardAdapter.ViewHolder>() {
private val c: Context = app.baseContext override lateinit var binding: CardItemBinding
private val generator: ColorGenerator = ColorGenerator.MATERIAL
private val imageMaxHeight: Int = private val imageMaxHeight: Int =
c.resources.getDimension(R.dimen.card_image_max_height).toInt() c.resources.getDimension(R.dimen.card_image_max_height).toInt()
@ -46,67 +41,20 @@ class ItemCardAdapter(
override val repository: Repository by instance() override val repository: Repository by instance()
override val appSettingsService: AppSettingsService by instance() override val appSettingsService: AppSettingsService by instance()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(
val binding = CardItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) parent: ViewGroup,
viewType: Int,
): ViewHolder {
binding = CardItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding) return ViewHolder(binding)
} }
override fun onBindViewHolder(holder: ViewHolder, position: Int) { private fun handleClickListeners(
with(holder) { holderBinding: CardItemBinding,
val itm = items[position] position: Int,
) {
binding.favButton.isSelected = itm.starred holderBinding.favButton.setOnClickListener {
binding.title.text = itm.title.getHtmlDecoded() val item = items[position]
binding.title.setOnTouchListener(LinkOnTouchListener())
binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent))
binding.sourceTitleAndDate.text = itm.sourceAndDateText()
if (!appSettingsService.isFullHeightCardsEnabled()) {
binding.itemImage.maxHeight = imageMaxHeight
binding.itemImage.scaleType = ScaleType.CENTER_CROP
}
if (itm.getThumbnail(repository.baseUrl).isEmpty()) {
binding.itemImage.visibility = View.GONE
Glide.with(c).clear(binding.itemImage)
binding.itemImage.setImageDrawable(null)
} else {
binding.itemImage.visibility = View.VISIBLE
c.bitmapCenterCrop(itm.getThumbnail(repository.baseUrl), binding.itemImage)
}
if (itm.getIcon(repository.baseUrl).isEmpty()) {
val color = generator.getColor(itm.title.getHtmlDecoded())
val drawable =
TextDrawable
.builder()
.round()
.build(itm.title.getHtmlDecoded().toTextDrawableString(), color)
binding.sourceImage.setImageDrawable(drawable)
} else {
c.circularBitmapDrawable(itm.getIcon(repository.baseUrl), binding.sourceImage)
}
}
}
override fun getItemCount(): Int {
return items.size
}
inner class ViewHolder(val binding: CardItemBinding) : RecyclerView.ViewHolder(binding.root) {
init {
handleClickListeners()
handleLinkOpening()
}
private fun handleClickListeners() {
binding.favButton.setOnClickListener {
val item = items[bindingAdapterPosition]
if (item.starred) { if (item.starred) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
repository.unstarr(item) repository.unstarr(item)
@ -121,25 +69,67 @@ class ItemCardAdapter(
} }
binding.shareBtn.setOnClickListener { binding.shareBtn.setOnClickListener {
val item = items[bindingAdapterPosition] val item = items[position]
c.shareLink(item.getLinkDecoded(), item.title.getHtmlDecoded()) c.shareLink(item.getLinkDecoded(), item.title.getHtmlDecoded())
} }
binding.browserBtn.setOnClickListener { binding.browserBtn.setOnClickListener {
c.openInBrowserAsNewTask(items[bindingAdapterPosition]) c.openItemUrlInBrowserAsNewTask(items[position])
} }
} }
private fun handleLinkOpening() { override fun onBindViewHolder(
binding.root.setOnClickListener { holder: ViewHolder,
c.openItemUrl( position: Int,
items, ) {
bindingAdapterPosition, with(holder) {
items[bindingAdapterPosition].getLinkDecoded(), val itm = items[position]
appSettingsService.isArticleViewerEnabled(),
app handleClickListeners(binding, position)
) handleLinkOpening(binding, position)
binding.favButton.isSelected = itm.starred
if (appSettingsService.getPublicAccess()) {
binding.favButton.visibility = View.GONE
}
binding.title.text = itm.title.getHtmlDecoded()
binding.title.setOnTouchListener(LinkOnTouchListener())
binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent))
binding.sourceTitleAndDate.text =
try {
itm.sourceAuthorAndDate()
} catch (e: Exception) {
e.sendSilentlyWithAcraWithName("ItemCardAdapter parse date")
itm.sourceAuthorOnly()
}
if (!appSettingsService.isFullHeightCardsEnabled()) {
binding.itemImage.maxHeight = imageMaxHeight
binding.itemImage.scaleType = ScaleType.CENTER_CROP
}
if (itm.getThumbnail(repository.baseUrl).isEmpty()) {
binding.itemImage.visibility = View.GONE
Glide.with(c).clear(binding.itemImage)
binding.itemImage.setImageDrawable(null)
} else {
binding.itemImage.visibility = View.VISIBLE
c.bitmapCenterCrop(itm.getThumbnail(repository.baseUrl), binding.itemImage, appSettingsService)
}
if (itm.getIcon(repository.baseUrl).isEmpty()) {
binding.sourceImage.setBackgroundAndText(itm.sourcetitle.getHtmlDecoded())
} else {
c.circularDrawable(itm.getIcon(repository.baseUrl), binding.sourceImage, appSettingsService)
} }
} }
} }
inner class ViewHolder(
val binding: CardItemBinding,
) : RecyclerView.ViewHolder(binding.root)
} }

View File

@ -1,97 +1,79 @@
package bou.amine.apps.readerforselfossv2.android.adapters package bou.amine.apps.readerforselfossv2.android.adapters
import android.app.Activity import android.app.Activity
import android.content.Context
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.databinding.ListItemBinding import bou.amine.apps.readerforselfossv2.android.databinding.ListItemBinding
import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString
import bou.amine.apps.readerforselfossv2.android.utils.LinkOnTouchListener import bou.amine.apps.readerforselfossv2.android.utils.LinkOnTouchListener
import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapCenterCrop import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcraWithName
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable import bou.amine.apps.readerforselfossv2.android.utils.glide.circularDrawable
import bou.amine.apps.readerforselfossv2.android.utils.openItemUrl
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.service.AppSettingsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
import bou.amine.apps.readerforselfossv2.utils.getIcon import bou.amine.apps.readerforselfossv2.utils.getIcon
import bou.amine.apps.readerforselfossv2.utils.getThumbnail import bou.amine.apps.readerforselfossv2.utils.getThumbnail
import com.amulyakhare.textdrawable.TextDrawable
import com.amulyakhare.textdrawable.util.ColorGenerator
import org.kodein.di.DI import org.kodein.di.DI
import org.kodein.di.android.closestDI import org.kodein.di.android.closestDI
import org.kodein.di.instance import org.kodein.di.instance
class ItemListAdapter( class ItemListAdapter(
override val app: Activity, override val app: Activity,
override var items: ArrayList<SelfossModel.Item>, override val items: ArrayList<SelfossModel.Item>,
override val updateItems: (ArrayList<SelfossModel.Item>) -> Unit override val updateHomeItems: (ArrayList<SelfossModel.Item>) -> Unit,
) : ItemsAdapter<ItemListAdapter.ViewHolder>() { ) : ItemsAdapter<ItemListAdapter.ViewHolder>() {
private val generator: ColorGenerator = ColorGenerator.MATERIAL override lateinit var binding: ListItemBinding
private val c: Context = app.baseContext
override val di: DI by closestDI(app) override val di: DI by closestDI(app)
override val repository : Repository by instance() override val repository: Repository by instance()
override val appSettingsService : AppSettingsService by instance() override val appSettingsService: AppSettingsService by instance()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(
val binding = ListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) parent: ViewGroup,
viewType: Int,
): ViewHolder {
binding = ListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding) return ViewHolder(binding)
} }
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(
holder: ViewHolder,
position: Int,
) {
with(holder) { with(holder) {
val itm = items[position] val itm = items[position]
handleLinkOpening(binding, position)
binding.title.text = itm.title.getHtmlDecoded() binding.title.text = itm.title.getHtmlDecoded()
binding.title.setOnTouchListener(LinkOnTouchListener()) binding.title.setOnTouchListener(LinkOnTouchListener())
binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent)) binding.title.setLinkTextColor(c.resources.getColor(R.color.colorAccent))
binding.sourceTitleAndDate.text = itm.sourceAndDateText() binding.sourceTitleAndDate.text =
try {
itm.sourceAuthorAndDate()
} catch (e: Exception) {
e.sendSilentlyWithAcraWithName("ItemListAdapter parse date")
itm.sourceAuthorOnly()
}
if (itm.getThumbnail(repository.baseUrl).isEmpty()) { if (itm.getThumbnail(repository.baseUrl).isEmpty()) {
if (itm.getIcon(repository.baseUrl).isEmpty()) { if (itm.getIcon(repository.baseUrl).isEmpty()) {
val color = generator.getColor(itm.title.getHtmlDecoded()) binding.itemImage.setBackgroundAndText(itm.sourcetitle.getHtmlDecoded())
val drawable =
TextDrawable
.builder()
.round()
.build(itm.title.getHtmlDecoded().toTextDrawableString(), color)
binding.itemImage.setImageDrawable(drawable)
} else { } else {
c.circularBitmapDrawable(itm.getIcon(repository.baseUrl), binding.itemImage) c.circularDrawable(itm.getIcon(repository.baseUrl), binding.itemImage, appSettingsService)
} }
} else { } else {
c.bitmapCenterCrop(itm.getThumbnail(repository.baseUrl), binding.itemImage) c.circularDrawable(itm.getThumbnail(repository.baseUrl), binding.itemImage, appSettingsService)
} }
} }
} }
override fun getItemCount(): Int = items.size inner class ViewHolder(
val binding: ListItemBinding,
inner class ViewHolder(val binding: ListItemBinding) : RecyclerView.ViewHolder(binding.root) { ) : RecyclerView.ViewHolder(binding.root)
init {
handleLinkOpening()
}
private fun handleLinkOpening() {
binding.root.setOnClickListener {
c.openItemUrl(
items,
bindingAdapterPosition,
items[bindingAdapterPosition].getLinkDecoded(),
appSettingsService.isArticleViewerEnabled(),
app
)
}
}
}
} }

View File

@ -1,10 +1,13 @@
package bou.amine.apps.readerforselfossv2.android.adapters package bou.amine.apps.readerforselfossv2.android.adapters
import android.app.Activity import android.app.Activity
import android.content.Context
import android.graphics.Color import android.graphics.Color
import android.widget.TextView import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.viewbinding.ViewBinding
import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.utils.openItemUrl
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.service.AppSettingsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
@ -15,31 +18,38 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.kodein.di.DIAware import org.kodein.di.DIAware
abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapter<VH>(), DIAware { abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> :
abstract var items: ArrayList<SelfossModel.Item> RecyclerView.Adapter<VH>(),
DIAware {
abstract val items: ArrayList<SelfossModel.Item>
abstract val repository: Repository abstract val repository: Repository
abstract val binding: ViewBinding
abstract val appSettingsService: AppSettingsService abstract val appSettingsService: AppSettingsService
abstract val app: Activity abstract val app: Activity
abstract val updateItems: (ArrayList<SelfossModel.Item>) -> Unit abstract val updateHomeItems: (ArrayList<SelfossModel.Item>) -> Unit
protected val c: Context get() = app.baseContext
fun updateAllItems(items: ArrayList<SelfossModel.Item>) { fun updateAllItems(items: ArrayList<SelfossModel.Item>) {
this.items = items this.items.clear()
this.items.addAll(items)
updateHomeItems(items)
notifyDataSetChanged() notifyDataSetChanged()
updateItems(this.items)
} }
private fun unmarkSnackbar(item: SelfossModel.Item, position: Int) { private fun unmarkSnackbar(
val s = Snackbar item: SelfossModel.Item,
position: Int,
) {
val s =
Snackbar
.make( .make(
app.findViewById(R.id.coordLayout), app.findViewById(R.id.coordLayout),
R.string.marked_as_read, R.string.marked_as_read,
Snackbar.LENGTH_LONG Snackbar.LENGTH_LONG,
) ).setAction(R.string.undo_string) {
.setAction(R.string.undo_string) {
CoroutineScope(Dispatchers.IO).launch {
unreadItemAtIndex(item, position, false) unreadItemAtIndex(item, position, false)
} }
}
val view = s.view val view = s.view
val tv: TextView = view.findViewById(com.google.android.material.R.id.snackbar_text) val tv: TextView = view.findViewById(com.google.android.material.R.id.snackbar_text)
@ -47,14 +57,17 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
s.show() s.show()
} }
private fun markSnackbar(item: SelfossModel.Item, position: Int) { private fun markSnackbar(
val s = Snackbar item: SelfossModel.Item,
position: Int,
) {
val s =
Snackbar
.make( .make(
app.findViewById(R.id.coordLayout), app.findViewById(R.id.coordLayout),
R.string.marked_as_unread, R.string.marked_as_unread,
Snackbar.LENGTH_LONG Snackbar.LENGTH_LONG,
) ).setAction(R.string.undo_string) {
.setAction(R.string.undo_string) {
readItemAtIndex(item, position, false) readItemAtIndex(item, position, false)
} }
@ -64,6 +77,21 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
s.show() s.show()
} }
protected fun handleLinkOpening(
holderBinding: ViewBinding,
position: Int,
) {
holderBinding.root.setOnClickListener {
repository.setReaderItems(items)
c.openItemUrl(
position,
items[position].getLinkDecoded(),
appSettingsService.isArticleViewerEnabled(),
app,
)
}
}
fun handleItemAtIndex(position: Int) { fun handleItemAtIndex(position: Int) {
if (items[position].unread) { if (items[position].unread) {
readItemAtIndex(items[position], position) readItemAtIndex(items[position], position)
@ -72,14 +100,19 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
} }
} }
private fun readItemAtIndex(item: SelfossModel.Item, position: Int, showSnackbar: Boolean = true) { private fun readItemAtIndex(
item: SelfossModel.Item,
position: Int,
showSnackbar: Boolean = true,
) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
repository.markAsRead(item) repository.markAsRead(item)
} }
if (repository.displayedItems == ItemType.UNREAD) { if (repository.displayedItems == ItemType.UNREAD) {
items.remove(item) items.remove(item)
notifyItemRemoved(position) notifyItemRemoved(position)
updateItems(items) notifyItemRangeChanged(position, itemCount)
updateHomeItems(items)
} else { } else {
notifyItemChanged(position) notifyItemChanged(position)
} }
@ -88,10 +121,13 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
} }
} }
private fun unreadItemAtIndex(item: SelfossModel.Item, position: Int, showSnackbar: Boolean = true) { private fun unreadItemAtIndex(
item: SelfossModel.Item,
position: Int,
showSnackbar: Boolean = true,
) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
repository.unmarkAsRead(item) repository.unmarkAsRead(item)
} }
notifyItemChanged(position) notifyItemChanged(position)
if (showSnackbar) { if (showSnackbar) {
@ -99,18 +135,21 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
} }
} }
fun addItemAtIndex(item: SelfossModel.Item, position: Int) { fun addItemAtIndex(
item: SelfossModel.Item,
position: Int,
) {
items.add(position, item) items.add(position, item)
notifyItemInserted(position) notifyItemInserted(position)
updateItems(items) updateHomeItems(items)
} }
fun addItemsAtEnd(newItems: List<SelfossModel.Item>) { fun addItemsAtEnd(newItems: List<SelfossModel.Item>) {
val oldSize = items.size val oldSize = items.size
items.addAll(newItems) items.addAll(newItems)
notifyItemRangeInserted(oldSize, newItems.size) notifyItemRangeInserted(oldSize, newItems.size)
updateItems(items) updateHomeItems(items)
} }
override fun getItemCount(): Int = items.size
} }

View File

@ -2,22 +2,23 @@ package bou.amine.apps.readerforselfossv2.android.adapters
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View
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 androidx.constraintlayout.widget.ConstraintLayout import androidx.constraintlayout.widget.ConstraintLayout
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.UpsertSourceActivity
import bou.amine.apps.readerforselfossv2.android.databinding.SourceListItemBinding import bou.amine.apps.readerforselfossv2.android.databinding.SourceListItemBinding
import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString import bou.amine.apps.readerforselfossv2.android.utils.glide.circularDrawable
import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
import bou.amine.apps.readerforselfossv2.utils.getIcon import bou.amine.apps.readerforselfossv2.utils.getIcon
import com.amulyakhare.textdrawable.TextDrawable
import com.amulyakhare.textdrawable.util.ColorGenerator
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -28,34 +29,69 @@ import org.kodein.di.instance
class SourcesListAdapter( class SourcesListAdapter(
private val app: Activity, private val app: Activity,
private val items: ArrayList<SelfossModel.Source> private val items: ArrayList<SelfossModel.SourceDetail>,
) : RecyclerView.Adapter<SourcesListAdapter.ViewHolder>(), DIAware { ) : RecyclerView.Adapter<SourcesListAdapter.ViewHolder>(),
DIAware {
private val c: Context = app.baseContext private val c: Context = app.baseContext
private val generator: ColorGenerator = ColorGenerator.MATERIAL
private lateinit var binding: SourceListItemBinding private lateinit var binding: SourceListItemBinding
override val di: DI by closestDI(app) override val di: DI by closestDI(app)
private val repository : Repository by instance() private val repository: Repository by instance()
private val appSettingsService: AppSettingsService by instance()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int,
): ViewHolder {
binding = SourceListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false) binding = SourceListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
return ViewHolder(binding.root) return ViewHolder(binding.root)
} }
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(
holder: ViewHolder,
position: Int,
) {
val itm = items[position] val itm = items[position]
if (itm.getIcon(repository.baseUrl).isEmpty()) { val deleteBtn: Button = holder.mView.findViewById(R.id.deleteBtn)
val color = generator.getColor(itm.title.getHtmlDecoded())
val drawable = deleteBtn.setOnClickListener {
TextDrawable val (id, title) = items[position]
.builder() CoroutineScope(Dispatchers.IO).launch {
.round() val successfullyDeletedSource = repository.deleteSource(id, title)
.build(itm.title.getHtmlDecoded().toTextDrawableString(), color) if (successfullyDeletedSource) {
binding.itemImage.setImageDrawable(drawable) items.removeAt(position)
notifyItemRemoved(position)
notifyItemRangeChanged(position, itemCount)
} else { } else {
c.circularBitmapDrawable(itm.getIcon(repository.baseUrl), binding.itemImage) Toast
.makeText(
app,
R.string.can_delete_source,
Toast.LENGTH_SHORT,
).show()
}
}
}
holder.mView.setOnClickListener {
val source = items[position]
repository.setSelectedSource(source)
app.startActivity(Intent(app, UpsertSourceActivity::class.java))
}
if (itm.getIcon(repository.baseUrl).isEmpty()) {
binding.itemImage.setBackgroundAndText(itm.title.getHtmlDecoded())
} else {
c.circularDrawable(itm.getIcon(repository.baseUrl), binding.itemImage, appSettingsService)
}
if (!itm.error.isNullOrBlank()) {
binding.errorText.visibility = View.VISIBLE
binding.errorText.text = itm.error
} else {
binding.errorText.visibility = View.GONE
} }
binding.sourceTitle.text = itm.title.getHtmlDecoded() binding.sourceTitle.text = itm.title.getHtmlDecoded()
@ -67,33 +103,7 @@ class SourcesListAdapter(
override fun getItemCount(): Int = items.size override fun getItemCount(): Int = items.size
inner class ViewHolder(private val mView: ConstraintLayout) : RecyclerView.ViewHolder(mView) { inner class ViewHolder(
val mView: ConstraintLayout,
init { ) : RecyclerView.ViewHolder(mView)
handleClickListeners()
}
private fun handleClickListeners() {
val deleteBtn: Button = mView.findViewById(R.id.deleteBtn)
deleteBtn.setOnClickListener {
val (id) = items[bindingAdapterPosition]
CoroutineScope(Dispatchers.IO).launch {
val successfullyDeletedSource = repository.deleteSource(id)
if (successfullyDeletedSource) {
items.removeAt(bindingAdapterPosition)
notifyItemRemoved(bindingAdapterPosition)
notifyItemRangeChanged(bindingAdapterPosition, itemCount)
} else {
Toast.makeText(
app,
R.string.can_delete_source,
Toast.LENGTH_SHORT
).show()
}
}
}
}
}
} }

View File

@ -0,0 +1,120 @@
package bou.amine.apps.readerforselfossv2.android.background
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.PRIORITY_DEFAULT
import androidx.core.app.NotificationCompat.PRIORITY_LOW
import androidx.work.Worker
import androidx.work.WorkerParameters
import bou.amine.apps.readerforselfossv2.android.MainActivity
import bou.amine.apps.readerforselfossv2.android.MyApp
import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.model.preloadImages
import bou.amine.apps.readerforselfossv2.android.utils.network.isNetworkAccessible
import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.kodein.di.DIAware
import org.kodein.di.instance
import java.util.Timer
import kotlin.concurrent.schedule
private const val NOTIFICATION_DELAY = 4000L
class LoadingWorker(
val context: Context,
params: WorkerParameters,
) : Worker(context, params),
DIAware {
override val di by lazy { (applicationContext as MyApp).di }
private val repository: Repository by instance()
private val appSettingsService: AppSettingsService by instance()
override fun doWork(): Result {
if (appSettingsService.isPeriodicRefreshEnabled() && isNetworkAccessible(context)) {
CoroutineScope(Dispatchers.IO).launch {
val notificationManager =
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val notification =
NotificationCompat
.Builder(applicationContext, AppSettingsService.SYNC_CHANNEL_ID)
.setContentTitle(context.getString(R.string.loading_notification_title))
.setContentText(context.getString(R.string.loading_notification_text))
.setOngoing(true)
.setPriority(PRIORITY_LOW)
.setChannelId(AppSettingsService.SYNC_CHANNEL_ID)
.setSmallIcon(R.drawable.ic_stat_cloud_download_black_24dp)
notificationManager.notify(1, notification.build())
repository.handleDBActions()
val apiItems = repository.tryToCacheItemsAndGetNewOnes()
if (appSettingsService.isNotifyNewItemsEnabled()) {
launch {
handleNewItemsNotification(apiItems, notificationManager)
}
}
apiItems.map { it.preloadImages(context, appSettingsService) }
}
}
return Result.success()
}
private fun handleNewItemsNotification(
newItems: List<SelfossModel.Item>?,
notificationManager: NotificationManager,
) {
CoroutineScope(Dispatchers.IO).launch {
val apiItems = newItems.orEmpty()
val newSize = apiItems.filter { it.unread }.size
if (newSize > 0) {
val intent =
Intent(context, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pflags =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.FLAG_IMMUTABLE
} else {
0
}
val pendingIntent: PendingIntent =
PendingIntent.getActivity(context, 0, intent, pflags)
val newItemsNotification =
NotificationCompat
.Builder(
applicationContext,
AppSettingsService.NEW_ITEMS_CHANNEL,
).setContentTitle(context.getString(R.string.new_items_notification_title))
.setContentText(
context.getString(
R.string.new_items_notification_text,
newSize,
),
).setPriority(PRIORITY_DEFAULT)
.setChannelId(AppSettingsService.NEW_ITEMS_CHANNEL)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_tab_fiber_new_black_24dp)
Timer("", false).schedule(NOTIFICATION_DELAY) {
notificationManager.notify(2, newItemsNotification.build())
}
}
Timer("", false).schedule(NOTIFICATION_DELAY) {
notificationManager.cancel(1)
}
}
}
}

View File

@ -1,113 +0,0 @@
package bou.amine.apps.readerforselfossv2.android.background
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.PRIORITY_DEFAULT
import androidx.core.app.NotificationCompat.PRIORITY_LOW
import androidx.work.Worker
import androidx.work.WorkerParameters
import bou.amine.apps.readerforselfossv2.android.MainActivity
import bou.amine.apps.readerforselfossv2.android.MyApp
import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.model.preloadImages
import bou.amine.apps.readerforselfossv2.android.utils.network.isNetworkAccessible
import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.kodein.di.DIAware
import org.kodein.di.instance
import java.util.*
import kotlin.concurrent.schedule
class LoadingWorker(val context: Context, params: WorkerParameters) : Worker(context, params), DIAware {
override val di by lazy { (applicationContext as MyApp).di }
private val repository : Repository by instance()
private val appSettingsService : AppSettingsService by instance()
override fun doWork(): Result {
if (appSettingsService.isPeriodicRefreshEnabled() && isNetworkAccessible(context)) {
CoroutineScope(Dispatchers.IO).launch {
val notificationManager =
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val notification =
NotificationCompat.Builder(applicationContext, AppSettingsService.syncChannelId)
.setContentTitle(context.getString(R.string.loading_notification_title))
.setContentText(context.getString(R.string.loading_notification_text))
.setOngoing(true)
.setPriority(PRIORITY_LOW)
.setChannelId(AppSettingsService.syncChannelId)
.setSmallIcon(R.drawable.ic_stat_cloud_download_black_24dp)
notificationManager.notify(1, notification.build())
repository.handleDBActions()
val apiItems = repository.tryToCacheItemsAndGetNewOnes()
if (appSettingsService.isNotifyNewItemsEnabled()) {
launch {
handleNewItemsNotification(apiItems, notificationManager)
}
}
apiItems.map { it.preloadImages(context) }
}
}
return Result.success()
}
private fun handleNewItemsNotification(
newItems: List<SelfossModel.Item>?,
notificationManager: NotificationManager
) {
// TODO: Check if this coroutine is actually required
CoroutineScope(Dispatchers.IO).launch {
val apiItems = newItems.orEmpty()
val newSize = apiItems.filter { it.unread }.size
if (newSize > 0) {
val intent = Intent(context, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
val pflags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.FLAG_IMMUTABLE
} else {
0
}
val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, pflags)
val newItemsNotification =
NotificationCompat.Builder(applicationContext, AppSettingsService.newItemsChannelId)
.setContentTitle(context.getString(R.string.new_items_notification_title))
.setContentText(
context.getString(
R.string.new_items_notification_text,
newSize
)
)
.setPriority(PRIORITY_DEFAULT)
.setChannelId(AppSettingsService.newItemsChannelId)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.setSmallIcon(R.drawable.ic_tab_fiber_new_black_24dp)
Timer("", false).schedule(4000) {
notificationManager.notify(2, newItemsNotification.build())
}
}
Timer("", false).schedule(4000) {
notificationManager.cancel(1)
}
}
}
}

View File

@ -1,23 +1,24 @@
package bou.amine.apps.readerforselfossv2.android.fragments package bou.amine.apps.readerforselfossv2.android.fragments
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.res.ColorStateList
import android.content.res.TypedArray import android.content.res.TypedArray
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.Typeface import android.graphics.Typeface
import android.graphics.drawable.ColorDrawable
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.util.TypedValue import android.util.TypedValue.DATA_NULL_UNDEFINED
import android.view.* import android.view.GestureDetector
import android.view.InflateException
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.webkit.WebResourceResponse import android.webkit.WebResourceResponse
import android.webkit.WebSettings import android.webkit.WebSettings
import android.webkit.WebView import android.webkit.WebView
import android.webkit.WebViewClient import android.webkit.WebViewClient
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.content.res.ResourcesCompat
import androidx.core.widget.NestedScrollView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import bou.amine.apps.readerforselfossv2.android.ImageActivity import bou.amine.apps.readerforselfossv2.android.ImageActivity
import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.R
@ -25,10 +26,18 @@ import bou.amine.apps.readerforselfossv2.android.databinding.FragmentArticleBind
import bou.amine.apps.readerforselfossv2.android.model.ParecelableItem import bou.amine.apps.readerforselfossv2.android.model.ParecelableItem
import bou.amine.apps.readerforselfossv2.android.model.toModel import bou.amine.apps.readerforselfossv2.android.model.toModel
import bou.amine.apps.readerforselfossv2.android.model.toParcelable import bou.amine.apps.readerforselfossv2.android.model.toParcelable
import bou.amine.apps.readerforselfossv2.android.sendSilentlyWithAcraWithName import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcraWithName
import bou.amine.apps.readerforselfossv2.android.utils.bottombar.addHomeMadeActionItem
import bou.amine.apps.readerforselfossv2.android.utils.getColorFromAttr
import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapFitCenter
import bou.amine.apps.readerforselfossv2.android.utils.glide.getBitmapInputStream import bou.amine.apps.readerforselfossv2.android.utils.glide.getBitmapInputStream
import bou.amine.apps.readerforselfossv2.android.utils.openInBrowserAsNewTask import bou.amine.apps.readerforselfossv2.android.utils.glide.getGlideImageForResource
import bou.amine.apps.readerforselfossv2.android.utils.isUrlValid
import bou.amine.apps.readerforselfossv2.android.utils.maybeIfContext
import bou.amine.apps.readerforselfossv2.android.utils.openItemUrlInBrowserAsNewTask
import bou.amine.apps.readerforselfossv2.android.utils.openUrlInBrowserAsNewTask
import bou.amine.apps.readerforselfossv2.android.utils.shareLink import bou.amine.apps.readerforselfossv2.android.utils.shareLink
import bou.amine.apps.readerforselfossv2.model.MercuryModel
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.repository.Repository import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.rest.MercuryApi import bou.amine.apps.readerforselfossv2.rest.MercuryApi
@ -37,52 +46,54 @@ import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
import bou.amine.apps.readerforselfossv2.utils.getImages import bou.amine.apps.readerforselfossv2.utils.getImages
import bou.amine.apps.readerforselfossv2.utils.getThumbnail import bou.amine.apps.readerforselfossv2.utils.getThumbnail
import bou.amine.apps.readerforselfossv2.utils.isEmptyOrNullOrNullString import bou.amine.apps.readerforselfossv2.utils.isEmptyOrNullOrNullString
import com.bumptech.glide.Glide import com.leinardi.android.speeddial.SpeedDialView
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.RequestOptions
import com.github.rubensousa.floatingtoolbar.FloatingToolbar
import com.google.android.material.floatingactionbutton.FloatingActionButton
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.acra.ktx.sendSilentlyWithAcra import org.acra.ktx.sendSilentlyWithAcra
import org.acra.ktx.sendWithAcra
import org.kodein.di.DI import org.kodein.di.DI
import org.kodein.di.DIAware import org.kodein.di.DIAware
import org.kodein.di.android.x.closestDI import org.kodein.di.android.x.closestDI
import org.kodein.di.instance import org.kodein.di.instance
import java.net.MalformedURLException import java.net.MalformedURLException
import java.net.SocketTimeoutException
import java.net.URL import java.net.URL
import java.util.* import java.util.Locale
import java.util.concurrent.ExecutionException import java.util.concurrent.ExecutionException
private const val IMAGE_JPG = "image/jpg"
private const val IMAGE_PNG = "image/png"
private const val IMAGE_WEBP = "image/webp"
class ArticleFragment : Fragment(), DIAware { private const val WHITE_COLOR_HEX = 0xFFFFFF
private var fontSize: Int = 16
private const val DEFAULT_FONT_SIZE = 16
class ArticleFragment :
Fragment(),
DIAware {
private var colorOnSurface: Int = 0
private var colorSurface: Int = 0
private var fontSize: Int = DEFAULT_FONT_SIZE
private lateinit var item: SelfossModel.Item private lateinit var item: SelfossModel.Item
private lateinit var url: String private lateinit var url: String
private lateinit var contentText: String private lateinit var contentText: String
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 allImages : ArrayList<String> private lateinit var allImages: ArrayList<String>
private lateinit var fab: FloatingActionButton private lateinit var fab: SpeedDialView
private lateinit var textAlignment: String private lateinit var textAlignment: String
private var _binding: FragmentArticleBinding? = null private lateinit var binding: FragmentArticleBinding
private val binding get() = _binding!!
override val di : DI by closestDI() override val di: DI by closestDI()
private val repository: Repository by instance() private val repository: Repository by instance()
private val appSettingsService: AppSettingsService by instance() private val appSettingsService: AppSettingsService by instance()
private var typeface: Typeface? = null private var typeface: Typeface? = null
private var resId: Int = 0 private var resId: Int = 0
private var font = "" private var font = ""
private var staticBar = false
private val mercuryApi : MercuryApi by instance()
private val mercuryApi: MercuryApi by instance()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -92,88 +103,74 @@ class ArticleFragment : Fragment(), DIAware {
item = pi.toModel() item = pi.toModel()
} }
@Suppress("detekt:LongMethod")
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?,
): View { ): View {
try { try {
_binding = FragmentArticleBinding.inflate(inflater, container, false) binding = FragmentArticleBinding.inflate(inflater, container, false)
try {
url = item.getLinkDecoded() url = item.getLinkDecoded()
} catch (e: Exception) {
e.sendSilentlyWithAcra()
}
colorOnSurface = getColorFromAttr(com.google.android.material.R.attr.colorOnSurface)
colorSurface = getColorFromAttr(com.google.android.material.R.attr.colorSurface)
contentText = item.content contentText = item.content
contentTitle = item.title.getHtmlDecoded() contentTitle = item.title.getHtmlDecoded()
contentImage = item.getThumbnail(repository.baseUrl) contentImage = item.getThumbnail(repository.baseUrl)
contentSource = item.sourceAndDateText() contentSource =
try {
item.sourceAuthorAndDate()
} catch (e: Exception) {
e.sendSilentlyWithAcraWithName("Article Fragment parse date")
item.sourceAuthorOnly()
}
allImages = item.getImages() allImages = item.getImages()
fontSize = appSettingsService.getFontSize() fontSize = appSettingsService.getFontSize()
staticBar = appSettingsService.isStaticBarEnabled()
font = appSettingsService.getFont() font = appSettingsService.getFont()
refreshAlignment() refreshAlignment()
fab = binding.fab handleFloatingToolbar()
fab.backgroundTintList = ColorStateList.valueOf(resources.getColor(R.color.colorAccent))
fab.rippleColor = resources.getColor(R.color.colorAccentDark)
val floatingToolbar: FloatingToolbar = binding.floatingToolbar
floatingToolbar.attachFab(fab)
floatingToolbar.background = ColorDrawable(resources.getColor(R.color.colorAccent))
floatingToolbar.setClickListener(
object : FloatingToolbar.ItemClickListener {
override fun onItemClick(item: MenuItem) {
when (item.itemId) {
R.id.share_action -> requireActivity().shareLink(url, contentTitle)
R.id.open_action -> requireActivity().openInBrowserAsNewTask(this@ArticleFragment.item)
R.id.unread_action -> if (context != null) {
if (this@ArticleFragment.item.unread) {
CoroutineScope(Dispatchers.IO).launch {
repository.markAsRead(this@ArticleFragment.item)
}
this@ArticleFragment.item.unread = false
Toast.makeText(
context,
R.string.marked_as_read,
Toast.LENGTH_LONG
).show()
} else {
CoroutineScope(Dispatchers.IO).launch {
repository.unmarkAsRead(this@ArticleFragment.item)
}
this@ArticleFragment.item.unread = true
Toast.makeText(
context,
R.string.marked_as_unread,
Toast.LENGTH_LONG
).show()
}
}
else -> Unit
}
}
override fun onItemLongClick(item: MenuItem?) {
}
}
)
if (staticBar) {
fab.hide()
floatingToolbar.show()
}
binding.source.text = contentSource binding.source.text = contentSource
if (typeface != null) { if (typeface != null) {
binding.source.typeface = typeface binding.source.typeface = typeface
} }
handleContent()
} catch (e: InflateException) {
e.sendSilentlyWithAcraWithName("webview not available")
maybeIfContext {
AlertDialog
.Builder(it)
.setMessage(it.getString(R.string.webview_dialog_issue_message))
.setTitle(it.getString(R.string.webview_dialog_issue_title))
.setPositiveButton(
android.R.string.ok,
) { _, _ ->
appSettingsService.disableArticleViewer()
requireActivity().finish()
}.create()
.show()
}
}
return binding.root
}
private fun handleContent() {
if (contentText.isEmptyOrNullOrNullString()) { if (contentText.isEmptyOrNullOrNullString()) {
if (repository.isNetworkAvailable()) {
getContentFromMercury() getContentFromMercury()
}
} else { } else {
binding.titleView.text = contentTitle binding.titleView.text = contentTitle
if (typeface != null) { if (typeface != null) {
@ -184,225 +181,273 @@ class ArticleFragment : Fragment(), DIAware {
if (!contentImage.isEmptyOrNullOrNullString() && context != null) { if (!contentImage.isEmptyOrNullOrNullString() && context != null) {
binding.imageView.visibility = View.VISIBLE binding.imageView.visibility = View.VISIBLE
Glide maybeIfContext { it.bitmapFitCenter(contentImage, binding.imageView, appSettingsService) }
.with(requireContext())
.asBitmap()
.load(contentImage)
.apply(RequestOptions.fitCenterTransform())
.into(binding.imageView)
} else { } else {
binding.imageView.visibility = View.GONE binding.imageView.visibility = View.GONE
} }
} }
}
binding.nestedScrollView.setOnScrollChangeListener( private fun handleFloatingToolbar() {
NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY -> fab = binding.speedDial
if (scrollY > oldScrollY) { fab.mainFabClosedIconColor = colorOnSurface
floatingToolbar.hide() fab.mainFabOpenedIconColor = colorOnSurface
fab.hide()
maybeIfContext { handleFloatingToolbarActionItems(it) }
fab.setOnActionSelectedListener { actionItem ->
when (actionItem.id) {
R.id.share_action -> requireActivity().shareLink(url, contentTitle)
R.id.open_action -> requireActivity().openItemUrlInBrowserAsNewTask(this@ArticleFragment.item)
R.id.unread_action ->
if (this@ArticleFragment.item.unread) {
CoroutineScope(Dispatchers.IO).launch {
repository.markAsRead(this@ArticleFragment.item)
}
this@ArticleFragment.item.unread = false
maybeIfContext {
Toast
.makeText(
it,
R.string.marked_as_read,
Toast.LENGTH_LONG,
).show()
}
} else { } else {
if (staticBar) { CoroutineScope(Dispatchers.IO).launch {
floatingToolbar.show() repository.unmarkAsRead(this@ArticleFragment.item)
} else { }
if (floatingToolbar.isShowing) floatingToolbar.hide() else fab.show() this@ArticleFragment.item.unread = true
maybeIfContext {
Toast
.makeText(
it,
R.string.marked_as_unread,
Toast.LENGTH_LONG,
).show()
} }
} }
else -> Unit
} }
false
}
}
private fun handleFloatingToolbarActionItems(c: Context) {
fab.addHomeMadeActionItem(
R.id.share_action,
resources.getDrawable(R.drawable.ic_share_white_24dp),
R.string.reader_action_share,
colorOnSurface,
colorSurface,
c,
)
fab.addHomeMadeActionItem(
R.id.open_action,
resources.getDrawable(R.drawable.ic_open_in_browser_white_24dp),
R.string.reader_action_open,
colorOnSurface,
colorSurface,
c,
)
fab.addHomeMadeActionItem(
R.id.unread_action,
resources.getDrawable(R.drawable.ic_baseline_white_eye_24dp),
R.string.unmark,
colorOnSurface,
colorSurface,
c,
) )
} catch (e: InflateException) {
e.sendSilentlyWithAcraWithName("webview not available")
AlertDialog.Builder(requireContext())
.setMessage(requireContext().getString(R.string.webview_dialog_issue_message))
.setTitle(requireContext().getString(R.string.webview_dialog_issue_title))
.setPositiveButton(android.R.string.ok
) { _, _ ->
appSettingsService.disableArticleViewer()
requireActivity().finish()
}
.create()
.show()
}
return binding.root
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
} }
private fun refreshAlignment() { private fun refreshAlignment() {
textAlignment = when (appSettingsService.getActiveAllignment()) { textAlignment =
when (appSettingsService.getActiveAllignment()) {
1 -> "justify" 1 -> "justify"
2 -> "left" 2 -> "left"
else -> "justify" else -> "justify"
} }
} }
@Suppress("detekt:SwallowedException")
private fun getContentFromMercury() { private fun getContentFromMercury() {
if (repository.isNetworkAvailable()) {
binding.progressBar.visibility = View.VISIBLE binding.progressBar.visibility = View.VISIBLE
CoroutineScope(Dispatchers.Main).launch { CoroutineScope(Dispatchers.Main).launch {
try { try {
val response = mercuryApi.query(url) val response = mercuryApi.query(url)
if (response.success && response.data != null && !response.data?.content.isNullOrEmpty()) { if (response.success && response.data != null) {
binding.titleView.text = response.data!!.title.orEmpty() handleMercuryData(response.data!!)
try { } else {
openInBrowserAfterFailing()
}
} catch (e: Exception) {
openInBrowserAfterFailing()
}
}
}
private fun handleMercuryData(data: MercuryModel.ParsedContent) {
if (data.error == true || data.failed == true) {
openInBrowserAfterFailing()
} else {
binding.titleView.text = data.title.orEmpty()
if (typeface != null) { if (typeface != null) {
binding.titleView.typeface = typeface binding.titleView.typeface = typeface
} }
} catch (e: Exception) { URL(data.url)
e.sendSilentlyWithAcraWithName("getContentFromMercury > typeface") url = data.url!!
}
try { contentText = data.content.orEmpty()
// Note: Mercury may return relative urls... If it does the url val will not be changed.
URL(response.data!!.url)
url = response.data!!.url
} catch (e: MalformedURLException) {
// Mercury returned a relative url
e.sendSilentlyWithAcraWithName("getContentFromMercury > malformedurlexception")
}
try {
contentText = response.data!!.content.orEmpty()
htmlToWebview() htmlToWebview()
} catch (e: Exception) {
e.sendSilentlyWithAcraWithName("getContentFromMercury > contenttext or html") handleLeadImage(data.lead_image_url)
binding.nestedScrollView.scrollTo(0, 0)
binding.progressBar.visibility = View.GONE
}
} }
if (!response.data?.lead_image_url.isNullOrEmpty() && context != null) { private fun handleLeadImage(leadImageUrl: String?) {
try { if (!leadImageUrl.isNullOrEmpty()) {
maybeIfContext {
binding.imageView.visibility = View.VISIBLE binding.imageView.visibility = View.VISIBLE
try { it.bitmapFitCenter(leadImageUrl, binding.imageView, appSettingsService)
Glide
.with(requireContext())
.asBitmap()
.load(
response.data!!.lead_image_url.orEmpty()
)
.apply(RequestOptions.fitCenterTransform())
.into(binding.imageView)
} catch (e: Exception) {
e.sendSilentlyWithAcraWithName("getContentFromMercury > glide lead image")
}
} catch (e: Exception) {
e.sendSilentlyWithAcraWithName("getContentFromMercury > outside glide lead image")
} }
} else { } else {
binding.imageView.visibility = View.GONE binding.imageView.visibility = View.GONE
} }
}
private fun handleImageLoading() {
binding.webcontent.webViewClient =
object : WebViewClient() {
@Deprecated("Deprecated in Java")
override fun shouldOverrideUrlLoading(
view: WebView?,
url: String,
): Boolean =
if (url.isUrlValid() &&
binding.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE
) {
maybeIfContext { it.openUrlInBrowserAsNewTask(url) }
true
} else {
false
}
@Suppress("detekt:SwallowedException", "detekt:ReturnCount")
@Deprecated("Deprecated in Java")
override fun shouldInterceptRequest(
view: WebView,
url: String,
): WebResourceResponse? {
val (mime: String?, compression: Bitmap.CompressFormat) =
if (url
.lowercase(Locale.US)
.contains(".jpg") ||
url.lowercase(Locale.US).contains(".jpeg")
) {
Pair(IMAGE_JPG, Bitmap.CompressFormat.JPEG)
} else if (url.lowercase(Locale.US).contains(".png")) {
Pair(IMAGE_PNG, Bitmap.CompressFormat.PNG)
} else if (url.lowercase(Locale.US).contains(".webp")) {
Pair(IMAGE_WEBP, Bitmap.CompressFormat.WEBP)
} else {
return super.shouldInterceptRequest(view, url)
}
try { try {
binding.nestedScrollView.scrollTo(0, 0) val image = view.getGlideImageForResource(url, appSettingsService)
binding.progressBar.visibility = View.GONE return WebResourceResponse(
} catch (e: Exception) { mime,
e.sendSilentlyWithAcraWithName("getContentFromMercury > scrollview") "UTF-8",
} getBitmapInputStream(image, compression),
} else { )
openInBrowserAfterFailing() } catch (e: ExecutionException) {
} return super.shouldInterceptRequest(view, url)
} catch (e: SocketTimeoutException) {
openInBrowserAfterFailing()
} catch (e: Exception) {
e.sendSilentlyWithAcraWithName("getContentFromMercury > whole thing")
openInBrowserAfterFailing()
} }
} }
} }
} }
@Suppress("detekt:LongMethod", "detekt:ImplicitDefaultLocale")
private fun htmlToWebview() { private fun htmlToWebview() {
maybeIfContext {
val attrs: IntArray = intArrayOf(android.R.attr.fontFamily) val attrs: IntArray = intArrayOf(android.R.attr.fontFamily)
val a: TypedArray = requireContext().obtainStyledAttributes(resId, attrs) val a: TypedArray = it.obtainStyledAttributes(resId, attrs)
binding.webcontent.settings.standardFontFamily = a.getString(0) binding.webcontent.settings.standardFontFamily = a.getString(0)
""
}
binding.webcontent.visibility = View.VISIBLE binding.webcontent.visibility = View.VISIBLE
val colorOnSurface = TypedValue() val colorSurfaceString =
requireContext().theme.resolveAttribute(R.attr.colorOnSurface, colorOnSurface, true) String.format(
"#%06X",
WHITE_COLOR_HEX and (if (colorSurface != DATA_NULL_UNDEFINED) colorSurface else WHITE_COLOR_HEX),
)
val colorSurface = TypedValue() val colorOnSurfaceString =
requireContext().theme.resolveAttribute(R.attr.colorSurface, colorSurface, true) String.format(
"#%06X",
WHITE_COLOR_HEX and (if (colorOnSurface != DATA_NULL_UNDEFINED) colorOnSurface else 0),
)
binding.webcontent.settings.useWideViewPort = true binding.webcontent.settings.useWideViewPort = true
binding.webcontent.settings.loadWithOverviewMode = true binding.webcontent.settings.loadWithOverviewMode = true
binding.webcontent.settings.javaScriptEnabled = false binding.webcontent.settings.javaScriptEnabled = false
binding.webcontent.webViewClient = object : WebViewClient() { handleImageLoading()
@Deprecated("Deprecated in Java")
override fun shouldOverrideUrlLoading(view: WebView?, url : String): Boolean {
if (binding.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
requireContext().startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
}
return true
}
@Deprecated("Deprecated in Java")
override fun shouldInterceptRequest(view: WebView?, url: String): WebResourceResponse? {
val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL)
if (url.lowercase(Locale.US).contains(".jpg") || url.lowercase(Locale.US).contains(".jpeg")) {
try { try {
val image = Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get() val gestureDetector =
return WebResourceResponse("image/jpg", "UTF-8", getBitmapInputStream(image, Bitmap.CompressFormat.JPEG)) GestureDetector(
} catch ( e : ExecutionException) { activity,
e.sendSilentlyWithAcraWithName("shouldInterceptRequest > jpeg") object : GestureDetector.SimpleOnGestureListener() {
} override fun onSingleTapUp(e: MotionEvent): Boolean = performClick()
} },
else if (url.lowercase(Locale.US).contains(".png")) { )
try {
val image = Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get()
return WebResourceResponse("image/jpg", "UTF-8", getBitmapInputStream(image, Bitmap.CompressFormat.PNG))
} catch ( e : ExecutionException) {
e.sendSilentlyWithAcraWithName("shouldInterceptRequest > png")
}
}
else if (url.lowercase(Locale.US).contains(".webp")) {
try {
val image = Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get()
return WebResourceResponse("image/jpg", "UTF-8", getBitmapInputStream(image, Bitmap.CompressFormat.WEBP))
} catch ( e : ExecutionException) {
e.sendSilentlyWithAcraWithName("shouldInterceptRequest > webp")
}
}
return super.shouldInterceptRequest(view, url) binding.webcontent.setOnTouchListener { _, event ->
gestureDetector.onTouchEvent(
event,
)
} }
} catch (e: IllegalStateException) {
e.sendSilentlyWithAcraWithName("Gesture detector issue ?")
return
} }
val gestureDetector = GestureDetector(activity, object : GestureDetector.SimpleOnGestureListener() {
override fun onSingleTapUp(e: MotionEvent): Boolean {
return performClick()
}
})
binding.webcontent.setOnTouchListener { _, event -> gestureDetector.onTouchEvent(event)}
binding.webcontent.settings.layoutAlgorithm = binding.webcontent.settings.layoutAlgorithm =
WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING
var baseUrl: String? = null var baseUrl: String? = null
try { try {
val itemUrl = URL(url) val itemUrl = URL(url)
baseUrl = itemUrl.protocol + "://" + itemUrl.host baseUrl = itemUrl.protocol + "://" + itemUrl.host
} catch (e: MalformedURLException) { } catch (e: MalformedURLException) {
e.sendSilentlyWithAcraWithName("htmlToWebview > item url") e.sendSilentlyWithAcraWithName("htmlToWebview > $url")
} }
val fontName = when (font) { val fontName: String =
getString(R.string.open_sans_font_id) -> "Open Sans" maybeIfContext {
getString(R.string.roboto_font_id) -> "Roboto" when (font) {
getString(R.string.source_code_pro_font_id) -> "Source Code Pro" it.getString(R.string.open_sans_font_id) -> "Open Sans"
it.getString(R.string.roboto_font_id) -> "Roboto"
it.getString(R.string.source_code_pro_font_id) -> "Source Code Pro"
else -> "" else -> ""
} }
}?.toString().orEmpty()
val fontLinkAndStyle = if (font.isNotEmpty()) { val fontLinkAndStyle =
"""<link href="https://fonts.googleapis.com/css?family=${fontName.replace(" ", "+")}" rel="stylesheet"> if (fontName.isNotEmpty()) {
"""<link href="https://fonts.googleapis.com/css?family=${
fontName.replace(
" ",
"+",
)
}" rel="stylesheet">
|<style> |<style>
| * { | * {
| font-family: '$fontName'; | font-family: '$fontName';
@ -412,7 +457,7 @@ class ArticleFragment : Fragment(), DIAware {
} else { } else {
"" ""
} }
try {
binding.webcontent.loadDataWithBaseURL( binding.webcontent.loadDataWithBaseURL(
baseUrl, baseUrl,
"""<html> """<html>
@ -426,10 +471,15 @@ class ArticleFragment : Fragment(), DIAware {
| max-width: 100%; | max-width: 100%;
| } | }
| a { | a {
| color: ${String.format("#%06X", 0xFFFFFF and resources.getColor(R.color.colorAccent))} !important; | color: ${
String.format(
"#%06X",
WHITE_COLOR_HEX and (maybeIfContext { it.resources.getColor(R.color.colorAccent) } as Int),
)
} !important;
| } | }
| *:not(a) { | *:not(a) {
| color: ${String.format("#%06X", 0xFFFFFF and colorOnSurface.data)}; | color: $colorOnSurfaceString;
| } | }
| * { | * {
| font-size: ${fontSize}px; | font-size: ${fontSize}px;
@ -437,11 +487,11 @@ class ArticleFragment : Fragment(), DIAware {
| word-break: break-word; | word-break: break-word;
| overflow:hidden; | overflow:hidden;
| line-height: 1.5em; | line-height: 1.5em;
| background-color: ${String.format("#%06X", 0xFFFFFF and colorSurface.data)}; | background-color: $colorSurfaceString;
| } | }
| body, html { | body, html {
| background-color: ${String.format("#%06X", 0xFFFFFF and colorSurface.data)} !important; | background-color: $colorSurfaceString !important;
| border-color: ${String.format("#%06X", 0xFFFFFF and colorSurface.data)} !important; | border-color: $colorSurfaceString !important;
| padding: 0 !important; | padding: 0 !important;
| margin: 0 !important; | margin: 0 !important;
| } | }
@ -451,41 +501,45 @@ class ArticleFragment : Fragment(), DIAware {
| pre, code { | pre, code {
| white-space: pre-wrap; | white-space: pre-wrap;
| width:100%; | width:100%;
| background-color: ${String.format("#%06X", 0xFFFFFF and colorSurface.data)}; | background-color: $colorSurfaceString;
| } | }
| </style> | </style>
| $fontLinkAndStyle | $fontLinkAndStyle
|</head> |</head>
|<body> |<body>
| $contentText | $contentText
|</body>""".trimMargin(), |</body>
""".trimMargin(),
"text/html", "text/html",
"utf-8", "utf-8",
null null,
) )
} catch (e: IllegalStateException) {
e.sendSilentlyWithAcraWithName("Context required is still null ?")
}
} }
fun scrollDown() { fun volumeButtonScrollDown() {
val height = binding.nestedScrollView.measuredHeight val height = binding.nestedScrollView.measuredHeight
binding.nestedScrollView.smoothScrollBy(0, height/2) binding.nestedScrollView.smoothScrollBy(0, height / 2)
} }
fun scrollUp() { fun volumeButtonScrollUp() {
val height = binding.nestedScrollView.measuredHeight val height = binding.nestedScrollView.measuredHeight
binding.nestedScrollView.smoothScrollBy(0, -height/2) binding.nestedScrollView.smoothScrollBy(0, -height / 2)
} }
private fun openInBrowserAfterFailing() { private fun openInBrowserAfterFailing() {
binding.progressBar.visibility = View.GONE binding.progressBar.visibility = View.GONE
requireActivity().openInBrowserAsNewTask(this@ArticleFragment.item) maybeIfContext {
it.openItemUrlInBrowserAsNewTask(this@ArticleFragment.item)
}
} }
companion object { companion object {
private const val ARG_ITEMS = "items" private const val ARG_ITEMS = "items"
fun newInstance( fun newInstance(item: SelfossModel.Item): ArticleFragment {
item: SelfossModel.Item
): ArticleFragment {
val fragment = ArticleFragment() val fragment = ArticleFragment()
val args = Bundle() val args = Bundle()
args.putParcelable(ARG_ITEMS, item.toParcelable()) args.putParcelable(ARG_ITEMS, item.toParcelable())
@ -495,10 +549,13 @@ class ArticleFragment : Fragment(), DIAware {
} }
fun performClick(): Boolean { fun performClick(): Boolean {
if (binding.webcontent.hitTestResult.type == WebView.HitTestResult.IMAGE_TYPE || if (allImages != null &&
binding.webcontent.hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) { (
binding.webcontent.hitTestResult.type == WebView.HitTestResult.IMAGE_TYPE ||
val position : Int = allImages.indexOf(binding.webcontent.hitTestResult.extra) binding.webcontent.hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE
)
) {
val position: Int = allImages.indexOf(binding.webcontent.hitTestResult.extra)
val intent = Intent(activity, ImageActivity::class.java) val intent = Intent(activity, ImageActivity::class.java)
intent.putExtra("allImages", allImages) intent.putExtra("allImages", allImages)
@ -508,6 +565,4 @@ class ArticleFragment : Fragment(), DIAware {
} }
return false return false
} }
} }

View File

@ -0,0 +1,213 @@
package bou.amine.apps.readerforselfossv2.android.fragments
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.graphics.drawable.GradientDrawable
import android.os.Build
import android.os.Bundle
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
import bou.amine.apps.readerforselfossv2.android.HomeActivity
import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.databinding.FilterFragmentBinding
import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcraWithName
import bou.amine.apps.readerforselfossv2.android.utils.glide.imageIntoViewTarget
import bou.amine.apps.readerforselfossv2.android.utils.maybeIfContext
import bou.amine.apps.readerforselfossv2.repository.Repository
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import bou.amine.apps.readerforselfossv2.utils.getColorHexCode
import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded
import bou.amine.apps.readerforselfossv2.utils.getIcon
import com.bumptech.glide.request.target.ViewTarget
import com.bumptech.glide.request.transition.Transition
import com.google.android.material.bottomsheet.BottomSheetDialogFragment
import com.google.android.material.chip.Chip
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.kodein.di.DI
import org.kodein.di.DIAware
import org.kodein.di.android.x.closestDI
import org.kodein.di.instance
private const val DRAWABLE_SIZE = 30
class FilterSheetFragment :
BottomSheetDialogFragment(),
DIAware {
private lateinit var binding: FilterFragmentBinding
override val di: DI by closestDI()
private val repository: Repository by instance()
private val appSettingsService: AppSettingsService by instance()
private var selectedChip: Chip? = null
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
binding =
FilterFragmentBinding.inflate(
inflater,
container,
false,
)
try {
CoroutineScope(Dispatchers.Main).launch {
handleTagChips()
handleSourceChips()
binding.progressBar2.visibility = GONE
binding.filterView.visibility = VISIBLE
}
} catch (e: IllegalStateException) {
dismiss()
e.sendSilentlyWithAcraWithName("FilterSheetFragment > onCreateView")
}
binding.floatingActionButton2.setOnClickListener {
(activity as HomeActivity).getElementsAccordingToTab()
(activity as HomeActivity).fetchOnEmptyList()
dismiss()
}
return binding.root
}
private suspend fun handleSourceChips() {
val sourceGroup = binding.sourcesGroup
repository.getSourcesDetailsOrStats().forEachIndexed { _, source ->
val c: Chip? =
maybeIfContext {
Chip(it)
} as Chip?
if (c == null) {
return
}
c.ellipsize = TextUtils.TruncateAt.END
maybeIfContext {
it.imageIntoViewTarget(
source.getIcon(repository.baseUrl),
object : ViewTarget<Chip?, Drawable?>(c) {
override fun onResourceReady(
resource: Drawable,
transition: Transition<in Drawable?>?,
) {
try {
c.chipIcon = resource
} catch (e: Exception) {
e.sendSilentlyWithAcraWithName("sources > onResourceReady")
}
}
},
appSettingsService,
)
}
c.text = source.title.getHtmlDecoded()
c.setOnCloseIconClickListener {
(it as Chip).isCloseIconVisible = false
selectedChip = null
repository.setSourceFilter(null)
}
c.setOnClickListener {
if (selectedChip != null) {
selectedChip!!.isCloseIconVisible = false
}
(it as Chip).isCloseIconVisible = true
selectedChip = it
repository.setSourceFilter(source)
repository.setTagFilter(null)
}
if (repository.sourceFilter.value?.equals(source) == true) {
c.isCloseIconVisible = true
selectedChip = c
}
c.isEnabled = source.error.isNullOrBlank()
if (!source.error.isNullOrBlank() && Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
c.tooltipText = source.error
}
sourceGroup.addView(c)
}
}
private suspend fun handleTagChips() {
val tagGroup = binding.tagsGroup
val tags = repository.getTags()
tags.forEachIndexed { _, tag ->
val c: Chip? = maybeIfContext { Chip(it) } as Chip?
if (c == null) {
return
}
c.ellipsize = TextUtils.TruncateAt.END
c.text = tag.tag
if (tag.color.isNotEmpty()) {
try {
val gd = GradientDrawable()
val gdColor =
try {
Color.parseColor(tag.getColorHexCode())
} catch (e: IllegalArgumentException) {
e.sendSilentlyWithAcraWithName("color issue " + tag.color + " / " + tag.getColorHexCode())
resources.getColor(R.color.colorPrimary)
}
gd.setColor(gdColor)
gd.shape = GradientDrawable.RECTANGLE
gd.setSize(DRAWABLE_SIZE, DRAWABLE_SIZE)
gd.cornerRadius = DRAWABLE_SIZE.toFloat()
c.chipIcon = gd
} catch (e: Exception) {
e.sendSilentlyWithAcraWithName("tags > GradientDrawable")
}
}
c.setOnCloseIconClickListener {
(it as Chip).isCloseIconVisible = false
selectedChip = null
repository.setTagFilter(null)
}
c.setOnClickListener {
if (selectedChip != null) {
selectedChip!!.isCloseIconVisible = false
}
(it as Chip).isCloseIconVisible = true
selectedChip = it
repository.setTagFilter(tag)
repository.setSourceFilter(null)
}
if (repository.tagFilter.value?.equals(tag) == true) {
c.isCloseIconVisible = true
selectedChip = c
}
tagGroup.addView(c)
}
}
companion object {
const val TAG = "FilterModalBottomSheet"
}
}

View File

@ -6,16 +6,21 @@ import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import bou.amine.apps.readerforselfossv2.android.databinding.FragmentImageBinding import bou.amine.apps.readerforselfossv2.android.databinding.FragmentImageBinding
import com.bumptech.glide.Glide import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapWithCache
import com.bumptech.glide.load.engine.DiskCacheStrategy import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import com.bumptech.glide.request.RequestOptions import org.kodein.di.DI
import org.kodein.di.DIAware
import org.kodein.di.android.x.closestDI
import org.kodein.di.instance
class ImageFragment : Fragment() { class ImageFragment :
Fragment(),
private lateinit var imageUrl : String DIAware {
private val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL) override val di: DI by closestDI()
private val appSettingsService: AppSettingsService by instance()
private lateinit var imageUrl: String
private var _binding: FragmentImageBinding? = null private var _binding: FragmentImageBinding? = null
private val binding get() = _binding val binding get() = _binding
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
@ -23,16 +28,16 @@ class ImageFragment : Fragment() {
imageUrl = requireArguments().getString("imageUrl")!! imageUrl = requireArguments().getString("imageUrl")!!
} }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
_binding = FragmentImageBinding.inflate(inflater, container, false) _binding = FragmentImageBinding.inflate(inflater, container, false)
val view = binding?.root val view = binding?.root
binding!!.photoView.visibility = View.VISIBLE binding!!.photoView.visibility = View.VISIBLE
Glide.with(activity) requireActivity().bitmapWithCache(imageUrl, binding!!.photoView, appSettingsService)
.asBitmap()
.apply(glideOptions)
.load(imageUrl)
.into(binding!!.photoView)
return view return view
} }
@ -45,9 +50,7 @@ class ImageFragment : Fragment() {
companion object { companion object {
private const val ARG_IMAGE = "imageUrl" private const val ARG_IMAGE = "imageUrl"
fun newInstance( fun newInstance(imageUrl: String): ImageFragment {
imageUrl : String
): ImageFragment {
val fragment = ImageFragment() val fragment = ImageFragment()
val args = Bundle() val args = Bundle()
args.putString(ARG_IMAGE, imageUrl) args.putString(ARG_IMAGE, imageUrl)

View File

@ -2,29 +2,25 @@ package bou.amine.apps.readerforselfossv2.android.model
import android.content.Context import android.content.Context
import android.webkit.URLUtil import android.webkit.URLUtil
import bou.amine.apps.readerforselfossv2.android.sendSilentlyWithAcraWithName import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcraWithName
import bou.amine.apps.readerforselfossv2.android.utils.glide.preloadImage
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import bou.amine.apps.readerforselfossv2.utils.getImages import bou.amine.apps.readerforselfossv2.utils.getImages
import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.request.RequestOptions
import org.acra.ktx.sendSilentlyWithAcra
fun SelfossModel.Item.preloadImages(context: Context) : Boolean { fun SelfossModel.Item.preloadImages(
context: Context,
appSettingsService: AppSettingsService,
): Boolean {
val imageUrls = this.getImages() val imageUrls = this.getImages()
val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL).timeout(10000)
try { try {
for (url in imageUrls) { for (url in imageUrls) {
if ( URLUtil.isValidUrl(url)) { if (URLUtil.isValidUrl(url)) {
Glide.with(context).asBitmap() context.preloadImage(url, appSettingsService)
.apply(glideOptions)
.load(url).submit()
} }
} }
} catch (e : Error) { } catch (e: Error) {
e.sendSilentlyWithAcraWithName("preloadImages") e.sendSilentlyWithAcraWithName("preloadImages")
return false return false
} }

View File

@ -4,7 +4,7 @@ import android.os.Parcel
import android.os.Parcelable import android.os.Parcelable
import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.model.SelfossModel
fun SelfossModel.Item.toParcelable() : ParecelableItem = fun SelfossModel.Item.toParcelable(): ParecelableItem =
ParecelableItem( ParecelableItem(
this.id, this.id,
this.datetime, this.datetime,
@ -16,9 +16,11 @@ fun SelfossModel.Item.toParcelable() : ParecelableItem =
this.icon, this.icon,
this.link, this.link,
this.sourcetitle, this.sourcetitle,
this.tags.joinToString(",") this.tags.joinToString(","),
this.author,
) )
fun ParecelableItem.toModel() : SelfossModel.Item =
fun ParecelableItem.toModel(): SelfossModel.Item =
SelfossModel.Item( SelfossModel.Item(
this.id, this.id,
this.datetime, this.datetime,
@ -30,8 +32,10 @@ fun ParecelableItem.toModel() : SelfossModel.Item =
this.icon, this.icon,
this.link, this.link,
this.sourcetitle, this.sourcetitle,
this.tags.split(",") this.tags.split(","),
this.author,
) )
data class ParecelableItem( data class ParecelableItem(
val id: Int, val id: Int,
val datetime: String, val datetime: String,
@ -43,13 +47,15 @@ data class ParecelableItem(
val icon: String?, val icon: String?,
val link: String, val link: String,
val sourcetitle: String, val sourcetitle: String,
val tags: String val tags: String,
val author: String?,
) : Parcelable { ) : Parcelable {
companion object { companion object {
@JvmField @JvmField
val CREATOR: Parcelable.Creator<ParecelableItem> = object : Parcelable.Creator<ParecelableItem> { val CREATOR: Parcelable.Creator<ParecelableItem> =
object : Parcelable.Creator<ParecelableItem> {
override fun createFromParcel(source: Parcel): ParecelableItem = ParecelableItem(source) override fun createFromParcel(source: Parcel): ParecelableItem = ParecelableItem(source)
override fun newArray(size: Int): Array<ParecelableItem?> = arrayOfNulls(size) override fun newArray(size: Int): Array<ParecelableItem?> = arrayOfNulls(size)
} }
} }
@ -65,12 +71,16 @@ data class ParecelableItem(
icon = source.readString(), icon = source.readString(),
link = source.readString().orEmpty(), link = source.readString().orEmpty(),
sourcetitle = source.readString().orEmpty(), sourcetitle = source.readString().orEmpty(),
tags = source.readString().orEmpty() tags = source.readString().orEmpty(),
author = source.readString().orEmpty(),
) )
override fun describeContents() = 0 override fun describeContents() = 0
override fun writeToParcel(dest: Parcel, flags: Int) { override fun writeToParcel(
dest: Parcel,
flags: Int,
) {
dest.writeInt(id) dest.writeInt(id)
dest.writeString(datetime) dest.writeString(datetime)
dest.writeString(title) dest.writeString(title)
@ -82,5 +92,6 @@ data class ParecelableItem(
dest.writeString(link) dest.writeString(link)
dest.writeString(sourcetitle) dest.writeString(sourcetitle)
dest.writeString(tags) dest.writeString(tags)
dest.writeString(author)
} }
} }

View File

@ -1,7 +1,5 @@
package bou.amine.apps.readerforselfossv2.android.settings package bou.amine.apps.readerforselfossv2.android.settings
import android.content.Intent
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.text.Editable import android.text.Editable
import android.text.InputFilter import android.text.InputFilter
@ -16,30 +14,32 @@ import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySettingsBinding import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySettingsBinding
import bou.amine.apps.readerforselfossv2.android.sendSilentlyWithAcraWithName import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcraWithName
import bou.amine.apps.readerforselfossv2.android.utils.openUrlInBrowser
import bou.amine.apps.readerforselfossv2.service.AppSettingsService import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import org.acra.ktx.sendSilentlyWithAcra import bou.amine.apps.readerforselfossv2.service.AppSettingsService.Companion.API_ITEMS_NUMBER
import org.acra.ktx.sendWithAcra import bou.amine.apps.readerforselfossv2.service.AppSettingsService.Companion.CURRENT_THEME
import bou.amine.apps.readerforselfossv2.service.AppSettingsService.Companion.READER_FONT_SIZE
import com.mikepenz.aboutlibraries.LibsBuilder
import org.kodein.di.DIAware import org.kodein.di.DIAware
import org.kodein.di.android.closestDI import org.kodein.di.android.closestDI
import org.kodein.di.instance
import org.matomo.sdk.Tracker
import org.matomo.sdk.extra.TrackHelper
private const val TITLE_TAG = "settingsActivityTitle" private const val TITLE_TAG = "settingsActivityTitle"
class SettingsActivity : AppCompatActivity(), const val MAX_ITEMS_NUMBER = 200
PreferenceFragmentCompat.OnPreferenceStartFragmentCallback, DIAware {
override val di by closestDI()
private val tracker : Tracker by instance() private const val MIN_ITEMS_NUMBER = 1
class SettingsActivity :
AppCompatActivity(),
PreferenceFragmentCompat.OnPreferenceStartFragmentCallback,
DIAware {
override val di by closestDI()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
val binding = ActivitySettingsBinding.inflate(layoutInflater) val binding = ActivitySettingsBinding.inflate(layoutInflater)
TrackHelper.track().screen("/settings").with(tracker)
setContentView(binding.root) setContentView(binding.root)
if (savedInstanceState == null) { if (savedInstanceState == null) {
supportFragmentManager supportFragmentManager
@ -68,31 +68,33 @@ class SettingsActivity : AppCompatActivity(),
outState.putCharSequence(TITLE_TAG, title) outState.putCharSequence(TITLE_TAG, title)
} }
override fun onSupportNavigateUp(): Boolean { override fun onSupportNavigateUp(): Boolean =
return if (supportFragmentManager.popBackStackImmediate()) { if (supportFragmentManager.popBackStackImmediate()) {
supportActionBar?.title = getText(R.string.title_activity_settings) supportActionBar?.title = getText(R.string.title_activity_settings)
false false
} else { } else {
super.onBackPressed() super.onBackPressed()
true true
} }
}
override fun onPreferenceStartFragment( override fun onPreferenceStartFragment(
caller: PreferenceFragmentCompat, caller: PreferenceFragmentCompat,
pref: Preference pref: Preference,
): Boolean { ): Boolean {
// Instantiate the new Fragment // Instantiate the new Fragment
val args = pref.extras val args = pref.extras
val fragment = supportFragmentManager.fragmentFactory.instantiate( val fragment =
supportFragmentManager.fragmentFactory
.instantiate(
classLoader, classLoader,
pref.fragment pref.fragment.toString(),
).apply { ).apply {
arguments = args arguments = args
setTargetFragment(caller, 0) setTargetFragment(caller, 0)
} }
// Replace the existing Fragment with the new Fragment // Replace the existing Fragment with the new Fragment
supportFragmentManager.beginTransaction() supportFragmentManager
.beginTransaction()
.replace(R.id.settings, fragment) .replace(R.id.settings, fragment)
.addToBackStack(null) .addToBackStack(null)
.commit() .commit()
@ -102,49 +104,95 @@ class SettingsActivity : AppCompatActivity(),
} }
class MainPreferenceFragment : PreferenceFragmentCompat() { class MainPreferenceFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(
savedInstanceState: Bundle?,
rootKey: String?,
) {
setPreferencesFromResource(R.xml.pref_main, rootKey) setPreferencesFromResource(R.xml.pref_main, rootKey)
preferenceManager.findPreference<Preference>("currentMode")?.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue -> preferenceManager.findPreference<Preference>(CURRENT_THEME)?.onPreferenceChangeListener =
AppCompatDelegate.setDefaultNightMode(newValue.toString().toInt()) // ListPreference Only takes string-arrays ¯\_(ツ)_/¯ Preference.OnPreferenceChangeListener { _, newValue ->
AppCompatDelegate.setDefaultNightMode(
newValue.toString().toInt(),
) // ListPreference Only takes string-arrays ¯\_(ツ)_/¯
true
}
preferenceManager.findPreference<Preference>("action_about")?.onPreferenceClickListener =
Preference.OnPreferenceClickListener { _ ->
context?.let {
LibsBuilder()
.withAboutIconShown(true)
.withAboutVersionShown(true)
.start(it)
}
true true
} }
} }
} }
class GeneralPreferenceFragment : PreferenceFragmentCompat() { class GeneralPreferenceFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(
savedInstanceState: Bundle?,
rootKey: String?,
) {
setPreferencesFromResource(R.xml.pref_general, rootKey) setPreferencesFromResource(R.xml.pref_general, rootKey)
val editTextPreference = preferenceManager.findPreference<EditTextPreference>("prefer_api_items_number") val editTextPreference =
preferenceManager.findPreference<EditTextPreference>(API_ITEMS_NUMBER)
editTextPreference?.setOnBindEditTextListener { editText -> editTextPreference?.setOnBindEditTextListener { editText ->
editText.inputType = InputType.TYPE_CLASS_NUMBER editText.inputType = InputType.TYPE_CLASS_NUMBER
editText.filters = arrayOf( editText.filters =
arrayOf(
InputFilter { source, _, _, dest, _, _ -> InputFilter { source, _, _, dest, _, _ ->
try { try {
val input: Int = (dest.toString() + source.toString()).toInt() val input: Int = (dest.toString() + source.toString()).toInt()
if (input in 1..200) return@InputFilter null if (input in MIN_ITEMS_NUMBER..MAX_ITEMS_NUMBER) return@InputFilter null
} catch (nfe: NumberFormatException) { } catch (nfe: NumberFormatException) {
nfe.sendSilentlyWithAcraWithName("GeneralPreferenceFragment") Toast
Toast.makeText(activity, R.string.items_number_should_be_number, Toast.LENGTH_LONG).show() .makeText(
activity,
R.string.items_number_should_be_number,
Toast.LENGTH_LONG,
).show()
} }
"" ""
} },
) )
} }
} }
} }
class ArticleViewerPreferenceFragment : PreferenceFragmentCompat() { class ArticleViewerPreferenceFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(
savedInstanceState: Bundle?,
rootKey: String?,
) {
setPreferencesFromResource(R.xml.pref_viewer, rootKey) setPreferencesFromResource(R.xml.pref_viewer, rootKey)
val fontSize = preferenceManager.findPreference<EditTextPreference>("reader_font_size") val fontSize = preferenceManager.findPreference<EditTextPreference>(READER_FONT_SIZE)
fontSize?.setOnBindEditTextListener { editText -> fontSize?.setOnBindEditTextListener { editText ->
editText.inputType = InputType.TYPE_CLASS_NUMBER editText.inputType = InputType.TYPE_CLASS_NUMBER
editText.addTextChangedListener { object : TextWatcher { editText.addTextChangedListener {
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} object : TextWatcher {
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {} override fun beforeTextChanged(
charSequence: CharSequence,
i: Int,
i1: Int,
i2: Int,
) {
// We do nothing
}
override fun onTextChanged(
charSequence: CharSequence,
i: Int,
i1: Int,
i2: Int,
) {
// We do nothing
}
override fun afterTextChanged(editable: Editable) { override fun afterTextChanged(editable: Editable) {
try { try {
editText.textSize = editable.toString().toInt().toFloat() editText.textSize = editable.toString().toInt().toFloat()
@ -152,8 +200,10 @@ class SettingsActivity : AppCompatActivity(),
e.sendSilentlyWithAcraWithName("ArticleViewerPreferenceFragment > afterTextChanged") e.sendSilentlyWithAcraWithName("ArticleViewerPreferenceFragment > afterTextChanged")
} }
} }
} } }
editText.filters = arrayOf( }
editText.filters =
arrayOf(
InputFilter { source, _, _, dest, _, _ -> InputFilter { source, _, _, dest, _, _ ->
try { try {
val input = (dest.toString() + source.toString()).toInt() val input = (dest.toString() + source.toString()).toInt()
@ -162,57 +212,57 @@ class SettingsActivity : AppCompatActivity(),
nfe.sendSilentlyWithAcraWithName("ArticleViewerPreferenceFragment > filters") nfe.sendSilentlyWithAcraWithName("ArticleViewerPreferenceFragment > filters")
} }
"" ""
} },
) )
} }
} }
} }
class OfflinePreferenceFragment : PreferenceFragmentCompat() { class OfflinePreferenceFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(
savedInstanceState: Bundle?,
rootKey: String?,
) {
setPreferencesFromResource(R.xml.pref_offline, rootKey) setPreferencesFromResource(R.xml.pref_offline, rootKey)
} }
} }
class ThemePreferenceFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.pref_theme, rootKey)
preferenceManager.findPreference<Preference>("currentMode")?.onPreferenceChangeListener = Preference.OnPreferenceChangeListener { _, newValue ->
AppCompatDelegate.setDefaultNightMode(newValue.toString().toInt()) // ListPreference Only takes string-arrays ¯\_(ツ)_/¯
true
}
}
}
class LinksPreferenceFragment : PreferenceFragmentCompat() { class LinksPreferenceFragment : PreferenceFragmentCompat() {
private fun openUrl(uri: Uri?) { private fun openUrl(url: String) {
val browserIntent = Intent(Intent.ACTION_VIEW, uri) context?.openUrlInBrowser(url)
startActivity(browserIntent)
} }
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(
savedInstanceState: Bundle?,
rootKey: String?,
) {
setPreferencesFromResource(R.xml.pref_links, rootKey) setPreferencesFromResource(R.xml.pref_links, rootKey)
preferenceManager.findPreference<Preference>("trackerLink")?.onPreferenceClickListener = Preference.OnPreferenceClickListener { preferenceManager.findPreference<Preference>("trackerLink")?.onPreferenceClickListener =
openUrl(Uri.parse(AppSettingsService.trackerUrl)) Preference.OnPreferenceClickListener {
openUrl(AppSettingsService.BUG_URL)
true true
} }
preferenceManager.findPreference<Preference>("sourceLink")?.onPreferenceClickListener = Preference.OnPreferenceClickListener { preferenceManager.findPreference<Preference>("sourceLink")?.onPreferenceClickListener =
openUrl(Uri.parse(AppSettingsService.sourceUrl)) Preference.OnPreferenceClickListener {
openUrl(AppSettingsService.SOURCE_URL)
false false
} }
preferenceManager.findPreference<Preference>("translation")?.onPreferenceClickListener = Preference.OnPreferenceClickListener { preferenceManager.findPreference<Preference>("translation")?.onPreferenceClickListener =
openUrl(Uri.parse(AppSettingsService.translationUrl)) Preference.OnPreferenceClickListener {
openUrl(AppSettingsService.TRANSLATION_URL)
false false
} }
} }
} }
class ExperimentalPreferenceFragment : PreferenceFragmentCompat() { class ExperimentalPreferenceFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(
savedInstanceState: Bundle?,
rootKey: String?,
) {
setPreferencesFromResource(R.xml.pref_experimental, rootKey) setPreferencesFromResource(R.xml.pref_experimental, rootKey)
} }
} }

View File

@ -0,0 +1,20 @@
package bou.amine.apps.readerforselfossv2.android.testing
import androidx.test.espresso.idling.CountingIdlingResource
object CountingIdlingResourceSingleton {
private const val RESOURCE = "GLOBAL"
@JvmField
val countingIdlingResource = CountingIdlingResource(RESOURCE)
fun increment() {
countingIdlingResource.increment()
}
fun decrement() {
if (!countingIdlingResource.isIdleNow) {
countingIdlingResource.decrement()
}
}
}

View File

@ -0,0 +1,18 @@
package bou.amine.apps.readerforselfossv2.android.testing
import android.os.Build
class TestingHelper {
fun isUnitTest(): Boolean {
var device = Build.DEVICE
var product = Build.PRODUCT
if (device == null) {
device = ""
}
if (product == null) {
product = ""
}
return device == "robolectric" && product == "robolectric"
}
}

View File

@ -2,10 +2,18 @@ package bou.amine.apps.readerforselfossv2.android.utils
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.util.TypedValue
import androidx.annotation.AttrRes
import androidx.annotation.ColorInt
import androidx.fragment.app.Fragment
import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.utils.acra.sendSilentlyWithAcraWithName
import bou.amine.apps.readerforselfossv2.utils.toStringUriWithHttp import bou.amine.apps.readerforselfossv2.utils.toStringUriWithHttp
fun Context.shareLink(itemUrl: String, itemTitle: String) { fun Context.shareLink(
itemUrl: String,
itemTitle: String,
) {
val sendIntent = Intent() val sendIntent = Intent()
sendIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK sendIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
sendIntent.action = Intent.ACTION_SEND sendIntent.action = Intent.ACTION_SEND
@ -13,9 +21,39 @@ fun Context.shareLink(itemUrl: String, itemTitle: String) {
sendIntent.putExtra(Intent.EXTRA_SUBJECT, itemTitle) sendIntent.putExtra(Intent.EXTRA_SUBJECT, itemTitle)
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),
) )
} }
@ColorInt
fun Fragment.getColorFromAttr(
@AttrRes attrColor: Int,
resolveRefs: Boolean = true,
): Int {
val typedValue = TypedValue()
maybeIfContextWithLog { this.requireContext().theme.resolveAttribute(attrColor, typedValue, resolveRefs) }
return typedValue.data
}
@Suppress("detekt:SwallowedException")
fun Fragment.maybeIfContext(fn: (Context) -> Any): Any? {
try {
return fn(this.requireContext())
} catch (e: Exception) {
// Do nothing
return null
}
}
fun Fragment.maybeIfContextWithLog(fn: (Context) -> Any): Any? {
try {
return fn(this.requireContext())
} catch (e: Exception) {
e.sendSilentlyWithAcraWithName("Fragment context issue...")
return null
}
}

View File

@ -0,0 +1,63 @@
package bou.amine.apps.readerforselfossv2.android.utils
import android.content.Context
import android.graphics.drawable.GradientDrawable
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.widget.RelativeLayout
import android.widget.TextView
import bou.amine.apps.readerforselfossv2.android.R
import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString
import com.google.android.material.imageview.ShapeableImageView
import kotlin.math.abs
class CircleImageView
@JvmOverloads
constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
) : RelativeLayout(context, attrs, defStyleAttr) {
val view: View
val imageView: ShapeableImageView
val textView: TextView
private val colorScheme =
listOf(
-0x1a8c8d,
-0xf9d6e,
-0x459738,
-0x6a8a33,
-0x867935,
-0x9b4a0a,
-0xb03c09,
-0xb22f1f,
-0xb24954,
-0x7e387c,
-0x512a7f,
-0x759b,
-0x2b1ea9,
-0x2ab1,
-0x48b3,
-0x5e7781,
-0x6f5b52,
)
init {
view = LayoutInflater.from(context).inflate(R.layout.circle_image_view, this, true)
imageView = view.findViewById(R.id.circleImage)
textView = view.findViewById(R.id.circleText)
}
fun setBackgroundAndText(text: String) {
val circleDrawable = GradientDrawable()
val color = colorFromIdentifier(text)
circleDrawable.setColor(color)
imageView.setImageDrawable(circleDrawable)
textView.text = text.toTextDrawableString()
}
private fun colorFromIdentifier(key: String): Int = colorScheme[abs(key.hashCode()) % colorScheme.size]
}

View File

@ -1,6 +1,7 @@
package bou.amine.apps.readerforselfossv2.android.utils package bou.amine.apps.readerforselfossv2.android.utils
import android.app.Activity import android.app.Activity
import android.content.ActivityNotFoundException
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
@ -18,36 +19,30 @@ import bou.amine.apps.readerforselfossv2.utils.toStringUriWithHttp
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
fun Context.openItemUrl( fun Context.openItemUrl(
allItems: ArrayList<SelfossModel.Item>,
currentItem: Int, currentItem: Int,
linkDecoded: String, linkDecoded: String,
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 (articleViewer) { if (articleViewer) {
ReaderActivity.allItems = allItems
val intent = Intent(this, ReaderActivity::class.java) val intent = Intent(this, ReaderActivity::class.java)
intent.putExtra("currentItem", currentItem) intent.putExtra("currentItem", currentItem)
app.startActivity(intent) app.startActivity(intent)
} else { } else {
val intent = Intent(Intent.ACTION_VIEW) this.openUrlInBrowserAsNewTask(linkDecoded)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.data = Uri.parse(linkDecoded.toStringUriWithHttp())
startActivity(intent)
} }
} }
} }
fun String.isUrlValid(): Boolean = fun String.isUrlValid(): Boolean = this.toHttpUrlOrNull() != null && Patterns.WEB_URL.matcher(this).matches()
this.toHttpUrlOrNull() != null && Patterns.WEB_URL.matcher(this).matches()
fun String.isBaseUrlInvalid(): Boolean { fun String.isBaseUrlInvalid(): Boolean {
val baseUrl = this.toHttpUrlOrNull() val baseUrl = this.toHttpUrlOrNull()
@ -60,15 +55,37 @@ fun String.isBaseUrlInvalid(): Boolean {
return !(Patterns.WEB_URL.matcher(this).matches() && existsAndEndsWithSlash) return !(Patterns.WEB_URL.matcher(this).matches() && existsAndEndsWithSlash)
} }
fun Context.openInBrowserAsNewTask(i: SelfossModel.Item) { fun Context.openItemUrlInBrowserAsNewTask(i: SelfossModel.Item) {
this.openUrlInBrowserAsNewTask(i.getLinkDecoded().toStringUriWithHttp())
}
fun Context.openUrlInBrowserAsNewTask(url: String) {
val intent = Intent(Intent.ACTION_VIEW) val intent = Intent(Intent.ACTION_VIEW)
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
intent.data = Uri.parse(i.getLinkDecoded().toStringUriWithHttp()) intent.data = Uri.parse(url)
startActivity(intent) this.mayBeStartActivity(intent)
}
fun Context.openUrlInBrowser(url: String) {
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse(url)
this.mayBeStartActivity(intent)
}
@Suppress("detekt:SwallowedException")
fun Context.mayBeStartActivity(intent: Intent) {
try {
this.startActivity(intent)
} catch (e: ActivityNotFoundException) {
Toast.makeText(this, getString(R.string.no_browser), Toast.LENGTH_SHORT).show()
}
} }
class LinkOnTouchListener : View.OnTouchListener { class LinkOnTouchListener : View.OnTouchListener {
override fun onTouch(v: View?, event: MotionEvent?): Boolean { override fun onTouch(
v: View?,
event: MotionEvent?,
): Boolean {
var ret = false var ret = false
val widget: TextView = v as TextView val widget: TextView = v as TextView
val text: CharSequence = widget.text val text: CharSequence = widget.text

View File

@ -1,4 +1,4 @@
package bou.amine.apps.readerforselfossv2.android package bou.amine.apps.readerforselfossv2.android.utils.acra
import org.acra.ACRA import org.acra.ACRA
import org.acra.ktx.sendSilentlyWithAcra import org.acra.ktx.sendSilentlyWithAcra

View File

@ -0,0 +1,26 @@
package bou.amine.apps.readerforselfossv2.android.utils.acra
import android.content.Context
import android.os.DeadSystemException
import com.google.auto.service.AutoService
import org.acra.builder.ReportBuilder
import org.acra.config.CoreConfiguration
import org.acra.config.ReportingAdministrator
import org.acra.data.CrashReportData
@AutoService(ReportingAdministrator::class)
class AcraReportingAdministrator : ReportingAdministrator {
override fun shouldStartCollecting(
context: Context,
config: CoreConfiguration,
reportBuilder: ReportBuilder,
): Boolean =
reportBuilder.exception !is DeadSystemException &&
(reportBuilder.exception != null && reportBuilder.exception!!::class.simpleName != "CannotDeliverBroadcastException")
override fun shouldSendReport(
context: Context,
config: CoreConfiguration,
crashReportData: CrashReportData,
): Boolean = crashReportData.get("BRAND") != "redroid"
}

View File

@ -1,6 +1,13 @@
package bou.amine.apps.readerforselfossv2.android.utils.bottombar package bou.amine.apps.readerforselfossv2.android.utils.bottombar
import android.content.Context
import android.graphics.drawable.Drawable
import androidx.annotation.IdRes
import androidx.annotation.StringRes
import bou.amine.apps.readerforselfossv2.android.R
import com.ashokvarma.bottomnavigation.TextBadgeItem import com.ashokvarma.bottomnavigation.TextBadgeItem
import com.leinardi.android.speeddial.SpeedDialActionItem
import com.leinardi.android.speeddial.SpeedDialView
fun TextBadgeItem.removeBadge(): TextBadgeItem { fun TextBadgeItem.removeBadge(): TextBadgeItem {
this.setText("") this.setText("")
@ -8,5 +15,26 @@ fun TextBadgeItem.removeBadge(): TextBadgeItem {
return this return this
} }
fun TextBadgeItem.maybeShow(): TextBadgeItem = fun TextBadgeItem.maybeShow(): TextBadgeItem = if (this.isHidden) this.show() else this
if (this.isHidden) this.show() else this
@Suppress("detekt:LongParameterList")
fun SpeedDialView.addHomeMadeActionItem(
@IdRes actionId: Int,
actionIcon: Drawable,
@StringRes labelId: Int,
colorOnSurface: Int,
colorSurface: Int,
context: Context,
) {
this.addActionItem(
SpeedDialActionItem
.Builder(actionId, actionIcon)
.setFabBackgroundColor(context.resources.getColor(R.color.colorAccent))
.setFabImageTintColor(colorOnSurface)
.setLabel(context.getString(labelId))
.setLabelClickable(false)
.setLabelBackgroundColor(colorOnSurface)
.setLabelColor(colorSurface)
.create(),
)
}

View File

@ -1,14 +0,0 @@
/* From https://github.com/mikepenz/MaterialDrawer/blob/develop/app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/CustomBaseViewHolder.java */
package bou.amine.apps.readerforselfossv2.android.utils.drawer
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import bou.amine.apps.readerforselfossv2.android.R
open class CustomBaseViewHolder(var view: View) : RecyclerView.ViewHolder(view) {
var icon: ImageView = view.findViewById(R.id.material_drawer_icon)
var name: TextView = view.findViewById(R.id.material_drawer_name)
var description: TextView = view.findViewById(R.id.material_drawer_description)
}

View File

@ -2,41 +2,135 @@ package bou.amine.apps.readerforselfossv2.android.utils.glide
import android.content.Context import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.webkit.WebView
import android.widget.ImageView import android.widget.ImageView
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory import bou.amine.apps.readerforselfossv2.android.utils.CircleImageView
import bou.amine.apps.readerforselfossv2.service.AppSettingsService
import bou.amine.apps.readerforselfossv2.utils.isEmptyOrNullOrNullString
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.load.model.LazyHeaders
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.ViewTarget
import com.google.android.material.chip.Chip
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.io.InputStream import java.io.InputStream
import kotlin.io.encoding.Base64
import kotlin.io.encoding.ExperimentalEncodingApi
fun Context.bitmapCenterCrop(url: String, iv: ImageView) = private const val PRELOAD_IMAGE_TIMEOUT = 10000
Glide.with(this)
@Suppress("detekt:ReturnCount")
@OptIn(ExperimentalEncodingApi::class)
fun String.toGlideUrl(appSettingsService: AppSettingsService): Any { // GlideUrl Or String
if (this.isEmptyOrNullOrNullString()) {
return ""
}
if (appSettingsService.getBasicUserName().isNotEmpty()) {
val authString = "${appSettingsService.getBasicUserName()}:${appSettingsService.getBasicPassword()}"
val authBuf = Base64.encode(authString.toByteArray(Charsets.UTF_8))
return GlideUrl(
this,
LazyHeaders
.Builder()
.addHeader("Authorization", "Basic $authBuf")
.build(),
)
} else {
return GlideUrl(
this,
)
}
}
fun WebView.getGlideImageForResource(
url: String,
appSettingsService: AppSettingsService,
) = Glide
.with(this)
.asBitmap() .asBitmap()
.load(url) .apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL))
.load(url.toGlideUrl(appSettingsService))
.submit()
.get()
fun Context.preloadImage(
url: String,
appSettingsService: AppSettingsService,
) = Glide
.with(this)
.asBitmap()
.apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL).timeout(PRELOAD_IMAGE_TIMEOUT))
.load(url.toGlideUrl(appSettingsService))
.submit()
fun Context.imageIntoViewTarget(
url: String,
target: ViewTarget<Chip?, Drawable?>,
appSettingsService: AppSettingsService,
) = Glide
.with(this)
.load(url.toGlideUrl(appSettingsService))
.into(target)
fun Context.bitmapWithCache(
url: String,
iv: ImageView,
appSettingsService: AppSettingsService,
) = Glide
.with(this)
.asBitmap()
.apply(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL))
.load(url.toGlideUrl(appSettingsService))
.into(iv)
fun Context.bitmapCenterCrop(
url: String,
iv: ImageView,
appSettingsService: AppSettingsService,
) = Glide
.with(this)
.asBitmap()
.load(url.toGlideUrl(appSettingsService))
.apply(RequestOptions.centerCropTransform()) .apply(RequestOptions.centerCropTransform())
.into(iv) .into(iv)
fun Context.circularBitmapDrawable(url: String, iv: ImageView) = fun Context.bitmapFitCenter(
Glide.with(this) url: String,
iv: ImageView,
appSettingsService: AppSettingsService,
) = Glide
.with(this)
.asBitmap() .asBitmap()
.load(url) .load(url.toGlideUrl(appSettingsService))
.apply(RequestOptions.centerCropTransform()) .apply(RequestOptions.fitCenterTransform())
.into(object : BitmapImageViewTarget(iv) { .into(iv)
override fun setResource(resource: Bitmap?) {
val circularBitmapDrawable = RoundedBitmapDrawableFactory.create(
resources,
resource
)
circularBitmapDrawable.isCircular = true
iv.setImageDrawable(circularBitmapDrawable)
}
})
fun getBitmapInputStream(bitmap:Bitmap,compressFormat: Bitmap.CompressFormat): InputStream { fun Context.circularDrawable(
url: String,
view: CircleImageView,
appSettingsService: AppSettingsService,
) {
view.textView.text = ""
Glide
.with(this)
.load(url.toGlideUrl(appSettingsService))
.into(view.imageView)
}
private const val BITMAP_INPUT_STREAM_COMPRESSION_QUALITY = 80
fun getBitmapInputStream(
bitmap: Bitmap,
compressFormat: Bitmap.CompressFormat,
): InputStream {
val byteArrayOutputStream = ByteArrayOutputStream() val byteArrayOutputStream = ByteArrayOutputStream()
bitmap.compress(compressFormat, 80, byteArrayOutputStream) bitmap.compress(compressFormat, BITMAP_INPUT_STREAM_COMPRESSION_QUALITY, byteArrayOutputStream)
val bitmapData: ByteArray = byteArrayOutputStream.toByteArray() val bitmapData: ByteArray = byteArrayOutputStream.toByteArray()
return ByteArrayInputStream(bitmapData) return ByteArrayInputStream(bitmapData)
} }

View File

@ -3,7 +3,6 @@ package bou.amine.apps.readerforselfossv2.android.utils.network
import android.content.Context import android.content.Context
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.net.NetworkCapabilities import android.net.NetworkCapabilities
import android.os.Build
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
lateinit var s: Snackbar lateinit var s: Snackbar
@ -11,9 +10,7 @@ lateinit var s: Snackbar
fun isNetworkAccessible(context: Context): Boolean { fun isNetworkAccessible(context: Context): Boolean {
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { val networkCapabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) ?: return false
val network = connectivityManager.activeNetwork ?: return false
val networkCapabilities = connectivityManager.getNetworkCapabilities(network) ?: return false
return when { return when {
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> true networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> true
@ -22,8 +19,4 @@ fun isNetworkAccessible(context: Context): Boolean {
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
else -> false else -> false
} }
} else {
val network = connectivityManager.activeNetworkInfo ?: return false
return network.isConnectedOrConnecting
}
} }

View File

@ -7,7 +7,9 @@ import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class AppViewModel(private val repository: Repository) : ViewModel() { class AppViewModel(
private val repository: Repository,
) : ViewModel() {
private val _networkAvailableProvider = MutableSharedFlow<Boolean>() private val _networkAvailableProvider = MutableSharedFlow<Boolean>()
val networkAvailableProvider = _networkAvailableProvider.asSharedFlow() val networkAvailableProvider = _networkAvailableProvider.asSharedFlow()
private var wasConnected = true private var wasConnected = true
@ -19,7 +21,7 @@ class AppViewModel(private val repository: Repository) : ViewModel() {
if (isConnected && !wasConnected && repository.connectionMonitored) { if (isConnected && !wasConnected && repository.connectionMonitored) {
_networkAvailableProvider.emit(true) _networkAvailableProvider.emit(true)
wasConnected = true wasConnected = true
} else if (!isConnected && wasConnected && repository.connectionMonitored){ } else if (!isConnected && wasConnected && repository.connectionMonitored) {
_networkAvailableProvider.emit(false) _networkAvailableProvider.emit(false)
wasConnected = false wasConnected = false
} }

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2015 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="100%p" android:toXDelta="0"
android:duration="@android:integer/config_mediumAnimTime"/>
</set>

View File

@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright 2015 Google Inc. All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="0" android:toXDelta="-100%p"
android:duration="@android:integer/config_mediumAnimTime"/>
</set>

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M4.25,5.61C6.27,8.2 10,13 10,13v6c0,0.55 0.45,1 1,1h2c0.55,0 1,-0.45 1,-1v-6c0,0 3.72,-4.8 5.74,-7.39C20.25,4.95 19.78,4 18.95,4H5.04C4.21,4 3.74,4.95 4.25,5.61z"/>
</vector>

View File

@ -1,7 +0,0 @@
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M21,8c-1.45,0 -2.26,1.44 -1.93,2.51l-3.55,3.56c-0.3,-0.09 -0.74,-0.09 -1.04,0l-2.55,-2.55C12.27,10.45 11.46,9 10,9c-1.45,0 -2.27,1.44 -1.93,2.52l-4.56,4.55C2.44,15.74 1,16.55 1,18c0,1.1 0.9,2 2,2c1.45,0 2.26,-1.44 1.93,-2.51l4.55,-4.56c0.3,0.09 0.74,0.09 1.04,0l2.55,2.55C12.73,16.55 13.54,18 15,18c1.45,0 2.27,-1.44 1.93,-2.52l3.56,-3.55C21.56,12.26 23,11.45 23,10C23,8.9 22.1,8 21,8z"/>
<path android:fillColor="@android:color/white" android:pathData="M15,9l0.94,-2.07l2.06,-0.93l-2.06,-0.93l-0.94,-2.07l-0.92,2.07l-2.08,0.93l2.08,0.93z"/>
<path android:fillColor="@android:color/white" android:pathData="M3.5,11l0.5,-2l2,-0.5l-2,-0.5l-0.5,-2l-0.5,2l-2,0.5l2,0.5z"/>
</vector>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M20,8h-2.81c-0.45,-0.78 -1.07,-1.45 -1.82,-1.96L17,4.41 15.59,3l-2.17,2.17C12.96,5.06 12.49,5 12,5c-0.49,0 -0.96,0.06 -1.41,0.17L8.41,3 7,4.41l1.62,1.63C7.88,6.55 7.26,7.22 6.81,8L4,8v2h2.09c-0.05,0.33 -0.09,0.66 -0.09,1v1L4,12v2h2v1c0,0.34 0.04,0.67 0.09,1L4,16v2h2.81c1.04,1.79 2.97,3 5.19,3s4.15,-1.21 5.19,-3L20,18v-2h-2.09c0.05,-0.33 0.09,-0.66 0.09,-1v-1h2v-2h-2v-1c0,-0.34 -0.04,-0.67 -0.09,-1L20,10L20,8zM14,16h-4v-2h4v2zM14,12h-4v-2h4v2z"/>
</vector>

View File

@ -1,5 +0,0 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>

View File

@ -1,5 +0,0 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"/>
</vector>

View File

@ -1,5 +0,0 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"/>
</vector>

View File

@ -1,13 +1,12 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout <androidx.drawerlayout.widget.DrawerLayout 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:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawerContainer" android:id="@+id/drawerContainer"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
tools:context="bou.amine.apps.readerforselfossv2.android.HomeActivity"
android:fitsSystemWindows="true" android:fitsSystemWindows="true"
xmlns:app="http://schemas.android.com/apk/res-auto"> tools:context="bou.amine.apps.readerforselfossv2.android.HomeActivity">
<androidx.coordinatorlayout.widget.CoordinatorLayout <androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="@+id/coordLayout" android:id="@+id/coordLayout"
@ -28,10 +27,12 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content">
<androidx.appcompat.widget.Toolbar app:popupTheme="?attr/toolbarPopupTheme" android:theme="@style/ToolBarStyle" <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"
android:theme="@style/ToolBarStyle"
app:popupTheme="?attr/toolbarPopupTheme"
/> />
@ -45,19 +46,19 @@
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:background="?android:attr/windowBackground"
android:background="?android:attr/windowBackground"> android:orientation="vertical">
<TextView <TextView
android:id="@+id/emptyText" android:id="@+id/emptyText"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@android:color/transparent"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:paddingTop="100dp" android:paddingTop="100dp"
android:text="@string/nothing_here" android:text="@string/nothing_here"
android:textAlignment="center" android:textAlignment="center"
android:textAppearance="@style/TextAppearance.AppCompat.Headline" android:textAppearance="@style/TextAppearance.AppCompat.Headline"
android:background="@android:color/transparent"
android:visibility="gone" /> android:visibility="gone" />
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
@ -69,7 +70,7 @@
android:paddingBottom="60dp" 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"
tools:listitem="@layout/list_item"/> tools:listitem="@layout/list_item" />
</LinearLayout> </LinearLayout>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout> </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
@ -77,6 +78,7 @@
</LinearLayout> </LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.ashokvarma.bottomnavigation.BottomNavigationBar <com.ashokvarma.bottomnavigation.BottomNavigationBar
android:id="@+id/bottomBar" android:id="@+id/bottomBar"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -85,11 +87,4 @@
app:bnbActiveColor="@color/colorAccent" app:bnbActiveColor="@color/colorAccent"
app:bnbBackgroundColor="?attr/bottomBarBackground" /> app:bnbBackgroundColor="?attr/bottomBarBackground" />
</androidx.coordinatorlayout.widget.CoordinatorLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout>
<com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView
android:id="@+id/mainDrawer"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true" />
</androidx.drawerlayout.widget.DrawerLayout> </androidx.drawerlayout.widget.DrawerLayout>

View File

@ -1,18 +1,17 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.motion.widget.MotionLayout 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"
android:id="@+id/container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent"
app:layoutDescription="@xml/image_close_scene">
<com.google.android.material.appbar.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">
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.Toolbar <androidx.appcompat.widget.Toolbar android:theme="@style/ToolBarStyle"
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"
@ -21,13 +20,21 @@
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<androidx.core.widget.NestedScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
app:layout_constraintTop_toBottomOf="@+id/appBarLayout">
<androidx.viewpager2.widget.ViewPager2 <androidx.viewpager2.widget.ViewPager2
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="match_parent"
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_toBottomOf="@+id/appBarLayout" /> </androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.motion.widget.MotionLayout>

View File

@ -1,31 +1,31 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
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"
android:layout_height="match_parent" android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:orientation="vertical" android:orientation="vertical"
tools:context="bou.amine.apps.readerforselfossv2.android.LoginActivity"> tools:context="bou.amine.apps.readerforselfossv2.android.LoginActivity">
<com.google.android.material.appbar.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">
<androidx.appcompat.widget.Toolbar app:popupTheme="?attr/toolbarPopupTheme" android:theme="@style/ToolBarStyle" <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"
android:theme="@style/ToolBarStyle"
/> app:popupTheme="?attr/toolbarPopupTheme" />
</com.google.android.material.appbar.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"
android:gravity="center"
android:orientation="vertical" android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin" android:padding="@dimen/activity_horizontal_margin">
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<!-- Login progress --> <!-- Login progress -->
<ProgressBar <ProgressBar
android:id="@+id/loginProgress" android:id="@+id/loginProgress"
@ -33,16 +33,12 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
android:visibility="gone"/> android:visibility="gone" />
<ScrollView
android:id="@+id/loginForm"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout <LinearLayout
android:id="@+id/loginForm"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<EditText <EditText
@ -53,14 +49,22 @@
android:imeOptions="actionUnspecified" android:imeOptions="actionUnspecified"
android:importantForAutofill="no" android:importantForAutofill="no"
android:inputType="textUri" android:inputType="textUri"
android:maxLines="1" /> android:maxLines="1"
android:minHeight="48dp" />
<com.google.android.material.switchmaterial.SwitchMaterial <com.google.android.material.switchmaterial.SwitchMaterial
android:text="@string/withLoginSwitch" android:id="@+id/selfSigned"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="wrap_content"
android:text="@string/disable_ssl"
android:textAlignment="viewStart" />
<com.google.android.material.switchmaterial.SwitchMaterial
android:id="@+id/withLogin" android:id="@+id/withLogin"
android:layout_weight="1"/> android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/withLoginSwitch"
android:textAlignment="viewStart" />
<EditText <EditText
android:id="@+id/loginView" android:id="@+id/loginView"
@ -70,6 +74,7 @@
android:hint="@string/prompt_login" android:hint="@string/prompt_login"
android:inputType="text" android:inputType="text"
android:maxLines="1" android:maxLines="1"
android:minHeight="48dp"
android:visibility="gone" /> android:visibility="gone" />
<EditText <EditText
@ -80,6 +85,7 @@
android:hint="@string/prompt_password" android:hint="@string/prompt_password"
android:inputType="textPassword" android:inputType="textPassword"
android:maxLines="1" android:maxLines="1"
android:minHeight="48dp"
android:visibility="gone" /> android:visibility="gone" />
<Button <Button
@ -93,7 +99,6 @@
android:textStyle="bold" /> android:textStyle="bold" />
</LinearLayout> </LinearLayout>
</ScrollView>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

View File

@ -24,7 +24,8 @@
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"
tools:listitem="@layout/source_list_item">
</androidx.recyclerview.widget.RecyclerView> </androidx.recyclerview.widget.RecyclerView>
<com.google.android.material.floatingactionbutton.FloatingActionButton <com.google.android.material.floatingactionbutton.FloatingActionButton

View File

@ -4,7 +4,7 @@
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="bou.amine.apps.readerforselfossv2.android.AddSourceActivity"> tools:context="bou.amine.apps.readerforselfossv2.android.UpsertSourceActivity">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
@ -17,116 +17,83 @@
<androidx.appcompat.widget.Toolbar app:popupTheme="?attr/toolbarPopupTheme" android:theme="@style/ToolBarStyle" <androidx.appcompat.widget.Toolbar app:popupTheme="?attr/toolbarPopupTheme" android:theme="@style/ToolBarStyle"
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" />
/>
</com.google.android.material.appbar.AppBarLayout> </com.google.android.material.appbar.AppBarLayout>
<androidx.constraintlayout.widget.ConstraintLayout <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_width="match_parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:id="@+id/formContainer" android:id="@+id/formContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp"
android:visibility="gone" android:visibility="gone"
app:layout_constraintHorizontal_bias="1.0" app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintVertical_bias="0.0">
<TextView
android:text="@string/add_source"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/textView2"
android:textAppearance="@style/TextAppearance.AppCompat.Large"
android:textAlignment="center"
android:layout_marginTop="16dp"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginEnd="16dp"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginRight="16dp"
android:layout_marginStart="16dp"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginLeft="16dp" app:layout_constraintTop_toTopOf="parent">
android:gravity="center_horizontal" />
<EditText <EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:id="@+id/nameInput" android:id="@+id/nameInput"
android:layout_marginTop="32dp" android:layout_width="match_parent"
app:layout_constraintTop_toBottomOf="@+id/textView2" android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent" android:minHeight="48dp"
app:layout_constraintRight_toRightOf="parent" android:layout_marginTop="16dp"
android:inputType="text" android:autofillHints="false"
android:hint="@string/add_source_hint_name" android:hint="@string/add_source_hint_name"
android:textColorHint="?android:textColorPrimary"
android:autofillHints="false" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textUri"
android:ems="10"
android:id="@+id/sourceUri"
android:hint="@string/add_source_hint_url"
android:textColorHint="?android:textColorPrimary"
android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@+id/nameInput"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:autofillHints="false" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:id="@+id/tags"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@+id/sourceUri"
android:hint="@string/add_source_hint_tags"
android:textColorHint="?android:textColorPrimary"
android:inputType="text" android:inputType="text"
android:autofillHints="false" /> android:textColorHint="?android:textColorPrimary"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/sourceUri"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dp"
android:layout_marginTop="16dp"
android:autofillHints="false"
android:hint="@string/add_source_hint_url"
android:inputType="textUri"
android:textColorHint="?android:textColorPrimary"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/nameInput" />
<EditText
android:id="@+id/tags"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dp"
android:layout_marginTop="16dp"
android:autofillHints="false"
android:hint="@string/add_source_hint_tags"
android:inputType="text"
android:textColorHint="?android:textColorPrimary"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/sourceUri" />
<Spinner <Spinner
android:layout_width="match_parent"
android:id="@+id/spoutsSpinner" android:id="@+id/spoutsSpinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="48dp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@+id/tags"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
android:layout_height="40dp"/> app:layout_constraintTop_toBottomOf="@+id/tags" />
<Button <Button
android:text="@string/add_source_save" android:id="@+id/saveBtn"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/saveBtn"
android:elevation="5dp"
android:textColor="?android:textColorPrimary"
android:layout_marginEnd="16dp"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginRight="16dp"
android:layout_marginStart="16dp"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp" android:layout_marginTop="16dp"
app:layout_constraintTop_toBottomOf="@+id/spoutsSpinner" android:elevation="5dp"
android:text="@string/add_source_save"
android:textColor="?android:textColorPrimary"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
android:layout_marginBottom="16dp" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintVertical_bias="0.0"/> app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/spoutsSpinner" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
@ -135,8 +102,6 @@
style="?android:attr/progressBarStyleLarge" style="?android:attr/progressBarStyleLarge"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" 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_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"

View File

@ -1,18 +1,14 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.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"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/card" android:id="@+id/card"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="8dp" android:layout_margin="8dp"
android:layout_marginRight="8dp" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginTop="8dp" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintHorizontal_bias="0.62"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
card_view:cardElevation="2dp" card_view:cardElevation="2dp"
card_view:cardUseCompatPadding="true" card_view:cardUseCompatPadding="true"
@ -28,8 +24,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:adjustViewBounds="true" android:adjustViewBounds="true"
android:cropToPadding="true" android:cropToPadding="true"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/background_splash" app:srcCompat="@drawable/background_splash"
card_view:layout_constraintBottom_toTopOf="@+id/constraintLayout" /> card_view:layout_constraintBottom_toTopOf="@+id/constraintLayout" />
@ -39,18 +35,17 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/itemImage"> app:layout_constraintTop_toBottomOf="@+id/itemImage">
<ImageView <bou.amine.apps.readerforselfossv2.android.utils.CircleImageView
android:id="@+id/sourceImage" android:id="@+id/sourceImage"
android:layout_width="40dp" android:layout_width="40dp"
android:layout_height="40dp" android:layout_height="40dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/background_splash" /> app:srcCompat="@drawable/background_splash" />
@ -58,70 +53,63 @@
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_marginEnd="8dp" android:layout_margin="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:gravity="start" android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:textStyle="bold"
android:textColor="?android:textColorPrimary" android:textColor="?android:textColorPrimary"
app:layout_constraintHorizontal_bias="0.0" android:textStyle="bold"
app:layout_constraintLeft_toRightOf="@+id/sourceImage" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintStart_toEndOf="@+id/sourceImage"
app:layout_constraintTop_toTopOf="@+id/sourceImage" app:layout_constraintTop_toTopOf="parent"
tools:text="Titre" /> tools:text="Titre" />
<TextView <TextView
android:id="@+id/sourceTitleAndDate" android:id="@+id/sourceTitleAndDate"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:gravity="start" android:layout_marginEnd="8dp"
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:textSize="14sp"
android:textColor="?android:textColorPrimary" android:textColor="?android:textColorPrimary"
app:layout_constraintLeft_toLeftOf="@+id/title" android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/title"
app:layout_constraintTop_toBottomOf="@+id/title" app:layout_constraintTop_toBottomOf="@+id/title"
tools:text="Google Actualité Il y a 5h" /> tools:text="Google Actualité Il y a 5h" />
<RelativeLayout <LinearLayout
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="16dp" android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/sourceTitleAndDate"> app:layout_constraintTop_toBottomOf="@+id/sourceTitleAndDate">
<ImageButton <ImageButton
android:id="@+id/favButton" android:id="@+id/browserBtn"
android:layout_width="35dp" android:layout_width="35dp"
android:layout_height="35dp" android:layout_height="35dp"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:adjustViewBounds="true" android:adjustViewBounds="true"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:contentDescription="@string/reader_action_open"
android:elevation="5dp" android:elevation="5dp"
android:padding="4dp" android:padding="4dp"
android:scaleType="centerCrop" android:scaleType="centerCrop"
app:srcCompat="@drawable/ic_menu_heart_60dp" app:srcCompat="@drawable/ic_open_in_browser_black_24dp"
app:tint="@color/ic_menu_heart_color" /> app:tint="?android:attr/textColorPrimary" />
<ImageButton <ImageButton
android:id="@+id/shareBtn" android:id="@+id/shareBtn"
android:layout_width="35dp" android:layout_width="35dp"
android:layout_height="35dp" android:layout_height="35dp"
android:layout_centerVertical="true" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_toLeftOf="@+id/favButton"
android:layout_toStartOf="@+id/favButton"
android:adjustViewBounds="true" android:adjustViewBounds="true"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:contentDescription="@string/share"
android:elevation="5dp" android:elevation="5dp"
android:padding="4dp" android:padding="4dp"
android:scaleType="centerCrop" android:scaleType="centerCrop"
@ -129,23 +117,21 @@
app:tint="?android:attr/textColorPrimary" /> app:tint="?android:attr/textColorPrimary" />
<ImageButton <ImageButton
android:id="@+id/browserBtn" android:id="@+id/favButton"
android:layout_width="35dp" android:layout_width="35dp"
android:layout_height="35dp" android:layout_height="35dp"
android:layout_centerVertical="true" android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_toLeftOf="@+id/shareBtn"
android:layout_toStartOf="@+id/shareBtn"
android:adjustViewBounds="true" android:adjustViewBounds="true"
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:contentDescription="@string/add_to_favs_reader"
android:elevation="5dp" android:elevation="5dp"
android:padding="4dp" android:padding="4dp"
android:scaleType="centerCrop" android:scaleType="centerCrop"
app:srcCompat="@drawable/ic_open_in_browser_black_24dp" app:srcCompat="@drawable/ic_menu_heart_60dp"
app:tint="?android:attr/textColorPrimary" /> app:tint="@color/ic_menu_heart_color" />
</RelativeLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/circleImage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
app:shapeAppearanceOverlay="@style/circleImageView"
app:srcCompat="@drawable/background_splash" />
<TextView
android:id="@+id/circleText"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:ellipsize="none"
android:gravity="center"
android:singleLine="true"
android:textColor="@color/white"
android:textIsSelectable="false"
android:textSize="20sp"
android:typeface="normal" />
</RelativeLayout>

View File

@ -0,0 +1,96 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/progressBar2"
style="?android:attr/progressBarStyle"
android:layout_width="48dp"
android:layout_height="48dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="gone" />
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/filterView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
tools:visibility="visible">
<TextView
android:id="@+id/filterTagsTitle"
style="@style/MaterialAlertDialog.MaterialComponents.Title.Text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="16dp"
android:text="@string/filter_item_tags"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/filterSourcesTitle"
style="@style/MaterialAlertDialog.MaterialComponents.Title.Text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginTop="24dp"
android:text="@string/filter_item_sources"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/tagsGroup" />
<com.google.android.material.chip.ChipGroup
android:id="@+id/tagsGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/filterTagsTitle"
app:singleSelection="true">
</com.google.android.material.chip.ChipGroup>
<com.google.android.material.chip.ChipGroup
android:id="@+id/sourcesGroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="24dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/filterSourcesTitle">
</com.google.android.material.chip.ChipGroup>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/floatingActionButton2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:clickable="true"
android:contentDescription="@string/menu_home_search"
android:focusable="true"
app:backgroundTint="@color/colorAccent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:rippleColor="@color/colorAccentDark"
app:srcCompat="@drawable/ic_menu_search_white_24dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -1,5 +1,4 @@
<androidx.coordinatorlayout.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"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -22,10 +21,22 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="200dp" android:layout_height="200dp"
android:scaleType="centerCrop" android:scaleType="centerCrop"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent" />
/>
<TextView
android:id="@+id/titleView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="8dp"
android:layout_marginRight="16dp"
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView" />
<TextView <TextView
android:id="@+id/source" android:id="@+id/source"
@ -36,40 +47,23 @@
android:layout_marginRight="16dp" android:layout_marginRight="16dp"
android:textColor="?android:textColorSecondary" android:textColor="?android:textColorSecondary"
android:textSize="12sp" android:textSize="12sp"
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"
app:layout_constraintTop_toBottomOf="@+id/titleView" /> app:layout_constraintTop_toBottomOf="@+id/titleView" />
<TextView
android:id="@+id/titleView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="8dp"
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
android:textStyle="bold"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageView" />
<WebView <WebView
android:id="@+id/webcontent" android:id="@+id/webcontent"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginTop="24dp"
android:layout_marginRight="16dp"
android:background="?attr/webviewBackground"
android:paddingBottom="48dp"
android:textColorLink="?attr/colorAccent" android:textColorLink="?attr/colorAccent"
android:visibility="gone" android:visibility="gone"
android:layout_marginLeft="16dp" app:layout_constraintEnd_toEndOf="parent"
android:layout_marginRight="16dp" app:layout_constraintStart_toStartOf="parent"
android:layout_marginTop="24dp"
android:paddingBottom="48dp"
android:background="?attr/webviewBackground"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/source" app:layout_constraintTop_toBottomOf="@+id/source"
tools:visibility="visible" /> tools:visibility="visible" />
@ -77,46 +71,23 @@
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
<FrameLayout <com.leinardi.android.speeddial.SpeedDialView
android:layout_width="match_parent" android:id="@+id/speedDial"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_gravity="end|bottom|right">
<com.github.rubensousa.floatingtoolbar.FloatingToolbar
android:id="@+id/floatingToolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_gravity="bottom"
app:floatingMenu="@menu/reader_toolbar" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
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_gravity="end|bottom|right" android:layout_gravity="bottom|end"
android:layout_marginBottom="16dp" app:layout_behavior="@string/speeddial_scrolling_view_snackbar_behavior"
android:layout_marginEnd="16dp" app:sdMainFabClosedSrc="@drawable/ic_add_white_24dp" />
android:layout_marginRight="16dp"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:src="@drawable/ic_add_white_24dp"
app:backgroundTint="?attr/colorAccent"
app:fabSize="mini"
app:rippleColor="?attr/colorAccentDark" />
</FrameLayout>
<FrameLayout <FrameLayout
android:id="@+id/progressBar" android:id="@+id/progressBar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:visibility="gone"
android:animateLayoutChanges="true"
android:alpha="0.8" android:alpha="0.8"
android:animateLayoutChanges="true"
android:background="@color/black" android:background="@color/black"
android:clickable="false"> android:clickable="false"
android:visibility="gone">
<ProgressBar <ProgressBar
style="?android:attr/progressBarStyleLarge" style="?android:attr/progressBarStyleLarge"
@ -126,4 +97,5 @@
android:progressTint="?attr/colorAccent" /> android:progressTint="?attr/colorAccent" />
</FrameLayout> </FrameLayout>
</androidx.coordinatorlayout.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"?>
<RelativeLayout 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"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent"> android:layout_height="match_parent">
@ -13,4 +13,4 @@
android:background="@drawable/checkerboard" android:background="@drawable/checkerboard"
app:srcCompat="@android:drawable/screen_background_dark" /> app:srcCompat="@android:drawable/screen_background_dark" />
</RelativeLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -3,17 +3,18 @@
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"
android:layout_height="88dp"> android:layout_height="wrap_content">
<ImageView <bou.amine.apps.readerforselfossv2.android.utils.CircleImageView
android:id="@+id/itemImage" android:id="@+id/itemImage"
android:layout_width="46dp" android:layout_width="46dp"
android:layout_height="46dp" android:layout_height="46dp"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="21dp" android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent" />
android:layout_marginLeft="8dp" />
<TextView <TextView
android:id="@+id/title" android:id="@+id/title"
@ -21,42 +22,35 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="8dp" android:layout_marginTop="8dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="8dp"
android:ellipsize="end" android:ellipsize="end"
android:fontFamily="sans-serif" android:fontFamily="sans-serif"
android:gravity="start"
android:maxLines="3" android:maxLines="3"
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:textAllCaps="false" android:textColor="?android:textColorPrimary"
android:textSize="16sp" android:textSize="16sp"
android:textStyle="bold" android:textStyle="bold"
android:textColor="?android:textColorPrimary"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
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"
tools:text="Titre" tools:text="Titre" />
android:layout_marginLeft="8dp"
android:layout_marginRight="16dp" />
<TextView <TextView
android:id="@+id/sourceTitleAndDate" android:id="@+id/sourceTitleAndDate"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:layout_marginTop="66dp" android:layout_marginTop="16dp"
android:layout_marginEnd="16dp" android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:gravity="start" android:gravity="start"
android:maxLines="1"
android:textAlignment="viewStart" android:textAlignment="viewStart"
android:textSize="14sp"
android:textColor="?android:textColorPrimary" android:textColor="?android:textColorPrimary"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
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_toBottomOf="@+id/title"
tools:text="Google Actualité Il y a 5h" tools:text="Google Actualité Il y a 5h" />
android:layout_marginLeft="8dp"
android:layout_marginRight="16dp" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -3,48 +3,74 @@
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"
android:layout_height="48dp" android:layout_height="wrap_content"
android:orientation="vertical"> android:orientation="vertical">
<ImageView
android:id="@+id/itemImage"
android:layout_width="36dp"
android:layout_height="36dp"
android:importantForAccessibility="no"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/sourceTitle"
android:layout_width="0dp"
android:layout_height="17dp"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:gravity="start"
android:maxLines="1"
android:textAlignment="textStart"
android:textSize="13sp"
android:textColor="?android:textColorPrimary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/deleteBtn"
app:layout_constraintStart_toEndOf="@+id/itemImage"
app:layout_constraintTop_toTopOf="parent"
tools:text="source title" />
<Button <Button
android:id="@+id/deleteBtn" android:id="@+id/deleteBtn"
style="@style/Widget.AppCompat.Button.Borderless" style="@style/Widget.AppCompat.Button.Borderless"
android:layout_width="34dp" android:layout_width="48dp"
android:layout_height="34dp" android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:background="@drawable/ic_remove_circle_outline_black_24dp" android:background="@drawable/ic_remove_circle_outline_black_24dp"
android:backgroundTint="?android:textColorSecondary" android:backgroundTint="?android:textColorSecondary"
android:elevation="4dp"
android:contentDescription="@string/remove_source" android:contentDescription="@string/remove_source"
android:elevation="4dp"
app:iconSize="34dp"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
<bou.amine.apps.readerforselfossv2.android.utils.CircleImageView
android:id="@+id/itemImage"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:importantForAccessibility="no"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
<TextView
android:id="@+id/sourceTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:ellipsize="end"
android:maxLines="1"
android:textAlignment="viewStart"
android:textColor="?android:textColorPrimary"
android:textSize="20sp"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/errorText"
app:layout_constraintEnd_toStartOf="@+id/deleteBtn"
app:layout_constraintStart_toEndOf="@+id/itemImage"
app:layout_constraintTop_toTopOf="parent"
tools:text="Source title" />
<TextView
android:id="@+id/errorText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10sp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:textAppearance="@style/TextAppearance.AppCompat.Small"
android:textColor="@color/red"
android:textStyle="italic"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/deleteBtn"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/itemImage"
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
tools:visibility="visible" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -8,18 +8,40 @@
app:showAsAction="ifRoom|collapseActionView" app:showAsAction="ifRoom|collapseActionView"
app:actionViewClass="androidx.appcompat.widget.SearchView" /> app:actionViewClass="androidx.appcompat.widget.SearchView" />
<item android:id="@+id/action_filter"
android:title="@string/menu_home_filter"
android:icon="@drawable/ic_baseline_filter_alt_24"
android:orderInCategory="1"
app:showAsAction="always" />
<item android:id="@+id/readAll" <item android:id="@+id/readAll"
android:icon="@drawable/ic_menu_done_all_white_24dp" android:icon="@drawable/ic_menu_done_all_white_24dp"
android:title="@string/readAll" android:title="@string/readAll"
android:orderInCategory="1" android:orderInCategory="2"
app:showAsAction="always"/> app:showAsAction="ifRoom"/>
<item android:id="@+id/action_sources"
android:title="@string/menu_home_sources"
android:orderInCategory="97"
app:showAsAction="never"/>
<item android:id="@+id/action_settings"
android:title="@string/title_activity_settings"
android:orderInCategory="98"
app:showAsAction="never"/>
<item <item
android:id="@+id/refresh" android:id="@+id/refresh"
android:icon="@drawable/ic_menu_refresh_white_24dp" app:showAsAction="never"
android:orderInCategory="99" android:orderInCategory="101"
android:title="@string/menu_home_refresh" /> android:title="@string/menu_home_refresh" />
<item
android:id="@+id/issue_tracker"
app:showAsAction="never"
android:orderInCategory="103"
android:title="@string/issue_tracker_link" />
<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

@ -3,6 +3,13 @@
xmlns:app="http://schemas.android.com/apk/res-auto"> xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/issue_tracker"
app:showAsAction="never"
android:orderInCategory="101"
android:title="@string/issue_tracker_link" />
<item android:id="@+id/about" <item android:id="@+id/about"
android:title="@string/action_about" android:title="@string/action_about"
android:orderInCategory="102" android:orderInCategory="102"

View File

@ -1,13 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" <menu 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_baseline_white_eye_24dp"
android:title="@string/unmark"
app:showAsAction="ifRoom" />
<item <item
android:id="@+id/open_action" android:id="@+id/open_action"
android:icon="@drawable/ic_open_in_browser_white_24dp" android:icon="@drawable/ic_open_in_browser_white_24dp"

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<string name="app_name">"Lector per a Selfoss"</string> <string name="app_name">"Lector per a Selfoss"</string>
<string name="title_activity_login">"Inicia la sessió"</string> <string name="title_activity_login">"Inicia la sessió"</string>
<string name="prompt_password">"Contrasenya"</string> <string name="prompt_password">"Contrasenya"</string>
@ -7,10 +7,10 @@
<string name="error_invalid_password">"La contrasenya és massa curta"</string> <string name="error_invalid_password">"La contrasenya és massa curta"</string>
<string name="error_field_required">"Camp necessari"</string> <string name="error_field_required">"Camp necessari"</string>
<string name="prompt_url">"URL"</string> <string name="prompt_url">"URL"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"Autenticació (si és necessària)"</string> <string name="withLoginSwitch">"Autenticació (si és necessària)"</string>
<string name="login_url_problem">"Pot ser que falti una \"/\" al final de l'url."</string> <string name="login_url_problem">"Pot ser que falti una \"/\" al final de l'url."</string>
<string name="prompt_login">"Nom d'usuari"</string> <string name="prompt_login">"Nom d'usuari"</string>
<string name="label_share">"Comparteix"</string>
<string name="readAll">"Llegeix-ho tot"</string> <string name="readAll">"Llegeix-ho tot"</string>
<string name="action_disconnect">"Desconnecta't"</string> <string name="action_disconnect">"Desconnecta't"</string>
<string name="title_activity_settings">"Configuració"</string> <string name="title_activity_settings">"Configuració"</string>
@ -23,13 +23,6 @@
<string name="wrong_infos">"Torneu a comprovar la informació."</string> <string name="wrong_infos">"Torneu a comprovar la informació."</string>
<string name="all_posts_not_read">"No s'han llegit totes les publicacions"</string> <string name="all_posts_not_read">"No s'han llegit totes les publicacions"</string>
<string name="all_posts_read">"S'han llegit totes les publicacions"</string> <string name="all_posts_read">"S'han llegit totes les publicacions"</string>
<string name="nothing_here">"No hi ha res"</string>
<string name="tab_new">"Nou"</string>
<string name="tab_read">"Tot"</string>
<string name="tab_favs">"Preferits"</string>
<string name="action_about">"Quant a"</string>
<string name="marked_as_read">"Element llegit"</string>
<string name="marked_as_unread">"Item unread"</string>
<string name="undo_string">"Desfés"</string> <string name="undo_string">"Desfés"</string>
<string name="addStringNoUrl">"Inicieu la sessió per afegir fonts."</string> <string name="addStringNoUrl">"Inicieu la sessió per afegir fonts."</string>
<string name="cant_get_sources">"No es pot obtenir la llista de fonts."</string> <string name="cant_get_sources">"No es pot obtenir la llista de fonts."</string>
@ -62,28 +55,18 @@
<string name="card_height_on">L\'alçada de les targetes s\'ajustarà al seu contingut</string> <string name="card_height_on">L\'alçada de les targetes s\'ajustarà al seu contingut</string>
<string name="card_height_off">L\'alçada de les targetes serà fixa</string> <string name="card_height_off">L\'alçada de les targetes serà fixa</string>
<string name="source_code">Codi font</string> <string name="source_code">Codi font</string>
<string name="drawer_error_loading_tags">S\'ha produït un error en carregar les etiquetes</string> <string name="filter_item_tags">Etiquetes</string>
<string name="drawer_item_filters">Filtres</string> <string name="filter_item_sources">Fonts</string>
<string name="drawer_action_clear">Esborra</string>
<string name="drawer_item_tags">Etiquetes</string>
<string name="drawer_item_sources">Fonts</string>
<string name="drawer_action_edit">Edita</string>
<string name="drawer_loading">S\'està carregant…</string>
<string name="menu_home_search">Cerca</string> <string name="menu_home_search">Cerca</string>
<string name="can_delete_source">No es pot suprimir la font</string> <string name="can_delete_source">No es pot suprimir la font</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">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="pref_header_theme">Temes</string> <string name="pref_header_theme">Temes</string>
<string name="default_theme">Predeterminat</string>
<string name="default_dark_theme">Predeterminat/Fosc</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">Nombre d\'elements carregats</string> <string name="pref_api_items_number_title">Nombre d\'elements carregats</string>
<string name="pref_hidden_tags">Etiquetes ocultes</string>
<string name="pref_general_infinite_loading_title">Carrega articles en desplaçar</string> <string name="pref_general_infinite_loading_title">Carrega articles en desplaçar</string>
<string name="translation">Traducció</string> <string name="translation">Traducció</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="cant_open_invalid_url">L\'element URL no és vàlid. Estic intentant solucionar aquest problema perquè l\'aplicació no falli.</string>
<string name="drawer_report_bug">Informa d\'un error</string>
<string name="items_number_should_be_number">El nombre d\'elements ha de ser enter.</string> <string name="items_number_should_be_number">El nombre d\'elements ha de ser enter.</string>
<string name="reader_action_more">Més informació</string>
<string name="reader_action_open">Obre al navegador</string> <string name="reader_action_open">Obre al navegador</string>
<string name="reader_action_share">Comparteix</string> <string name="reader_action_share">Comparteix</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="pref_switch_actions_pager_scroll_on">Es marcaran els articles com a llegits en lliscar el dit d\'un article a l\'altre.</string>
@ -94,7 +77,6 @@
<string name="markall_dialog_message">Aquesta acció marcarà els elements com a llegits.</string> <string name="markall_dialog_message">Aquesta acció marcarà els elements com a llegits.</string>
<string name="pref_switch_actions_pager_scroll">Marca com a llegit en lliscar el dit</string> <string name="pref_switch_actions_pager_scroll">Marca com a llegit en lliscar el dit</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="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="drawer_item_hidden_tags">Etiquetes ocultes</string>
<string name="unmark">Marca com no llegit</string> <string name="unmark">Marca com no llegit</string>
<string name="pref_header_offline">Sense connexió i memòria clau</string> <string name="pref_header_offline">Sense connexió i memòria clau</string>
<string name="pref_switch_items_caching_off">Els articles no es guardaran a la memòria del dispositiu i l\'aplicació no es podrà utilitzar sense connexió.</string> <string name="pref_switch_items_caching_off">Els articles no es guardaran a la memòria del dispositiu i l\'aplicació no es podrà utilitzar sense connexió.</string>
@ -102,14 +84,14 @@
<string name="pref_switch_items_caching">Guarda els elements per utilitzar-los sense connexió</string> <string name="pref_switch_items_caching">Guarda els elements per utilitzar-los sense connexió</string>
<string name="pref_switch_update_sources">Check for new sources and tags</string> <string name="pref_switch_update_sources">Check for new sources and tags</string>
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string> <string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
<string name="network_connectivity_lost">"Network connection lost"</string> <string name="network_connectivity_lost">"Sense connexió!"</string>
<string name="network_connectivity_retrieved">"Network connection is now available"</string> <string name="network_connectivity_retrieved">"Network connection is now available"</string>
<string name="pref_switch_periodic_refresh">Sincronitza els articles</string> <string name="pref_switch_periodic_refresh">Sincronitza els articles</string>
<string name="pref_switch_periodic_refresh_off">Els articles no se sincronitzaran en segon pla</string> <string name="pref_switch_periodic_refresh_off">Els articles no se sincronitzaran en segon pla</string>
<string name="pref_switch_periodic_refresh_on">Els articles se sincronitzaran periòdicament</string> <string name="pref_switch_periodic_refresh_on">Els articles se sincronitzaran periòdicament</string>
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Interval de sincronització ( >= 15 minuts)]]></string> <string name="pref_periodic_refresh_minutes_title"><![CDATA[Interval de sincronització ( >= 15 minuts)]]></string>
<string name="pref_switch_refresh_when_charging">Sincronitza només quan el telèfon s\'està carregant</string> <string name="pref_switch_refresh_when_charging">Sincronitza només quan el telèfon s\'està carregant</string>
<string name="loading_notification_title">S\'està carregant...</string> <string name="loading_notification_title">S\'està carregant</string>
<string name="loading_notification_text">Selfoss està sincronitzant els articles</string> <string name="loading_notification_text">Selfoss està sincronitzant els articles</string>
<string name="notification_channel_sync">Notificació de sincronització</string> <string name="notification_channel_sync">Notificació de sincronització</string>
<string name="new_items_channel_sync">Notificació d\'elements nous</string> <string name="new_items_channel_sync">Notificació d\'elements nous</string>
@ -124,18 +106,27 @@
<string name="reader_text_align_left">Align left</string> <string name="reader_text_align_left">Align left</string>
<string name="reader_text_align_justify">Justify</string> <string name="reader_text_align_justify">Justify</string>
<string name="settings_reader_font">Reader font</string> <string name="settings_reader_font">Reader font</string>
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string> <string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string> <string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string> <string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string> <string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
<string name="pref_switch_enable_analytics">Enable analytics</string>
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string> <string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string> <string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string> <string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string> <string name="pref_switch_disable_acra">"Disable automatic bug reporting."</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"No hi ha res"</string>
<string name="tab_new">"Nou"</string>
<string name="tab_read">"Tot"</string>
<string name="tab_favs">"Preferits"</string>
<string name="action_about">"Quant a"</string>
<string name="marked_as_read">"Element llegit"</string>
<string name="marked_as_unread">"Item unread"</string>
</resources> </resources>

View File

@ -1,16 +1,16 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<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="action_sign_in">"Fortfahren"</string> <string name="action_sign_in">"Fortfahren"</string>
<string name="error_invalid_password">"Passwort ist nicht lang genug"</string> <string name="error_invalid_password">"Passwort ist nicht lang genug"</string>
<string name="error_field_required">"Pflichtfeld"</string> <string name="error_field_required">"Pflichtfeld"</string>
<string name="prompt_url">"URL"</string> <string name="prompt_url">"URL"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"Anmeldung erforderlich?"</string> <string name="withLoginSwitch">"Anmeldung erforderlich?"</string>
<string name="login_url_problem">"Ups. Du musst eventuell ein \"/\" am Ende der URL anhängen."</string> <string name="login_url_problem">"Ups. Du musst eventuell ein \"/\" am Ende der URL anhängen."</string>
<string name="prompt_login">"Benutzername"</string> <string name="prompt_login">"Benutzername"</string>
<string name="label_share">"Teilen"</string>
<string name="readAll">"Alle gelesen"</string> <string name="readAll">"Alle gelesen"</string>
<string name="action_disconnect">"Verbindung trennen"</string> <string name="action_disconnect">"Verbindung trennen"</string>
<string name="title_activity_settings">"Einstellungen"</string> <string name="title_activity_settings">"Einstellungen"</string>
@ -23,22 +23,15 @@
<string name="wrong_infos">"Überprüfe deine Angaben noch einmal."</string> <string name="wrong_infos">"Überprüfe deine Angaben noch einmal."</string>
<string name="all_posts_not_read">"Nicht alle Beiträge wurden gelesen"</string> <string name="all_posts_not_read">"Nicht alle Beiträge wurden gelesen"</string>
<string name="all_posts_read">"Alle Beiträge wurden gelesen"</string> <string name="all_posts_read">"Alle Beiträge wurden gelesen"</string>
<string name="nothing_here">"Keine Einträge vorhanden"</string>
<string name="tab_new">"Neu"</string>
<string name="tab_read">"Alle"</string>
<string name="tab_favs">"Favoriten"</string>
<string name="action_about">"Über"</string>
<string name="marked_as_read">"Artikel gelesen"</string>
<string name="marked_as_unread">"Item unread"</string>
<string name="undo_string">"Rückgängig"</string> <string name="undo_string">"Rückgängig"</string>
<string name="addStringNoUrl">"Melde dich an um Quellen hinzuzufügen."</string> <string name="addStringNoUrl">"Melde dich an um Quellen hinzuzufügen."</string>
<string name="cant_get_sources">"Quellen können nicht abgerufen werden."</string> <string name="cant_get_sources">"Quellen können nicht abgerufen werden."</string>
<string name="cant_create_source">"Quelle kann nicht gespeichert werden."</string> <string name="cant_create_source">"Quelle kann nicht gespeichert werden."</string>
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string> <string name="cant_get_spouts_no_network">"Fehler beim Laden der Spouts-Liste aufgrund von Netzwerkproblemen."</string>
<string name="cant_get_spouts">"Can't get spouts list. There may ben an api issue."</string> <string name="cant_get_spouts">"Fehler beim Laden der Spouts-Liste, möglicherweise aufgrund eines API-Fehlers."</string>
<string name="form_not_complete">"Das Formular ist nicht vollständig"</string> <string name="form_not_complete">"Das Formular ist nicht vollständig"</string>
<string name="pref_header_links">"Links"</string> <string name="pref_header_links">"Links"</string>
<string name="issue_tracker_link">"Issue Tracker"</string> <string name="issue_tracker_link">"Ticketsystem"</string>
<string name="issue_tracker_summary">"Melde einen Bug oder rege ein neues Feature an"</string> <string name="issue_tracker_summary">"Melde einen Bug oder rege ein neues Feature an"</string>
<string name="warning_wrong_url">"WARNUNG"</string> <string name="warning_wrong_url">"WARNUNG"</string>
<string name="pref_switch_card_view_title">"Kachelansicht"</string> <string name="pref_switch_card_view_title">"Kachelansicht"</string>
@ -62,80 +55,78 @@
<string name="card_height_on">Kartenhöhe passt sich Inhalt an</string> <string name="card_height_on">Kartenhöhe passt sich Inhalt an</string>
<string name="card_height_off">Kartenhöhe ist fix</string> <string name="card_height_off">Kartenhöhe ist fix</string>
<string name="source_code">Quellcode</string> <string name="source_code">Quellcode</string>
<string name="drawer_error_loading_tags">Fehler beim Laden der Tags…</string> <string name="filter_item_tags">Tags</string>
<string name="drawer_item_filters">Filter</string> <string name="filter_item_sources">Quellen</string>
<string name="drawer_action_clear">leeren</string>
<string name="drawer_item_tags">Tags</string>
<string name="drawer_item_sources">Quellen</string>
<string name="drawer_action_edit">bearbeiten</string>
<string name="drawer_loading">Lade…</string>
<string name="menu_home_search">Suche</string> <string name="menu_home_search">Suche</string>
<string name="can_delete_source">Can\'t delete the source</string> <string name="can_delete_source">Quelle konnte nicht gelöscht werden…</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="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_dark_theme">Standard (Dunkel)</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">Anzahl der zu ladenden Artikel</string>
<string name="pref_hidden_tags">Hidden Tags</string> <string name="pref_general_infinite_loading_title">Weitere Artikel beim Navigieren laden</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">Der Artikel-Link ist ungültig. Ich such nach einer Lösung dieses Problems, damit die App nicht abstürzt.</string>
<string name="drawer_report_bug">Melde einen Fehler</string> <string name="items_number_should_be_number">Die Anzahl der Artikel sollte eine Ganzzahl sein.</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">Im Browser öffnen</string> <string name="reader_action_open">Im Browser öffnen</string>
<string name="reader_action_share">Teilen</string> <string name="reader_action_share">Teilen</string>
<string name="pref_switch_actions_pager_scroll_on">Mark articles as read when swiping between articles.</string> <string name="pref_switch_actions_pager_scroll_on">Artikel als gelesen markieren, wenn zwischen den Artikeln gewischt wird.</string>
<string name="add_to_favs_reader">Zu Favoriten hinzufügen</string> <string name="add_to_favs_reader">Zu Favoriten hinzufügen</string>
<string name="pref_content_reader_font_size">Article reader content font size</string> <string name="pref_content_reader_font_size">Schriftgröße im Lesemodus</string>
<string name="pref_header_viewer">Article viewer</string> <string name="pref_header_viewer">Lesemodus</string>
<string name="refresh_dialog_message">This will refresh your Selfoss instance.</string> <string name="refresh_dialog_message">Dies wird die Selfoss-Instanz aktualisieren.</string>
<string name="markall_dialog_message">Dies wird alle Elemente als gelesen markieren.</string> <string name="markall_dialog_message">Dies wird alle Elemente als gelesen markieren.</string>
<string name="pref_switch_actions_pager_scroll">Beim Wischen als gelesen markieren</string> <string name="pref_switch_actions_pager_scroll">Beim Wischen als gelesen markieren</string>
<string name="pref_switch_actions_pager_scroll_off">Don\'t mark articles as read when swiping.</string> <string name="pref_switch_actions_pager_scroll_off">Artikel nicht als gelesen markieren, wenn zwischen den Artikeln gewischt wird.</string>
<string name="drawer_item_hidden_tags">Hidden Tags</string>
<string name="unmark">Eintrag als ungelesen markieren</string> <string name="unmark">Eintrag als ungelesen markieren</string>
<string name="pref_header_offline">Offline and cache</string> <string name="pref_header_offline">Offline-Modus und Cache</string>
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string> <string name="pref_switch_items_caching_off">Artikel werden nicht lokal zwischengespeichert wodurch die App nicht offline nutzbar ist.</string>
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string> <string name="pref_switch_items_caching_on">Artikel werden lokal zwischengespeichert wodurch die App offline nutzbar ist.</string>
<string name="pref_switch_items_caching">Save items for offline use</string> <string name="pref_switch_items_caching">Artikel lokal zwischenspeichern</string>
<string name="pref_switch_update_sources">Check for new sources and tags</string> <string name="pref_switch_update_sources">Nach neuen Quellen und Tags suchen</string>
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string> <string name="pref_switch_update_sources_summary">Diese Funktion sollte deaktiviert werden, wenn der Server übermäßig viele Datenbankanfragen erhält.</string>
<string name="network_connectivity_lost">"Die Netzwerkverbindung wurde unterbrochen"</string> <string name="network_connectivity_lost">"Die Netzwerkverbindung wurde unterbrochen"</string>
<string name="network_connectivity_retrieved">"Netzwerkverbindung ist jetzt verfügbar"</string> <string name="network_connectivity_retrieved">"Netzwerkverbindung ist jetzt verfügbar"</string>
<string name="pref_switch_periodic_refresh">Synchronisiere Artikel</string> <string name="pref_switch_periodic_refresh">Synchronisiere Artikel</string>
<string name="pref_switch_periodic_refresh_off">Artikel werden nicht im Hintergrund synchronisiert</string> <string name="pref_switch_periodic_refresh_off">Artikel werden nicht im Hintergrund synchronisiert</string>
<string name="pref_switch_periodic_refresh_on">Die Artikel werden regelmäßig synchronisiert</string> <string name="pref_switch_periodic_refresh_on">Die Artikel werden regelmäßig synchronisiert</string>
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string> <string name="pref_periodic_refresh_minutes_title"><![CDATA[Aktualisierungsintervall (>= 15 Minuten)]]></string>
<string name="pref_switch_refresh_when_charging">Nur aktualisieren, wenn das Telefon aufgeladen wird</string> <string name="pref_switch_refresh_when_charging">Nur aktualisieren, wenn das Telefon aufgeladen wird</string>
<string name="loading_notification_title">Lädt...</string> <string name="loading_notification_title">Lädt</string>
<string name="loading_notification_text">Selfoss synchronisiert Ihre Artikel</string> <string name="loading_notification_text">Selfoss synchronisiert Ihre Artikel</string>
<string name="notification_channel_sync">Sync notification</string> <string name="notification_channel_sync">Synchronisationsbenachrichtigung</string>
<string name="new_items_channel_sync">New items notification</string> <string name="new_items_channel_sync">Benachrichtigung bei neuen Artikeln</string>
<string name="new_items_notification_title">New items !</string> <string name="new_items_notification_title">Neue Artikel!</string>
<string name="new_items_notification_text">%1$d new items loaded.</string> <string name="new_items_notification_text">%1$d neue Artikel geladen.</string>
<string name="pref_switch_notify_new_items">Notify on new items synced.</string> <string name="pref_switch_notify_new_items">Benachrichtigung bei neuen Artikeln</string>
<string name="shortcut_offline">Offline</string> <string name="shortcut_offline">Offline</string>
<string name="pref_api_timeout">API-Zeitüberschreitung</string> <string name="pref_api_timeout">API-Zeitüberschreitung</string>
<string name="pref_header_experimental">Experimentell</string> <string name="pref_header_experimental">Experimentell</string>
<string name="webview_dialog_issue_message">Webview not available. Disabling the article viewer to avoid any future crashes. Will load articles inside of your browser from now on.</string> <string name="webview_dialog_issue_message">Webview ist nicht verfügbar. Deaktiviere den Lesemodus, um zukünftige Abstürze zu vermeiden. Lade von nun an die Nachrichten in deinen Browser.</string>
<string name="webview_dialog_issue_title">Webview issue</string> <string name="webview_dialog_issue_title">Webview-Probleme</string>
<string name="reader_text_align_left">Align left</string> <string name="reader_text_align_left">Linksbündig</string>
<string name="reader_text_align_justify">Justify</string> <string name="reader_text_align_justify">Blocksatz</string>
<string name="settings_reader_font">Reader font</string> <string name="settings_reader_font">Schriftgröße im Lesemodus</string>
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string> <string name="remove_source">Quelle entfernen</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string> <string name="pref_theme_title">Heller/Dunkler Modus</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string> <string name="mode_dark">Dunkler Modus</string>
<string name="remove_source">Remove source</string> <string name="mode_system">Systemeinstellungen übernehmen</string>
<string name="pref_theme_title">Light/Dark mode</string> <string name="mode_light">Heller Modus</string>
<string name="mode_dark">Dark mode</string> <string name="gdpr_dialog_title">Diese App teilt keine persönlichen Daten.</string>
<string name="mode_system">Follow the system setting</string> <string name="gdpr_dialog_message"><![CDATA[Das Senden von Absturzberichten ist jetzt aktiviert. Die Funktion kann auf der Einstellungsseite deaktiviert werden, beachte aber bitte, dass Absturzberichte für die Anwendungsentwicklung von entscheidender Bedeutung sind.]]></string>
<string name="mode_light">Light mode</string> <string name="crash_toast_text">Die App ist abgestürzt. Details werden an den Entwickler gesendet.</string>
<string name="drawer_error_loading_sources">Error loading sources…</string> <string name="pref_switch_disable_acra">"Automatische Fehlerberichterstattung deaktivieren."</string>
<string name="pref_switch_enable_analytics">Enable analytics</string> <string name="menu_home_filter">Filter</string>
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string> <string name="application_selfoss_only">Diese App funktioniert nur mit einer Selfoss-Instanz, nicht mit einzelnen RSS-Feeds.</string>
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string> <string name="menu_home_sources">Quellen</string>
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string> <string name="update_source">Quelle aktualisieren</string>
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string> <string name="confirm_disconnect_title">Verbindung trennen?</string>
<string name="confirm_disconnect_description">Die Verbindung zur Selfoss-Instanz wird getrennt.</string>
<string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"Keine Einträge vorhanden"</string>
<string name="tab_new">"Neu"</string>
<string name="tab_read">"Alle"</string>
<string name="tab_favs">"Favoriten"</string>
<string name="action_about">"Über"</string>
<string name="marked_as_read">"Artikel gelesen"</string>
<string name="marked_as_unread">"Item unread"</string>
</resources> </resources>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<string name="app_name">"Lector para Selfoss"</string> <string name="app_name">"Lector para Selfoss"</string>
<string name="title_activity_login">"Iniciar sesión"</string> <string name="title_activity_login">"Iniciar sesión"</string>
<string name="prompt_password">"Contraseña"</string> <string name="prompt_password">"Contraseña"</string>
@ -7,10 +7,10 @@
<string name="error_invalid_password">"La contraseña no es suficientemente larga"</string> <string name="error_invalid_password">"La contraseña no es suficientemente larga"</string>
<string name="error_field_required">"Campo requerido"</string> <string name="error_field_required">"Campo requerido"</string>
<string name="prompt_url">"Url"</string> <string name="prompt_url">"Url"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"Inicio de sesión requerido ?"</string> <string name="withLoginSwitch">"Inicio de sesión requerido ?"</string>
<string name="login_url_problem">"Oops. Puede que necesite añadir un \"/\" al final de la url."</string> <string name="login_url_problem">"Oops. Puede que necesite añadir un \"/\" al final de la url."</string>
<string name="prompt_login">"Nombre de usuario"</string> <string name="prompt_login">"Nombre de usuario"</string>
<string name="label_share">"Compartir"</string>
<string name="readAll">"Leer todo"</string> <string name="readAll">"Leer todo"</string>
<string name="action_disconnect">"Desconectar"</string> <string name="action_disconnect">"Desconectar"</string>
<string name="title_activity_settings">"Configuración"</string> <string name="title_activity_settings">"Configuración"</string>
@ -23,19 +23,12 @@
<string name="wrong_infos">"Revise sus datos de nuevo."</string> <string name="wrong_infos">"Revise sus datos de nuevo."</string>
<string name="all_posts_not_read">"No todas las publicaciones fueron leídas"</string> <string name="all_posts_not_read">"No todas las publicaciones fueron leídas"</string>
<string name="all_posts_read">"Todas las publicaciones fueron leídas"</string> <string name="all_posts_read">"Todas las publicaciones fueron leídas"</string>
<string name="nothing_here">"Nada aquí"</string>
<string name="tab_new">"Nuevo"</string>
<string name="tab_read">"Todo"</string>
<string name="tab_favs">"Favoritos"</string>
<string name="action_about">"Acerca de"</string>
<string name="marked_as_read">"Artículo leído"</string>
<string name="marked_as_unread">"Artículo no leído"</string>
<string name="undo_string">"Deshacer"</string> <string name="undo_string">"Deshacer"</string>
<string name="addStringNoUrl">"Iniciar sesión para añadir fuentes."</string> <string name="addStringNoUrl">"Iniciar sesión para añadir fuentes."</string>
<string name="cant_get_sources">"No se puede obtener la lista de fuentes."</string> <string name="cant_get_sources">"No se puede obtener la lista de fuentes."</string>
<string name="cant_create_source">"No se puede crear la fuente."</string> <string name="cant_create_source">"No se puede crear la fuente."</string>
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string> <string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
<string name="cant_get_spouts">"Can't get spouts list. There may ben an api issue."</string> <string name="cant_get_spouts">"No se puede obtener la lista de fuentes."</string>
<string name="form_not_complete">"El formulario no está completo"</string> <string name="form_not_complete">"El formulario no está completo"</string>
<string name="pref_header_links">"Enlaces"</string> <string name="pref_header_links">"Enlaces"</string>
<string name="issue_tracker_link">"Rastreador de Incidencias"</string> <string name="issue_tracker_link">"Rastreador de Incidencias"</string>
@ -62,28 +55,18 @@
<string name="card_height_on">Altura de tarjetas se ajustará a su contenido</string> <string name="card_height_on">Altura de tarjetas se ajustará a su contenido</string>
<string name="card_height_off">Se fijará la altura de la tarjeta</string> <string name="card_height_off">Se fijará la altura de la tarjeta</string>
<string name="source_code">Código fuente</string> <string name="source_code">Código fuente</string>
<string name="drawer_error_loading_tags">Error al cargar etiquetas…</string> <string name="filter_item_tags">Etiquetas</string>
<string name="drawer_item_filters">Filtros</string> <string name="filter_item_sources">Fuentes</string>
<string name="drawer_action_clear">limpiar</string>
<string name="drawer_item_tags">Etiquetas</string>
<string name="drawer_item_sources">Fuentes</string>
<string name="drawer_action_edit">editar</string>
<string name="drawer_loading">Cargando…</string>
<string name="menu_home_search">Buscar</string> <string name="menu_home_search">Buscar</string>
<string name="can_delete_source">No se puede eliminar la fuente…</string> <string name="can_delete_source">No se puede eliminar la fuente…</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="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_dark_theme">Predeterminado/Oscuro</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="pref_general_infinite_loading_title">Cargar más artículos en desplazamiento</string> <string name="pref_general_infinite_loading_title">Cargar más artículos en desplazamiento</string>
<string name="translation">Traducción</string> <string name="translation">Traducción</string>
<string name="cant_open_invalid_url">La url del elemento no es válida. Estoy buscando resolver este problema para que la aplicación no colapse.</string> <string name="cant_open_invalid_url">La url del elemento no es válida. Estoy buscando resolver este problema para que la aplicación no colapse.</string>
<string name="drawer_report_bug">Reportar un error</string>
<string name="items_number_should_be_number">El número de artículos debe ser un número entero.</string> <string name="items_number_should_be_number">El número de artículos debe ser un número entero.</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_on">Marcar artículos como leidos al desplazarse entre ellos.</string> <string name="pref_switch_actions_pager_scroll_on">Marcar artículos como leidos al desplazarse entre ellos.</string>
@ -94,7 +77,6 @@
<string name="markall_dialog_message">Esto marcará todos los artículos como leídos.</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">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="pref_switch_actions_pager_scroll_off">No marcar artículos como leídos al deslizar con el dedo hacia los lados.</string>
<string name="drawer_item_hidden_tags">Etiquetas ocultas</string>
<string name="unmark">Marcar artículo como no leído</string> <string name="unmark">Marcar artículo como no leído</string>
<string name="pref_header_offline">Sin conexión y caché</string> <string name="pref_header_offline">Sin conexión y caché</string>
<string name="pref_switch_items_caching_off">Los artículos no se guardarán en la memoria del dispositivo y la aplicación no se podrá utilizar sin conexión.</string> <string name="pref_switch_items_caching_off">Los artículos no se guardarán en la memoria del dispositivo y la aplicación no se podrá utilizar sin conexión.</string>
@ -102,14 +84,14 @@
<string name="pref_switch_items_caching">Guardar elementos para uso sin conexión</string> <string name="pref_switch_items_caching">Guardar elementos para uso sin conexión</string>
<string name="pref_switch_update_sources">Check for new sources and tags</string> <string name="pref_switch_update_sources">Check for new sources and tags</string>
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string> <string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
<string name="network_connectivity_lost">"Network connection lost"</string> <string name="network_connectivity_lost">"Sin conexión!"</string>
<string name="network_connectivity_retrieved">"Network connection is now available"</string> <string name="network_connectivity_retrieved">"Network connection is now available"</string>
<string name="pref_switch_periodic_refresh">Sincronizar artículos</string> <string name="pref_switch_periodic_refresh">Sincronizar artículos</string>
<string name="pref_switch_periodic_refresh_off">Los artículos no se sincronizarán en segundo plano</string> <string name="pref_switch_periodic_refresh_off">Los artículos no se sincronizarán en segundo plano</string>
<string name="pref_switch_periodic_refresh_on">Los artículos se sincronizarán periódicamente</string> <string name="pref_switch_periodic_refresh_on">Los artículos se sincronizarán periódicamente</string>
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Intervalo de sincronización (>= 15 minutos)]]></string> <string name="pref_periodic_refresh_minutes_title"><![CDATA[Intervalo de sincronización (>= 15 minutos)]]></string>
<string name="pref_switch_refresh_when_charging">Sólo refrescar cuando el teléfono está cargando</string> <string name="pref_switch_refresh_when_charging">Sólo refrescar cuando el teléfono está cargando</string>
<string name="loading_notification_title">Cargando...</string> <string name="loading_notification_title">Cargando</string>
<string name="loading_notification_text">Selfoss está sincronizando tus artículos</string> <string name="loading_notification_text">Selfoss está sincronizando tus artículos</string>
<string name="notification_channel_sync">Notificación de sincronización</string> <string name="notification_channel_sync">Notificación de sincronización</string>
<string name="new_items_channel_sync">Notificación de elementos nuevos</string> <string name="new_items_channel_sync">Notificación de elementos nuevos</string>
@ -124,18 +106,27 @@
<string name="reader_text_align_left">Alinear a la izquierda</string> <string name="reader_text_align_left">Alinear a la izquierda</string>
<string name="reader_text_align_justify">Justificado</string> <string name="reader_text_align_justify">Justificado</string>
<string name="settings_reader_font">Modo lectura</string> <string name="settings_reader_font">Modo lectura</string>
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string> <string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string> <string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string> <string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string> <string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
<string name="pref_switch_enable_analytics">Enable analytics</string>
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string> <string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string> <string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string> <string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string> <string name="pref_switch_disable_acra">"Disable automatic bug reporting."</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"Nada aquí"</string>
<string name="tab_new">"Nuevo"</string>
<string name="tab_read">"Todo"</string>
<string name="tab_favs">"Favoritos"</string>
<string name="action_about">"Acerca de"</string>
<string name="marked_as_read">"Artículo leído"</string>
<string name="marked_as_unread">"Artículo no leído"</string>
</resources> </resources>

View File

@ -1,141 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<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="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="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="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="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="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="marked_as_unread">"Item unread"</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_no_network">"Can't get spouts list because of a network issue."</string>
<string name="cant_get_spouts">"Can't get spouts list. There may ben an api issue."</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="share">"Share"</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="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_article_viewer_title">"Open links inside the app"</string>
<string name="pref_article_viewer_on">"Articles will open inside the app"</string>
<string name="pref_article_viewer_off">"Articles will open with your default browser"</string>
<string name="pref_general_category_links">"Link handling"</string>
<string name="pref_general_category_displaying">"Displaying"</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="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="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="drawer_error_loading_tags">Error loading tags…</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="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="default_dark_theme">Default/Dark</string>
<string name="pref_selfoss_category">Selfoss Api</string>
<string name="pref_api_items_number_title">Loaded items number</string>
<string name="pref_hidden_tags">Hidden Tags</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_on">Mark articles as read when swiping between articles.</string>
<string name="add_to_favs_reader">Add to favorites</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="drawer_item_hidden_tags">Hidden Tags</string>
<string name="unmark">Mark item as unread</string>
<string name="pref_header_offline">Offline and cache</string>
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
<string name="pref_switch_items_caching">Save items for offline use</string>
<string name="pref_switch_update_sources">Check for new sources and tags</string>
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
<string name="network_connectivity_lost">"Network connection lost"</string>
<string name="network_connectivity_retrieved">"Network connection is now available"</string>
<string name="pref_switch_periodic_refresh">Sync articles</string>
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string>
<string name="pref_switch_refresh_when_charging">Only refresh when phone is charging</string>
<string name="loading_notification_title">Loading ...</string>
<string name="loading_notification_text">Selfoss is syncing your articles</string>
<string name="notification_channel_sync">Sync notification</string>
<string name="new_items_channel_sync">New items notification</string>
<string name="new_items_notification_title">New items !</string>
<string name="new_items_notification_text">%1$d new items loaded.</string>
<string name="pref_switch_notify_new_items">Notify on new items synced.</string>
<string name="shortcut_offline">Offline</string>
<string name="pref_api_timeout">Api Timeout</string>
<string name="pref_header_experimental">Experimental</string>
<string name="webview_dialog_issue_message">Webview not available. Disabling the article viewer to avoid any future crashes. Will load articles inside of your browser from now on.</string>
<string name="webview_dialog_issue_title">Webview issue</string>
<string name="reader_text_align_left">Align left</string>
<string name="reader_text_align_justify">Justify</string>
<string name="settings_reader_font">Reader font</string>
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
<string name="pref_switch_enable_analytics">Enable analytics</string>
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
</resources>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<string name="app_name">"Reader for Selfoss"</string> <string name="app_name">"Reader for Selfoss"</string>
<string name="title_activity_login">"Login"</string> <string name="title_activity_login">"Login"</string>
<string name="prompt_password">"Mot de passe"</string> <string name="prompt_password">"Mot de passe"</string>
@ -7,10 +7,10 @@
<string name="error_invalid_password">"Mot de passe trop court"</string> <string name="error_invalid_password">"Mot de passe trop court"</string>
<string name="error_field_required">"Champ requis"</string> <string name="error_field_required">"Champ requis"</string>
<string name="prompt_url">"Url Selfoss"</string> <string name="prompt_url">"Url Selfoss"</string>
<string name="disable_ssl">"Désactiver la vérification SSL"</string>
<string name="withLoginSwitch">"Avec login ?"</string> <string name="withLoginSwitch">"Avec login ?"</string>
<string name="login_url_problem">"Petit souci. Il manque peut-être un / à la fin ?"</string> <string name="login_url_problem">"Petit souci. Il manque peut-être un / à la fin ?"</string>
<string name="prompt_login">"Utilisateur"</string> <string name="prompt_login">"Utilisateur"</string>
<string name="label_share">"Partager"</string>
<string name="readAll">"Tout lire"</string> <string name="readAll">"Tout lire"</string>
<string name="action_disconnect">"Déconnecter"</string> <string name="action_disconnect">"Déconnecter"</string>
<string name="title_activity_settings">"Paramètres"</string> <string name="title_activity_settings">"Paramètres"</string>
@ -23,16 +23,9 @@
<string name="wrong_infos">"Vérifiez vos informations."</string> <string name="wrong_infos">"Vérifiez vos informations."</string>
<string name="all_posts_not_read">"Tous les posts n'ont pas été lus"</string> <string name="all_posts_not_read">"Tous les posts n'ont pas été lus"</string>
<string name="all_posts_read">"Tous les posts sont lus"</string> <string name="all_posts_read">"Tous les posts sont lus"</string>
<string name="nothing_here">"Il n'y a rien ici !"</string>
<string name="tab_new">"Non lus"</string>
<string name="tab_read">"Tous"</string>
<string name="tab_favs">"Favoris"</string>
<string name="action_about">"À propos"</string>
<string name="marked_as_read">"Marqué comme lu"</string>
<string name="marked_as_unread">"Marqué comme non lu"</string>
<string name="undo_string">"Annuler"</string> <string name="undo_string">"Annuler"</string>
<string name="addStringNoUrl">"Identifiez-vous pour ajouter une source."</string> <string name="addStringNoUrl">"Identifiez-vous pour ajouter une source."</string>
<string name="cant_get_sources">"Impossible de récupérer la liste des sources"</string> <string name="cant_get_sources">"Impossible de récupérer la liste des sources."</string>
<string name="cant_create_source">"Impossible de créer la source."</string> <string name="cant_create_source">"Impossible de créer la source."</string>
<string name="cant_get_spouts_no_network">"Impossible d'obtenir la liste des spouts en raison d'un problème de réseau."</string> <string name="cant_get_spouts_no_network">"Impossible d'obtenir la liste des spouts en raison d'un problème de réseau."</string>
<string name="cant_get_spouts">"Impossible d'obtenir la liste des spouts. Cela pourrait venir de l'api."</string> <string name="cant_get_spouts">"Impossible d'obtenir la liste des spouts. Cela pourrait venir de l'api."</string>
@ -43,13 +36,13 @@
<string name="warning_wrong_url">"ATTENTION"</string> <string name="warning_wrong_url">"ATTENTION"</string>
<string name="pref_switch_card_view_title">"Vue en carte"</string> <string name="pref_switch_card_view_title">"Vue en carte"</string>
<string name="share">"Partager"</string> <string name="share">"Partager"</string>
<string name="switch_unread_count">"Afficher le nombre d'articles non lus sur la barre en bas de l'écran"</string> <string name="switch_unread_count">"Afficher le nombre d'articles non lus sur la barre en bas de l'écran."</string>
<string name="switch_unread_count_title">"Afficher le nombre de non lus"</string> <string name="switch_unread_count_title">"Afficher le nombre de non lus"</string>
<string name="display_all_counts_title">"Afficher le nombre de favoris et d'articles lus"</string> <string name="display_all_counts_title">"Afficher le nombre de favoris et d'articles lus"</string>
<string name="text_wrong_url">"Vous semblez essayer de vous connecter avec une URL invalide. Assurez-vous que c'est la bonne, et si le problème persiste, contactez-moi via le lien du play store. Notez aussi que l'application ne peut fonctionner sans l'application web Selfoss. Vous ne pouvez pas utiliser l'application pour accéder directement aux flux RSS."</string> <string name="text_wrong_url">"Vous semblez essayer de vous connecter avec une URL invalide. Assurez-vous que c'est la bonne, et si le problème persiste, contactez-moi via le lien du play store. Notez aussi que l'application ne peut fonctionner sans l'application web Selfoss. Vous ne pouvez pas utiliser l'application pour accéder directement aux flux RSS."</string>
<string name="pref_article_viewer_title">"Ouvrir les liens dans l'application"</string> <string name="pref_article_viewer_title">"Ouvrir les liens dans l'application"</string>
<string name="pref_article_viewer_on">"Les articles s'ouvriront dans l'application"</string> <string name="pref_article_viewer_on">"Les articles s'ouvriront dans l'application"</string>
<string name="pref_article_viewer_off">"Les articles s'ouvriront dans votre naviguateur par défaut"</string> <string name="pref_article_viewer_off">"Les articles s'ouvriront dans votre navigateur par défaut"</string>
<string name="pref_general_category_links">"Gestion des liens"</string> <string name="pref_general_category_links">"Gestion des liens"</string>
<string name="pref_general_category_displaying">"Affichage"</string> <string name="pref_general_category_displaying">"Affichage"</string>
<string name="pref_switch_card_view_on">"Les articles seront affichés en forme de carte"</string> <string name="pref_switch_card_view_on">"Les articles seront affichés en forme de carte"</string>
@ -62,28 +55,18 @@
<string name="card_height_on">La taille de la carte s\'adaptera au contenu</string> <string name="card_height_on">La taille de la carte s\'adaptera au contenu</string>
<string name="card_height_off">La taille de la carte sera fixe</string> <string name="card_height_off">La taille de la carte sera fixe</string>
<string name="source_code">Code source</string> <string name="source_code">Code source</string>
<string name="drawer_error_loading_tags">Erreur lors du chargement des tags…</string> <string name="filter_item_tags">Tags</string>
<string name="drawer_item_filters">Filtres</string> <string name="filter_item_sources">Sources</string>
<string name="drawer_action_clear">raz</string>
<string name="drawer_item_tags">Tags</string>
<string name="drawer_item_sources">Sources</string>
<string name="drawer_action_edit">éditer</string>
<string name="drawer_loading">Chargement …</string>
<string name="menu_home_search">Rechercher</string> <string name="menu_home_search">Rechercher</string>
<string name="can_delete_source">Impossible de supprimer la source…</string> <string name="can_delete_source">Impossible de supprimer la source…</string>
<string name="base_url_error">Il y a eu un souci lors de la communication avec votre instance Selfoss. Si le problèmes persiste, contactez-moi pour trouver une solution.</string> <string name="base_url_error">Il y a eu un souci lors de la communication avec votre instance Selfoss. Si le problème persiste, contactez-moi pour trouver une solution.</string>
<string name="pref_header_theme">Thèmes</string> <string name="pref_header_theme">Thèmes</string>
<string name="default_theme">Par défaut</string>
<string name="default_dark_theme">Par défaut/Foncé</string>
<string name="pref_selfoss_category">Api Selfoss</string> <string name="pref_selfoss_category">Api Selfoss</string>
<string name="pref_api_items_number_title">Nombre d\'articles chargés</string> <string name="pref_api_items_number_title">Nombre d\'articles chargés</string>
<string name="pref_hidden_tags">Tags Cachés</string>
<string name="pref_general_infinite_loading_title">Charger plus d\'articles au scroll</string> <string name="pref_general_infinite_loading_title">Charger plus d\'articles au scroll</string>
<string name="translation">Traduction</string> <string name="translation">Traduction</string>
<string name="cant_open_invalid_url">Lurl de lélément nest pas valide. En attendant la résolution du problème, le lien ne s\'ouvrira pas.</string> <string name="cant_open_invalid_url">Lurl de lélément nest pas valide. En attendant la résolution du problème, le lien ne s\'ouvrira pas.</string>
<string name="drawer_report_bug">Signaler un bug</string>
<string name="items_number_should_be_number">Le nombre d\'articles doit être un entier.</string> <string name="items_number_should_be_number">Le nombre d\'articles doit être un entier.</string>
<string name="reader_action_more">Lire plus</string>
<string name="reader_action_open">Ouvrir</string> <string name="reader_action_open">Ouvrir</string>
<string name="reader_action_share">Partager</string> <string name="reader_action_share">Partager</string>
<string name="pref_switch_actions_pager_scroll_on">Marquer les articles comme lus à la navigation dans le lecteur d\'article.</string> <string name="pref_switch_actions_pager_scroll_on">Marquer les articles comme lus à la navigation dans le lecteur d\'article.</string>
@ -92,9 +75,8 @@
<string name="pref_header_viewer">Lecteur d\'articles</string> <string name="pref_header_viewer">Lecteur d\'articles</string>
<string name="refresh_dialog_message">En validant, votre instance Selfoss sera mise à jour.</string> <string name="refresh_dialog_message">En validant, votre instance Selfoss sera mise à jour.</string>
<string name="markall_dialog_message">Marquer tous les éléments comme lus ?</string> <string name="markall_dialog_message">Marquer tous les éléments comme lus ?</string>
<string name="pref_switch_actions_pager_scroll">Marquer comme lu à la navigation.</string> <string name="pref_switch_actions_pager_scroll">Marquer comme lu à la navigation</string>
<string name="pref_switch_actions_pager_scroll_off">Ne pas marquer les articles comme lus à la navigation.</string> <string name="pref_switch_actions_pager_scroll_off">Ne pas marquer les articles comme lus à la navigation.</string>
<string name="drawer_item_hidden_tags">Tags Cachés</string>
<string name="unmark">Marquer l\'article comme non lu</string> <string name="unmark">Marquer l\'article comme non lu</string>
<string name="pref_header_offline">Hors ligne et cache</string> <string name="pref_header_offline">Hors ligne et cache</string>
<string name="pref_switch_items_caching_off">Les articles ne seront pas enregistrés et l\'application ne sera pas utilisable hors ligne.</string> <string name="pref_switch_items_caching_off">Les articles ne seront pas enregistrés et l\'application ne sera pas utilisable hors ligne.</string>
@ -105,11 +87,11 @@
<string name="network_connectivity_lost">"Connexion au réseau perdue"</string> <string name="network_connectivity_lost">"Connexion au réseau perdue"</string>
<string name="network_connectivity_retrieved">"Connexion réseau de nouveau disponible"</string> <string name="network_connectivity_retrieved">"Connexion réseau de nouveau disponible"</string>
<string name="pref_switch_periodic_refresh">Synchroniser les articles</string> <string name="pref_switch_periodic_refresh">Synchroniser les articles</string>
<string name="pref_switch_periodic_refresh_off">Les articles ne seront pas synchronisés en arrière plan</string> <string name="pref_switch_periodic_refresh_off">Les articles ne seront pas synchronisés en arrière-plan</string>
<string name="pref_switch_periodic_refresh_on">Articles seront périodiquement synchronisées</string> <string name="pref_switch_periodic_refresh_on">Articles seront périodiquement synchronisés</string>
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Interval de synchronisation ( >= 15 minutes)]]></string> <string name="pref_periodic_refresh_minutes_title"><![CDATA[Intervalle de synchronisation ( >= 15 minutes)]]></string>
<string name="pref_switch_refresh_when_charging">Synchroniser uniquement lorsque le téléphone est en charge</string> <string name="pref_switch_refresh_when_charging">Synchroniser uniquement lorsque le téléphone est en charge</string>
<string name="loading_notification_title">Chargement ...</string> <string name="loading_notification_title">Chargement </string>
<string name="loading_notification_text">Selfoss synchronise vos articles</string> <string name="loading_notification_text">Selfoss synchronise vos articles</string>
<string name="notification_channel_sync">Notification de synchronisation</string> <string name="notification_channel_sync">Notification de synchronisation</string>
<string name="new_items_channel_sync">Notification de nouveaux articles</string> <string name="new_items_channel_sync">Notification de nouveaux articles</string>
@ -119,23 +101,32 @@
<string name="shortcut_offline">Hors ligne</string> <string name="shortcut_offline">Hors ligne</string>
<string name="pref_api_timeout">Timeout de l\'api</string> <string name="pref_api_timeout">Timeout de l\'api</string>
<string name="pref_header_experimental">Expérimental</string> <string name="pref_header_experimental">Expérimental</string>
<string name="webview_dialog_issue_message">Pas de Webview disponible. Désactivation du lecteur d\'article pour éviter les futures erreurs. Les articles seront lu via votre navigateur à l\'avenir.</string> <string name="webview_dialog_issue_message">Pas de Webview disponible. Désactivation du lecteur d\'article pour éviter les futures erreurs. Les articles seront lus via votre navigateur à l\'avenir.</string>
<string name="webview_dialog_issue_title">Problème de Webview</string> <string name="webview_dialog_issue_title">Problème de Webview</string>
<string name="reader_text_align_left">Aligner à gauche</string> <string name="reader_text_align_left">Aligner à gauche</string>
<string name="reader_text_align_justify">Justifier le texte</string> <string name="reader_text_align_justify">Justifier le texte</string>
<string name="settings_reader_font">Police du lecteur d\'articles</string> <string name="settings_reader_font">Police du lecteur d\'articles</string>
<string name="reader_static_bar_title">Barre statique pour le visionneur d\'articles</string>
<string name="reader_static_bar_on">La barre sera affichée</string>
<string name="reader_static_bar_off">La barre sera affichée grâce au bouton</string>
<string name="remove_source">Supprimer la source</string> <string name="remove_source">Supprimer la source</string>
<string name="pref_theme_title">Thème Clair/Sombre</string> <string name="pref_theme_title">Thème Clair/Sombre</string>
<string name="mode_dark">Thème sombre</string> <string name="mode_dark">Thème sombre</string>
<string name="mode_system">Utiliser les paramètres système</string> <string name="mode_system">Utiliser les paramètres système</string>
<string name="mode_light">Thème clair</string> <string name="mode_light">Thème clair</string>
<string name="drawer_error_loading_sources">Error loading sources…</string> <string name="gdpr_dialog_title">L\'application ne partage aucune information personnelle.</string>
<string name="pref_switch_enable_analytics">Enable analytics</string> <string name="gdpr_dialog_message"><![CDATA[Le rapport de plantage est activés par défaut. Il peut être désactivé depuis les paramètres de l\'application. Notez que les rapports de plantage sont essentiels pour le développement de l\'application.]]></string>
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string> <string name="crash_toast_text">Un bug s\'est produit. Le développeur en sera informé.</string>
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string> <string name="pref_switch_disable_acra">"Désactiver les rapports de plantage."</string>
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string> <string name="menu_home_filter">Filtres</string>
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string> <string name="application_selfoss_only">Cette application ne fonctionne qu\'avec l\'api de Selfoss, et aucun autre.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Mise à jour des sources</string>
<string name="confirm_disconnect_title">Se déconnecter ?</string>
<string name="confirm_disconnect_description">Vous allez être déconnecté de votre instance Selfoss.</string>
<string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"Il n'y a rien ici !"</string>
<string name="tab_new">"Non lus"</string>
<string name="tab_read">"Tous"</string>
<string name="tab_favs">"Favoris"</string>
<string name="action_about">"À propos"</string>
<string name="marked_as_read">"Marqué comme lu"</string>
<string name="marked_as_unread">"Marqué comme non lu"</string>
</resources> </resources>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<string name="app_name">"Lector para selfoss"</string> <string name="app_name">"Lector para selfoss"</string>
<string name="title_activity_login">"Conectar"</string> <string name="title_activity_login">"Conectar"</string>
<string name="prompt_password">"Contrasinal"</string> <string name="prompt_password">"Contrasinal"</string>
@ -7,10 +7,10 @@
<string name="error_invalid_password">"O contrasinal non é suficientemente longo"</string> <string name="error_invalid_password">"O contrasinal non é suficientemente longo"</string>
<string name="error_field_required">"Campo requirido"</string> <string name="error_field_required">"Campo requirido"</string>
<string name="prompt_url">"URL"</string> <string name="prompt_url">"URL"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"É preciso iniciar sesión?"</string> <string name="withLoginSwitch">"É preciso iniciar sesión?"</string>
<string name="login_url_problem">"Ups! Pode que precises engadir un \"/\" o final da URL."</string> <string name="login_url_problem">"Ups! Pode que precises engadir un \"/\" o final da URL."</string>
<string name="prompt_login">"Nome de usuario"</string> <string name="prompt_login">"Nome de usuario"</string>
<string name="label_share">"Compartir"</string>
<string name="readAll">"Ler todos"</string> <string name="readAll">"Ler todos"</string>
<string name="action_disconnect">"Desconectar"</string> <string name="action_disconnect">"Desconectar"</string>
<string name="title_activity_settings">"Axustes"</string> <string name="title_activity_settings">"Axustes"</string>
@ -23,19 +23,12 @@
<string name="wrong_infos">"Comprobar os teus detalles de novo."</string> <string name="wrong_infos">"Comprobar os teus detalles de novo."</string>
<string name="all_posts_not_read">"Non se leron todas as publicacións"</string> <string name="all_posts_not_read">"Non se leron todas as publicacións"</string>
<string name="all_posts_read">"Leronse todas as publicacións"</string> <string name="all_posts_read">"Leronse todas as publicacións"</string>
<string name="nothing_here">"Non hai nada aquí"</string>
<string name="tab_new">"Novo"</string>
<string name="tab_read">"Todos"</string>
<string name="tab_favs">"Favoritos"</string>
<string name="action_about">"Acerca de"</string>
<string name="marked_as_read">"Elemento lido"</string>
<string name="marked_as_unread">"Elemento non lido"</string>
<string name="undo_string">"Desfacer"</string> <string name="undo_string">"Desfacer"</string>
<string name="addStringNoUrl">"Accede pra engadir fontes."</string> <string name="addStringNoUrl">"Accede pra engadir fontes."</string>
<string name="cant_get_sources">"Non se pode obter a lista de fontes."</string> <string name="cant_get_sources">"Non se pode obter a lista de fontes."</string>
<string name="cant_create_source">"Non se pode crear unha fonte."</string> <string name="cant_create_source">"Non se pode crear unha fonte."</string>
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string> <string name="cant_get_spouts_no_network">"Non se pode obter a lista de spouts por mor dun erro de rede."</string>
<string name="cant_get_spouts">"Can't get spouts list. There may ben an api issue."</string> <string name="cant_get_spouts">"Non se pode obter a lista de spoits. Pode que haxa algún problema coa api."</string>
<string name="form_not_complete">"O formulario non está completo"</string> <string name="form_not_complete">"O formulario non está completo"</string>
<string name="pref_header_links">"Ligazóns"</string> <string name="pref_header_links">"Ligazóns"</string>
<string name="issue_tracker_link">"Rastrexador de Incidencias"</string> <string name="issue_tracker_link">"Rastrexador de Incidencias"</string>
@ -62,28 +55,18 @@
<string name="card_height_on">A altura das tarxetas axustarase ao seu contido</string> <string name="card_height_on">A altura das tarxetas axustarase ao seu contido</string>
<string name="card_height_off">A altura das tarxetas será fixa</string> <string name="card_height_off">A altura das tarxetas será fixa</string>
<string name="source_code">Código fonte</string> <string name="source_code">Código fonte</string>
<string name="drawer_error_loading_tags">Produciuse un erro ao cargar as etiquetas…</string> <string name="filter_item_tags">Etiquetas</string>
<string name="drawer_item_filters">Filtros</string> <string name="filter_item_sources">Fontes</string>
<string name="drawer_action_clear">limpar</string>
<string name="drawer_item_tags">Etiquetas</string>
<string name="drawer_item_sources">Fontes</string>
<string name="drawer_action_edit">editar</string>
<string name="drawer_loading">Cargando…</string>
<string name="menu_home_search">Procurar</string> <string name="menu_home_search">Procurar</string>
<string name="can_delete_source">Non se puido eliminar a fonte…</string> <string name="can_delete_source">Non se puido eliminar a fonte…</string>
<string name="base_url_error">Houno unha incidencia ao tratar de comunicarse coa túa instancia de Selfoss. Se o problema persiste, prégolle que se poña en contacto conmigo.</string> <string name="base_url_error">Houno unha incidencia ao tratar de comunicarse coa túa instancia de Selfoss. Se o problema persiste, prégolle que se poña 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_dark_theme">Predeterminado/Escuro</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 elementos cargados</string> <string name="pref_api_items_number_title">Número de elementos cargados</string>
<string name="pref_hidden_tags">Etiquetas ocultas</string>
<string name="pref_general_infinite_loading_title">Cargar máis artigos ao desprazarse</string> <string name="pref_general_infinite_loading_title">Cargar máis artigos ao desprazarse</string>
<string name="translation">Traducción</string> <string name="translation">Traducción</string>
<string name="cant_open_invalid_url">A URL do elemento non é válida. Estou tratando de solucionar isto pra que a aplicación non falle.</string> <string name="cant_open_invalid_url">A URL do elemento non é válida. Estou tratando de solucionar isto pra que a aplicación non falle.</string>
<string name="drawer_report_bug">Informar dun erro</string>
<string name="items_number_should_be_number">O número de elementos debería ser un enteiro.</string> <string name="items_number_should_be_number">O número de elementos debería ser un enteiro.</string>
<string name="reader_action_more">Ler máis</string>
<string name="reader_action_open">Abrir no navegador</string> <string name="reader_action_open">Abrir no navegador</string>
<string name="reader_action_share">Compartir</string> <string name="reader_action_share">Compartir</string>
<string name="pref_switch_actions_pager_scroll_on">Marcar artigos como lidos cando se desliza o dedo dun a outro.</string> <string name="pref_switch_actions_pager_scroll_on">Marcar artigos como lidos cando se desliza o dedo dun a outro.</string>
@ -94,7 +77,6 @@
<string name="markall_dialog_message">Isto marcara todos os elementos como lidos.</string> <string name="markall_dialog_message">Isto marcara todos os elementos como lidos.</string>
<string name="pref_switch_actions_pager_scroll">Marcar artigos como lidos ao deslizar co dedo cara os lados</string> <string name="pref_switch_actions_pager_scroll">Marcar artigos como lidos ao deslizar co dedo cara os lados</string>
<string name="pref_switch_actions_pager_scroll_off">Non marcar artigos como lidos ao deslizar co dedo cara os lados.</string> <string name="pref_switch_actions_pager_scroll_off">Non marcar artigos como lidos ao deslizar co dedo cara os lados.</string>
<string name="drawer_item_hidden_tags">Etiquetas ocultas</string>
<string name="unmark">Marcar artículo como non lido</string> <string name="unmark">Marcar artículo como non lido</string>
<string name="pref_header_offline">Sen conexión e caché</string> <string name="pref_header_offline">Sen conexión e caché</string>
<string name="pref_switch_items_caching_off">Os artigos non se gardaran na memoria do dispositivo e non se poderá utilizar a aplicación sen conexión.</string> <string name="pref_switch_items_caching_off">Os artigos non se gardaran na memoria do dispositivo e non se poderá utilizar a aplicación sen conexión.</string>
@ -109,7 +91,7 @@
<string name="pref_switch_periodic_refresh_on">Os artigos sincronizaranse periódicamente</string> <string name="pref_switch_periodic_refresh_on">Os artigos sincronizaranse periódicamente</string>
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Intervalo de sincronización (>= 15 minutos)]]></string> <string name="pref_periodic_refresh_minutes_title"><![CDATA[Intervalo de sincronización (>= 15 minutos)]]></string>
<string name="pref_switch_refresh_when_charging">Só refrescar cando o teléfono se está a cargar</string> <string name="pref_switch_refresh_when_charging">Só refrescar cando o teléfono se está a cargar</string>
<string name="loading_notification_title">Cargando...</string> <string name="loading_notification_title">Cargando</string>
<string name="loading_notification_text">Selfoss está sincronizando os teus ar tigos</string> <string name="loading_notification_text">Selfoss está sincronizando os teus ar tigos</string>
<string name="notification_channel_sync">Notificación de sincronización</string> <string name="notification_channel_sync">Notificación de sincronización</string>
<string name="new_items_channel_sync">Notificación de actualizacións</string> <string name="new_items_channel_sync">Notificación de actualizacións</string>
@ -124,18 +106,27 @@
<string name="reader_text_align_left">Aliñar á esquerda</string> <string name="reader_text_align_left">Aliñar á esquerda</string>
<string name="reader_text_align_justify">Xustificado</string> <string name="reader_text_align_justify">Xustificado</string>
<string name="settings_reader_font">Modo lector</string> <string name="settings_reader_font">Modo lector</string>
<string name="reader_static_bar_title">Barra inferior estática na vista de artigos</string>
<string name="reader_static_bar_on">A barra inferior mostrarase sempre</string>
<string name="reader_static_bar_off">A barra inferior pode mostrarse a través do botón flotante</string>
<string name="remove_source">Eliminar fonte</string> <string name="remove_source">Eliminar fonte</string>
<string name="pref_theme_title">Light/Dark mode</string> <string name="pref_theme_title">Modo Claro/Escuro</string>
<string name="mode_dark">Dark mode</string> <string name="mode_dark">Modo escuro</string>
<string name="mode_system">Follow the system setting</string> <string name="mode_system">Seguir axustes do sistema</string>
<string name="mode_light">Light mode</string> <string name="mode_light">Modo claro</string>
<string name="drawer_error_loading_sources">Error loading sources…</string> <string name="gdpr_dialog_title">A aplicación non comparte ningún dato persoal seu.</string>
<string name="pref_switch_enable_analytics">Enable analytics</string> <string name="gdpr_dialog_message"><![CDATA[O envío de informes de erros está habilitado. Pode deshabilitarse dende a páxina de axustes. Ten en conta que os informes de erros son esenciais para o desenvolvemento da aplicación.]]></string>
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string> <string name="crash_toast_text">Ocurriu un erro. Enviando os detalles o desenvolvedor.</string>
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string> <string name="pref_switch_disable_acra">"Deshabilitar o reporte automático de erros."</string>
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string> <string name="menu_home_filter">Filtros</string>
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string> <string name="application_selfoss_only">Esta aplicación só funciona cunha instancia de Selfoss, e con ningún outro filtro RSS.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"Non hai nada aquí"</string>
<string name="tab_new">"Novo"</string>
<string name="tab_read">"Todos"</string>
<string name="tab_favs">"Favoritos"</string>
<string name="action_about">"Acerca de"</string>
<string name="marked_as_read">"Elemento lido"</string>
<string name="marked_as_unread">"Elemento non lido"</string>
</resources> </resources>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<string name="app_name">"Reader for Selfoss"</string> <string name="app_name">"Reader for Selfoss"</string>
<string name="title_activity_login">"Masuk"</string> <string name="title_activity_login">"Masuk"</string>
<string name="prompt_password">"Kata sandi"</string> <string name="prompt_password">"Kata sandi"</string>
@ -7,10 +7,10 @@
<string name="error_invalid_password">"Kata sandinya tidak cukup panjang"</string> <string name="error_invalid_password">"Kata sandinya tidak cukup panjang"</string>
<string name="error_field_required">"Kolom wajib diisi"</string> <string name="error_field_required">"Kolom wajib diisi"</string>
<string name="prompt_url">"URL"</string> <string name="prompt_url">"URL"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"Harus masuk?"</string> <string name="withLoginSwitch">"Harus masuk?"</string>
<string name="login_url_problem">"Ups. Anda mungkin harus menambahkan \"/\" di akhir url."</string> <string name="login_url_problem">"Ups. Anda mungkin harus menambahkan \"/\" di akhir url."</string>
<string name="prompt_login">"Nama pengguna"</string> <string name="prompt_login">"Nama pengguna"</string>
<string name="label_share">"Bagikan"</string>
<string name="readAll">"Baca semua"</string> <string name="readAll">"Baca semua"</string>
<string name="action_disconnect">"Putuskan sambungan"</string> <string name="action_disconnect">"Putuskan sambungan"</string>
<string name="title_activity_settings">"Pengaturan"</string> <string name="title_activity_settings">"Pengaturan"</string>
@ -23,19 +23,12 @@
<string name="wrong_infos">"Periksa kembali detail Anda."</string> <string name="wrong_infos">"Periksa kembali detail Anda."</string>
<string name="all_posts_not_read">"Semua pos belum dibaca"</string> <string name="all_posts_not_read">"Semua pos belum dibaca"</string>
<string name="all_posts_read">"Semua pos sudah dibaca"</string> <string name="all_posts_read">"Semua pos sudah dibaca"</string>
<string name="nothing_here">"Tidak ada di sini"</string>
<string name="tab_new">"Baru"</string>
<string name="tab_read">"Semua"</string>
<string name="tab_favs">"Favorit"</string>
<string name="action_about">"Tentang"</string>
<string name="marked_as_read">"Membaca item"</string>
<string name="marked_as_unread">"Item unread"</string>
<string name="undo_string">"Urung"</string> <string name="undo_string">"Urung"</string>
<string name="addStringNoUrl">"Masuk untuk menambah sumber."</string> <string name="addStringNoUrl">"Masuk untuk menambah sumber."</string>
<string name="cant_get_sources">"Tidak bisa mendapatkan daftar sumber."</string> <string name="cant_get_sources">"Tidak bisa mendapatkan daftar sumber."</string>
<string name="cant_create_source">"Tidak dapat membuat sumber."</string> <string name="cant_create_source">"Tidak dapat membuat sumber."</string>
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string> <string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
<string name="cant_get_spouts">"Can't get spouts list. There may ben an api issue."</string> <string name="cant_get_spouts">"Tidak bisa masuk ke daftar Spouts."</string>
<string name="form_not_complete">"Formulirnya belum selesai"</string> <string name="form_not_complete">"Formulirnya belum selesai"</string>
<string name="pref_header_links">"Tautan"</string> <string name="pref_header_links">"Tautan"</string>
<string name="issue_tracker_link">"Pelacak Masalah"</string> <string name="issue_tracker_link">"Pelacak Masalah"</string>
@ -62,28 +55,18 @@
<string name="card_height_on">Tinggi kartu akan disesuaikan dengan konten</string> <string name="card_height_on">Tinggi kartu akan disesuaikan dengan konten</string>
<string name="card_height_off">Ukuran kartu akan tetap</string> <string name="card_height_off">Ukuran kartu akan tetap</string>
<string name="source_code">Kode sumber</string> <string name="source_code">Kode sumber</string>
<string name="drawer_error_loading_tags">Kesalahan saat memuat tag…</string> <string name="filter_item_tags">Tag</string>
<string name="drawer_item_filters">Filter</string> <string name="filter_item_sources">Sumber</string>
<string name="drawer_action_clear">kosongkan</string>
<string name="drawer_item_tags">Tag</string>
<string name="drawer_item_sources">Sumber</string>
<string name="drawer_action_edit">suntung</string>
<string name="drawer_loading">Memuat …</string>
<string name="menu_home_search">Cari</string> <string name="menu_home_search">Cari</string>
<string name="can_delete_source">Tidak dapat menghapus sumber…</string> <string name="can_delete_source">Tidak dapat menghapus sumber…</string>
<string name="base_url_error">Ada masalah saat berkomunikasi dengan Selfoss Anda. Jika masalah berlanjut, tolong hubungi saya.</string> <string name="base_url_error">Ada masalah saat berkomunikasi dengan Selfoss Anda. Jika masalah berlanjut, tolong hubungi saya.</string>
<string name="pref_header_theme">Tema</string> <string name="pref_header_theme">Tema</string>
<string name="default_theme">Bawaan</string>
<string name="default_dark_theme">Bawaan/Gelap</string>
<string name="pref_selfoss_category">Selfoss Api</string> <string name="pref_selfoss_category">Selfoss Api</string>
<string name="pref_api_items_number_title">Item nomor dimuat</string> <string name="pref_api_items_number_title">Item nomor dimuat</string>
<string name="pref_hidden_tags">Hidden Tags</string>
<string name="pref_general_infinite_loading_title">Muat lebih banyak artikel saat membalik halaman</string> <string name="pref_general_infinite_loading_title">Muat lebih banyak artikel saat membalik halaman</string>
<string name="translation">Terjemahan</string> <string name="translation">Terjemahan</string>
<string name="cant_open_invalid_url">Alamat tautan proyek tidak valid. Saya mencoba memecahkan masalah ini untuk menghindari aplikasi berhenti.</string> <string name="cant_open_invalid_url">Alamat tautan proyek tidak valid. Saya mencoba memecahkan masalah ini untuk menghindari aplikasi berhenti.</string>
<string name="drawer_report_bug">Laporkan bug</string>
<string name="items_number_should_be_number">Jumlah item harus berupa bilangan bulat.</string> <string name="items_number_should_be_number">Jumlah item harus berupa bilangan bulat.</string>
<string name="reader_action_more">Baca lebih lanjut</string>
<string name="reader_action_open">Buka di peramban</string> <string name="reader_action_open">Buka di peramban</string>
<string name="reader_action_share">Bagikan</string> <string name="reader_action_share">Bagikan</string>
<string name="pref_switch_actions_pager_scroll_on">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>
@ -94,7 +77,6 @@
<string name="markall_dialog_message">This will mark all the items as read.</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">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="pref_switch_actions_pager_scroll_off">Don\'t mark articles as read when swiping.</string>
<string name="drawer_item_hidden_tags">Hidden Tags</string>
<string name="unmark">Mark item as unread</string> <string name="unmark">Mark item as unread</string>
<string name="pref_header_offline">Offline and cache</string> <string name="pref_header_offline">Offline and cache</string>
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string> <string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
@ -102,14 +84,14 @@
<string name="pref_switch_items_caching">Save items for offline use</string> <string name="pref_switch_items_caching">Save items for offline use</string>
<string name="pref_switch_update_sources">Check for new sources and tags</string> <string name="pref_switch_update_sources">Check for new sources and tags</string>
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string> <string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
<string name="network_connectivity_lost">"Network connection lost"</string> <string name="network_connectivity_lost">"Koneksi jaringan hilang"</string>
<string name="network_connectivity_retrieved">"Network connection is now available"</string> <string name="network_connectivity_retrieved">"Network connection is now available"</string>
<string name="pref_switch_periodic_refresh">Sync articles</string> <string name="pref_switch_periodic_refresh">Sync articles</string>
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string> <string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string> <string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string> <string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string>
<string name="pref_switch_refresh_when_charging">Only refresh when phone is charging</string> <string name="pref_switch_refresh_when_charging">Only refresh when phone is charging</string>
<string name="loading_notification_title">Loading ...</string> <string name="loading_notification_title">Loading </string>
<string name="loading_notification_text">Selfoss is syncing your articles</string> <string name="loading_notification_text">Selfoss is syncing your articles</string>
<string name="notification_channel_sync">Sync notification</string> <string name="notification_channel_sync">Sync notification</string>
<string name="new_items_channel_sync">New items notification</string> <string name="new_items_channel_sync">New items notification</string>
@ -124,18 +106,27 @@
<string name="reader_text_align_left">Align left</string> <string name="reader_text_align_left">Align left</string>
<string name="reader_text_align_justify">Justify</string> <string name="reader_text_align_justify">Justify</string>
<string name="settings_reader_font">Reader font</string> <string name="settings_reader_font">Reader font</string>
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string> <string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string> <string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string> <string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string> <string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
<string name="pref_switch_enable_analytics">Enable analytics</string>
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string> <string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string> <string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string> <string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string> <string name="pref_switch_disable_acra">"Disable automatic bug reporting."</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"Tidak ada di sini"</string>
<string name="tab_new">"Baru"</string>
<string name="tab_read">"Semua"</string>
<string name="tab_favs">"Favorit"</string>
<string name="action_about">"Tentang"</string>
<string name="marked_as_read">"Membaca item"</string>
<string name="marked_as_unread">"Item unread"</string>
</resources> </resources>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<string name="app_name">"Lettore RSS per Selfoss"</string> <string name="app_name">"Lettore RSS per Selfoss"</string>
<string name="title_activity_login">"Accedi"</string> <string name="title_activity_login">"Accedi"</string>
<string name="prompt_password">"Password"</string> <string name="prompt_password">"Password"</string>
@ -7,10 +7,10 @@
<string name="error_invalid_password">"La password non è sufficientemente lunga"</string> <string name="error_invalid_password">"La password non è sufficientemente lunga"</string>
<string name="error_field_required">"Campo obbligatorio"</string> <string name="error_field_required">"Campo obbligatorio"</string>
<string name="prompt_url">"URL"</string> <string name="prompt_url">"URL"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"È richiesto l'accesso?"</string> <string name="withLoginSwitch">"È richiesto l'accesso?"</string>
<string name="login_url_problem">"Oops. Potrebbe essere necessario aggiungere un \"/\" alla fine dell'url."</string> <string name="login_url_problem">"Oops. Potrebbe essere necessario aggiungere un \"/\" alla fine dell'url."</string>
<string name="prompt_login">"Nome utente"</string> <string name="prompt_login">"Nome utente"</string>
<string name="label_share">"Condividi"</string>
<string name="readAll">"Segna tutte come lette"</string> <string name="readAll">"Segna tutte come lette"</string>
<string name="action_disconnect">"Scollegati"</string> <string name="action_disconnect">"Scollegati"</string>
<string name="title_activity_settings">"Impostazioni"</string> <string name="title_activity_settings">"Impostazioni"</string>
@ -23,13 +23,6 @@
<string name="wrong_infos">"Controlla nuovamente i dati."</string> <string name="wrong_infos">"Controlla nuovamente i dati."</string>
<string name="all_posts_not_read">"All posts weren't read"</string> <string name="all_posts_not_read">"All posts weren't read"</string>
<string name="all_posts_read">"Tutti i messaggi sono stati letti"</string> <string name="all_posts_read">"Tutti i messaggi sono stati letti"</string>
<string name="nothing_here">"Non c'è niente qui"</string>
<string name="tab_new">"Nuovi"</string>
<string name="tab_read">"Tutti"</string>
<string name="tab_favs">"Preferiti"</string>
<string name="action_about">"Informazioni"</string>
<string name="marked_as_read">"Articolo letto"</string>
<string name="marked_as_unread">"Item unread"</string>
<string name="undo_string">"Annulla"</string> <string name="undo_string">"Annulla"</string>
<string name="addStringNoUrl">"Autenticati per aggiungere fonti."</string> <string name="addStringNoUrl">"Autenticati per aggiungere fonti."</string>
<string name="cant_get_sources">"Can't get sources list."</string> <string name="cant_get_sources">"Can't get sources list."</string>
@ -62,28 +55,18 @@
<string name="card_height_on">Cards height will adjust to its content</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="card_height_off">Card height will be fixed</string>
<string name="source_code">Codice sorgente</string> <string name="source_code">Codice sorgente</string>
<string name="drawer_error_loading_tags">Errore nel caricamento dei tag…</string> <string name="filter_item_tags">Tags</string>
<string name="drawer_item_filters">Filtri</string> <string name="filter_item_sources">Fonti</string>
<string name="drawer_action_clear">cancella</string>
<string name="drawer_item_tags">Tags</string>
<string name="drawer_item_sources">Fonti</string>
<string name="drawer_action_edit">modifica</string>
<string name="drawer_loading">Caricamento…</string>
<string name="menu_home_search">Cerca</string> <string name="menu_home_search">Cerca</string>
<string name="can_delete_source">Non è possibile eliminare la fonte…</string> <string name="can_delete_source">Non è possibile eliminare la fonte…</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="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">Temi</string> <string name="pref_header_theme">Temi</string>
<string name="default_theme">Predefinito</string>
<string name="default_dark_theme">Predefinito (Scuro)</string>
<string name="pref_selfoss_category">Api di Selfoss</string> <string name="pref_selfoss_category">Api di Selfoss</string>
<string name="pref_api_items_number_title">Numero di elementi caricati</string> <string name="pref_api_items_number_title">Numero di elementi caricati</string>
<string name="pref_hidden_tags">Tag nascosti</string>
<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">Traduzioni</string> <string name="translation">Traduzioni</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">Segnala un bug</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_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_on">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>
@ -94,7 +77,6 @@
<string name="markall_dialog_message">This will mark all the items as read.</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">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="pref_switch_actions_pager_scroll_off">Don\'t mark articles as read when swiping.</string>
<string name="drawer_item_hidden_tags">Hidden Tags</string>
<string name="unmark">Segna come non letto</string> <string name="unmark">Segna come non letto</string>
<string name="pref_header_offline">Offline and cache</string> <string name="pref_header_offline">Offline and cache</string>
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string> <string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
@ -109,7 +91,7 @@
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string> <string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string> <string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string>
<string name="pref_switch_refresh_when_charging">Only refresh when phone is charging</string> <string name="pref_switch_refresh_when_charging">Only refresh when phone is charging</string>
<string name="loading_notification_title">Loading ...</string> <string name="loading_notification_title">Loading </string>
<string name="loading_notification_text">Selfoss is syncing your articles</string> <string name="loading_notification_text">Selfoss is syncing your articles</string>
<string name="notification_channel_sync">Sync notification</string> <string name="notification_channel_sync">Sync notification</string>
<string name="new_items_channel_sync">New items notification</string> <string name="new_items_channel_sync">New items notification</string>
@ -124,18 +106,27 @@
<string name="reader_text_align_left">Align left</string> <string name="reader_text_align_left">Align left</string>
<string name="reader_text_align_justify">Justify</string> <string name="reader_text_align_justify">Justify</string>
<string name="settings_reader_font">Reader font</string> <string name="settings_reader_font">Reader font</string>
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string> <string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string> <string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string> <string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string> <string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
<string name="pref_switch_enable_analytics">Enable analytics</string>
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string> <string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string> <string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string> <string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string> <string name="pref_switch_disable_acra">"Disable automatic bug reporting."</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"Non c'è niente qui"</string>
<string name="tab_new">"Nuovi"</string>
<string name="tab_read">"Tutti"</string>
<string name="tab_favs">"Preferiti"</string>
<string name="action_about">"Informazioni"</string>
<string name="marked_as_read">"Articolo letto"</string>
<string name="marked_as_unread">"Item unread"</string>
</resources> </resources>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<string name="app_name">"Reader for Selfoss"</string> <string name="app_name">"Reader for Selfoss"</string>
<string name="title_activity_login">"로그인"</string> <string name="title_activity_login">"로그인"</string>
<string name="prompt_password">"비밀번호"</string> <string name="prompt_password">"비밀번호"</string>
@ -7,10 +7,10 @@
<string name="error_invalid_password">"패스워드가 짧습니다."</string> <string name="error_invalid_password">"패스워드가 짧습니다."</string>
<string name="error_field_required">"필수 항목"</string> <string name="error_field_required">"필수 항목"</string>
<string name="prompt_url">"Url"</string> <string name="prompt_url">"Url"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"로그인이 필요합니까?"</string> <string name="withLoginSwitch">"로그인이 필요합니까?"</string>
<string name="login_url_problem">"죄송합니다. Url의 끝에 \"/\"를 추가할 필요가 있습니다."</string> <string name="login_url_problem">"죄송합니다. Url의 끝에 \"/\"를 추가할 필요가 있습니다."</string>
<string name="prompt_login">"사용자 이름"</string> <string name="prompt_login">"사용자 이름"</string>
<string name="label_share">"공유"</string>
<string name="readAll">"모두 읽기"</string> <string name="readAll">"모두 읽기"</string>
<string name="action_disconnect">"연결 해제"</string> <string name="action_disconnect">"연결 해제"</string>
<string name="title_activity_settings">"설정"</string> <string name="title_activity_settings">"설정"</string>
@ -23,19 +23,12 @@
<string name="wrong_infos">"세부 정보를 다시 확인하세요."</string> <string name="wrong_infos">"세부 정보를 다시 확인하세요."</string>
<string name="all_posts_not_read">"모든 게시물을 읽지 않았습니다."</string> <string name="all_posts_not_read">"모든 게시물을 읽지 않았습니다."</string>
<string name="all_posts_read">"모든 게시물을 읽었습니다."</string> <string name="all_posts_read">"모든 게시물을 읽었습니다."</string>
<string name="nothing_here">"비어있음"</string>
<string name="tab_new">"새로운"</string>
<string name="tab_read">"전체"</string>
<string name="tab_favs">"즐겨찾기"</string>
<string name="action_about">"정보"</string>
<string name="marked_as_read">"항목 읽기"</string>
<string name="marked_as_unread">"Item unread"</string>
<string name="undo_string">"실행 취소"</string> <string name="undo_string">"실행 취소"</string>
<string name="addStringNoUrl">"로그인 소스를 추가 해야 합니다."</string> <string name="addStringNoUrl">"로그인 소스를 추가 해야 합니다."</string>
<string name="cant_get_sources">"소스 리스트를 얻을 수 없습니다."</string> <string name="cant_get_sources">"소스 리스트를 얻을 수 없습니다."</string>
<string name="cant_create_source">"소스를 만들 수 없습니다."</string> <string name="cant_create_source">"소스를 만들 수 없습니다."</string>
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string> <string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
<string name="cant_get_spouts">"Can't get spouts list. There may ben an api issue."</string> <string name="cant_get_spouts">"Spouts 목록을 가져올 수 없습니다."</string>
<string name="form_not_complete">"양식이 완료되지 않았습니다."</string> <string name="form_not_complete">"양식이 완료되지 않았습니다."</string>
<string name="pref_header_links">"링크"</string> <string name="pref_header_links">"링크"</string>
<string name="issue_tracker_link">"이슈 트래커"</string> <string name="issue_tracker_link">"이슈 트래커"</string>
@ -62,28 +55,18 @@
<string name="card_height_on">Cards height will adjust to its content</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="card_height_off">Card height will be fixed</string>
<string name="source_code">Source code</string> <string name="source_code">Source code</string>
<string name="drawer_error_loading_tags">Error loading tags…</string> <string name="filter_item_tags">Tags</string>
<string name="drawer_item_filters">Filters</string> <string name="filter_item_sources">Sources</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="drawer_loading">Loading …</string>
<string name="menu_home_search">Search</string> <string name="menu_home_search">Search</string>
<string name="can_delete_source">Can\'t delete the source…</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="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_dark_theme">Default/Dark</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="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">Translation</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="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="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_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_on">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>
@ -94,7 +77,6 @@
<string name="markall_dialog_message">This will mark all the items as read.</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">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="pref_switch_actions_pager_scroll_off">Don\'t mark articles as read when swiping.</string>
<string name="drawer_item_hidden_tags">Hidden Tags</string>
<string name="unmark">Mark item as unread</string> <string name="unmark">Mark item as unread</string>
<string name="pref_header_offline">Offline and cache</string> <string name="pref_header_offline">Offline and cache</string>
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string> <string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
@ -109,7 +91,7 @@
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string> <string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string> <string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string>
<string name="pref_switch_refresh_when_charging">Only refresh when phone is charging</string> <string name="pref_switch_refresh_when_charging">Only refresh when phone is charging</string>
<string name="loading_notification_title">Loading ...</string> <string name="loading_notification_title">Loading </string>
<string name="loading_notification_text">Selfoss is syncing your articles</string> <string name="loading_notification_text">Selfoss is syncing your articles</string>
<string name="notification_channel_sync">Sync notification</string> <string name="notification_channel_sync">Sync notification</string>
<string name="new_items_channel_sync">New items notification</string> <string name="new_items_channel_sync">New items notification</string>
@ -124,18 +106,27 @@
<string name="reader_text_align_left">Align left</string> <string name="reader_text_align_left">Align left</string>
<string name="reader_text_align_justify">Justify</string> <string name="reader_text_align_justify">Justify</string>
<string name="settings_reader_font">Reader font</string> <string name="settings_reader_font">Reader font</string>
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string> <string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string> <string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string> <string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string> <string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
<string name="pref_switch_enable_analytics">Enable analytics</string>
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string> <string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string> <string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string> <string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string> <string name="pref_switch_disable_acra">"Disable automatic bug reporting."</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"비어있음"</string>
<string name="tab_new">"새로운"</string>
<string name="tab_read">"전체"</string>
<string name="tab_favs">"즐겨찾기"</string>
<string name="action_about">"정보"</string>
<string name="marked_as_read">"항목 읽기"</string>
<string name="marked_as_unread">"Item unread"</string>
</resources> </resources>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="pref_switch_enable_analytics">Enable analytics</string>
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string>
</resources>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<string name="app_name">"Selfoss Reader"</string> <string name="app_name">"Selfoss Reader"</string>
<string name="title_activity_login">"Inloggen"</string> <string name="title_activity_login">"Inloggen"</string>
<string name="prompt_password">"Wachtwoord"</string> <string name="prompt_password">"Wachtwoord"</string>
@ -7,10 +7,10 @@
<string name="error_invalid_password">"Wachtwoord niet lang genoeg"</string> <string name="error_invalid_password">"Wachtwoord niet lang genoeg"</string>
<string name="error_field_required">"Dit veld is verplicht"</string> <string name="error_field_required">"Dit veld is verplicht"</string>
<string name="prompt_url">"Selfoss server"</string> <string name="prompt_url">"Selfoss server"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"Authenticatie vereist?"</string> <string name="withLoginSwitch">"Authenticatie vereist?"</string>
<string name="login_url_problem">"Oeps, ben je soms de \"/\" vergeten aan het eind?"</string> <string name="login_url_problem">"Oeps, ben je soms de \"/\" vergeten aan het eind?"</string>
<string name="prompt_login">"Gebruikersnaam"</string> <string name="prompt_login">"Gebruikersnaam"</string>
<string name="label_share">"Delen"</string>
<string name="readAll">"Alles lezen"</string> <string name="readAll">"Alles lezen"</string>
<string name="action_disconnect">"Verbinding verbreken"</string> <string name="action_disconnect">"Verbinding verbreken"</string>
<string name="title_activity_settings">"Instellingen"</string> <string name="title_activity_settings">"Instellingen"</string>
@ -23,19 +23,12 @@
<string name="wrong_infos">"Controleer de gegevens nogmaals."</string> <string name="wrong_infos">"Controleer de gegevens nogmaals."</string>
<string name="all_posts_not_read">"Fout bij markeren als gelezen"</string> <string name="all_posts_not_read">"Fout bij markeren als gelezen"</string>
<string name="all_posts_read">"Alle artikelen gemarkeerd als gelezen"</string> <string name="all_posts_read">"Alle artikelen gemarkeerd als gelezen"</string>
<string name="nothing_here">"Niets gevonden"</string>
<string name="tab_new">"Nieuw"</string>
<string name="tab_read">"Alle"</string>
<string name="tab_favs">"Favorieten"</string>
<string name="action_about">"Over"</string>
<string name="marked_as_read">"Artikel gelezen"</string>
<string name="marked_as_unread">"Item unread"</string>
<string name="undo_string">"Ongedaan maken"</string> <string name="undo_string">"Ongedaan maken"</string>
<string name="addStringNoUrl">"Login om bronnen toe te voegen"</string> <string name="addStringNoUrl">"Login om bronnen toe te voegen"</string>
<string name="cant_get_sources">"Kan de lijst met bronnen niet ophalen"</string> <string name="cant_get_sources">"Kan de lijst met bronnen niet ophalen"</string>
<string name="cant_create_source">"Kan bron niet creëeren"</string> <string name="cant_create_source">"Kan bron niet creëeren"</string>
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string> <string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
<string name="cant_get_spouts">"Can't get spouts list. There may ben an api issue."</string> <string name="cant_get_spouts">"Ophalen spouts mislukt"</string>
<string name="form_not_complete">"Formulier is niet volledig ingevuld"</string> <string name="form_not_complete">"Formulier is niet volledig ingevuld"</string>
<string name="pref_header_links">"Links"</string> <string name="pref_header_links">"Links"</string>
<string name="issue_tracker_link">"Bug tracker"</string> <string name="issue_tracker_link">"Bug tracker"</string>
@ -62,28 +55,18 @@
<string name="card_height_on">Hoogte aanpassen aan de hand van kaartinhoud</string> <string name="card_height_on">Hoogte aanpassen aan de hand van kaartinhoud</string>
<string name="card_height_off">Vaste hoogte</string> <string name="card_height_off">Vaste hoogte</string>
<string name="source_code">Broncode</string> <string name="source_code">Broncode</string>
<string name="drawer_error_loading_tags">Fout bij het laden van tags…</string> <string name="filter_item_tags">Tags</string>
<string name="drawer_item_filters">Filters</string> <string name="filter_item_sources">Bronnen</string>
<string name="drawer_action_clear">wissen</string>
<string name="drawer_item_tags">Tags</string>
<string name="drawer_item_sources">Bronnen</string>
<string name="drawer_action_edit">bewerken</string>
<string name="drawer_loading">Bezig met laden …</string>
<string name="menu_home_search">Zoeken</string> <string name="menu_home_search">Zoeken</string>
<string name="can_delete_source">Kan de bron niet verwijderen…</string> <string name="can_delete_source">Kan de bron niet verwijderen…</string>
<string name="base_url_error">Er was een probleem bij het communiceren met uw Selfoss Instance. Als het probleem blijft, neem dan contact met mij op.</string> <string name="base_url_error">Er was een probleem bij het communiceren met uw Selfoss Instance. Als het probleem blijft, neem dan contact met mij op.</string>
<string name="pref_header_theme">Thema \'s</string> <string name="pref_header_theme">Thema \'s</string>
<string name="default_theme">Standaard</string>
<string name="default_dark_theme">Standaard/Donker</string>
<string name="pref_selfoss_category">Selfoss Api</string> <string name="pref_selfoss_category">Selfoss Api</string>
<string name="pref_api_items_number_title">Geladen items nummer</string> <string name="pref_api_items_number_title">Geladen items nummer</string>
<string name="pref_hidden_tags">Hidden Tags</string>
<string name="pref_general_infinite_loading_title">Laad meer artikelen door te bladeren</string> <string name="pref_general_infinite_loading_title">Laad meer artikelen door te bladeren</string>
<string name="translation">Vertaling</string> <string name="translation">Vertaling</string>
<string name="cant_open_invalid_url">De URL is ongeldig. Ik probeer dit probleem op te lossen, zodat de toepassing niet wordt afgesloten.</string> <string name="cant_open_invalid_url">De URL is ongeldig. Ik probeer dit probleem op te lossen, zodat de toepassing niet wordt afgesloten.</string>
<string name="drawer_report_bug">Een fout melden</string>
<string name="items_number_should_be_number">Het aantal items moet een geheel getal zijn.</string> <string name="items_number_should_be_number">Het aantal items moet een geheel getal zijn.</string>
<string name="reader_action_more">Lees meer</string>
<string name="reader_action_open">Openen in browser</string> <string name="reader_action_open">Openen in browser</string>
<string name="reader_action_share">Delen</string> <string name="reader_action_share">Delen</string>
<string name="pref_switch_actions_pager_scroll_on">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>
@ -94,7 +77,6 @@
<string name="markall_dialog_message">This will mark all the items as read.</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">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="pref_switch_actions_pager_scroll_off">Don\'t mark articles as read when swiping.</string>
<string name="drawer_item_hidden_tags">Hidden Tags</string>
<string name="unmark">Mark item as unread</string> <string name="unmark">Mark item as unread</string>
<string name="pref_header_offline">Offline and cache</string> <string name="pref_header_offline">Offline and cache</string>
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string> <string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
@ -104,12 +86,12 @@
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string> <string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
<string name="network_connectivity_lost">"Network connection lost"</string> <string name="network_connectivity_lost">"Network connection lost"</string>
<string name="network_connectivity_retrieved">"Network connection is now available"</string> <string name="network_connectivity_retrieved">"Network connection is now available"</string>
<string name="pref_switch_periodic_refresh">Sync articles</string> <string name="pref_switch_periodic_refresh">Artikel synchronisieren</string>
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string> <string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string> <string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string> <string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string>
<string name="pref_switch_refresh_when_charging">Only refresh when phone is charging</string> <string name="pref_switch_refresh_when_charging">Only refresh when phone is charging</string>
<string name="loading_notification_title">Loading ...</string> <string name="loading_notification_title">Loading </string>
<string name="loading_notification_text">Selfoss is syncing your articles</string> <string name="loading_notification_text">Selfoss is syncing your articles</string>
<string name="notification_channel_sync">Sync notification</string> <string name="notification_channel_sync">Sync notification</string>
<string name="new_items_channel_sync">New items notification</string> <string name="new_items_channel_sync">New items notification</string>
@ -124,18 +106,27 @@
<string name="reader_text_align_left">Align left</string> <string name="reader_text_align_left">Align left</string>
<string name="reader_text_align_justify">Justify</string> <string name="reader_text_align_justify">Justify</string>
<string name="settings_reader_font">Reader font</string> <string name="settings_reader_font">Reader font</string>
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string> <string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string> <string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string> <string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string> <string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
<string name="pref_switch_enable_analytics">Enable analytics</string>
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string> <string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string> <string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string> <string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string> <string name="pref_switch_disable_acra">"Disable automatic bug reporting."</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"Niets gevonden"</string>
<string name="tab_new">"Nieuw"</string>
<string name="tab_read">"Alle"</string>
<string name="tab_favs">"Favorieten"</string>
<string name="action_about">"Over"</string>
<string name="marked_as_read">"Artikel gelezen"</string>
<string name="marked_as_unread">"Item unread"</string>
</resources> </resources>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<string name="app_name">"Reader for Selfoss"</string> <string name="app_name">"Reader for Selfoss"</string>
<string name="title_activity_login">"Entrar"</string> <string name="title_activity_login">"Entrar"</string>
<string name="prompt_password">"Senha"</string> <string name="prompt_password">"Senha"</string>
@ -7,10 +7,10 @@
<string name="error_invalid_password">"Senha muito pequena"</string> <string name="error_invalid_password">"Senha muito pequena"</string>
<string name="error_field_required">"Campo obrigatório"</string> <string name="error_field_required">"Campo obrigatório"</string>
<string name="prompt_url">"Url"</string> <string name="prompt_url">"Url"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"É necessário o login ?"</string> <string name="withLoginSwitch">"É necessário o login ?"</string>
<string name="login_url_problem">"Oops. Talvez você precise adicionar uma \"/\" no final da url."</string> <string name="login_url_problem">"Oops. Talvez você precise adicionar uma \"/\" no final da url."</string>
<string name="prompt_login">"Usuário"</string> <string name="prompt_login">"Usuário"</string>
<string name="label_share">"Compartilhar"</string>
<string name="readAll">"Ler todos"</string> <string name="readAll">"Ler todos"</string>
<string name="action_disconnect">"Desconectar"</string> <string name="action_disconnect">"Desconectar"</string>
<string name="title_activity_settings">"Configurações"</string> <string name="title_activity_settings">"Configurações"</string>
@ -23,19 +23,12 @@
<string name="wrong_infos">"Verifique os detalhes novamente."</string> <string name="wrong_infos">"Verifique os detalhes novamente."</string>
<string name="all_posts_not_read">"Nenhum post foi lido"</string> <string name="all_posts_not_read">"Nenhum post foi lido"</string>
<string name="all_posts_read">"Todos os posts foram lidos"</string> <string name="all_posts_read">"Todos os posts foram lidos"</string>
<string name="nothing_here">"Nada aqui"</string>
<string name="tab_new">"Novo"</string>
<string name="tab_read">"Todos"</string>
<string name="tab_favs">"Favoritos"</string>
<string name="action_about">"Sobre"</string>
<string name="marked_as_read">"Item lido"</string>
<string name="marked_as_unread">"Item unread"</string>
<string name="undo_string">"Desfazer"</string> <string name="undo_string">"Desfazer"</string>
<string name="addStringNoUrl">"Faça login para adicionar fontes."</string> <string name="addStringNoUrl">"Faça login para adicionar fontes."</string>
<string name="cant_get_sources">"Não é possível obter a lista de fontes."</string> <string name="cant_get_sources">"Não é possível obter a lista de fontes."</string>
<string name="cant_create_source">"Não é possível criar fonte."</string> <string name="cant_create_source">"Não é possível criar fonte."</string>
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string> <string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
<string name="cant_get_spouts">"Can't get spouts list. There may ben an api issue."</string> <string name="cant_get_spouts">"Não é possível obter a lista de spouts."</string>
<string name="form_not_complete">"O formulário não está completo"</string> <string name="form_not_complete">"O formulário não está completo"</string>
<string name="pref_header_links">"Links"</string> <string name="pref_header_links">"Links"</string>
<string name="issue_tracker_link">"Rastreador de problemas"</string> <string name="issue_tracker_link">"Rastreador de problemas"</string>
@ -62,28 +55,18 @@
<string name="card_height_on">Cards com altura ajustáveis de acordo com o conteúdo</string> <string name="card_height_on">Cards com altura ajustáveis de acordo com o conteúdo</string>
<string name="card_height_off">Cards com altura de tamanho fixo</string> <string name="card_height_off">Cards com altura de tamanho fixo</string>
<string name="source_code">Código fonte</string> <string name="source_code">Código fonte</string>
<string name="drawer_error_loading_tags">Erro ao carregar as tags…</string> <string name="filter_item_tags">Tags</string>
<string name="drawer_item_filters">Filtros</string> <string name="filter_item_sources">Fontes</string>
<string name="drawer_action_clear">limpar</string>
<string name="drawer_item_tags">Tags</string>
<string name="drawer_item_sources">Fontes</string>
<string name="drawer_action_edit">editar</string>
<string name="drawer_loading">Carregando …</string>
<string name="menu_home_search">Procurar</string> <string name="menu_home_search">Procurar</string>
<string name="can_delete_source">Não foi possível apagar a fonte…</string> <string name="can_delete_source">Não foi possível apagar a fonte…</string>
<string name="base_url_error">Houve um problema ao tentar se comunicar com o seu Selfoss. Se o problema persistir, entre em contato comigo.</string> <string name="base_url_error">Houve um problema ao tentar se comunicar com o seu Selfoss. Se o problema persistir, entre em contato comigo.</string>
<string name="pref_header_theme">Temas</string> <string name="pref_header_theme">Temas</string>
<string name="default_theme">Padrão</string>
<string name="default_dark_theme">Padrão/Escuro</string>
<string name="pref_selfoss_category">Selfoss Api</string> <string name="pref_selfoss_category">Selfoss Api</string>
<string name="pref_api_items_number_title">Quantidade de itens carregados</string> <string name="pref_api_items_number_title">Quantidade de itens carregados</string>
<string name="pref_hidden_tags">Hidden Tags</string>
<string name="pref_general_infinite_loading_title">Carregar mais artigos ao realizar o scroll</string> <string name="pref_general_infinite_loading_title">Carregar mais artigos ao realizar o scroll</string>
<string name="translation">Traduções</string> <string name="translation">Traduções</string>
<string name="cant_open_invalid_url">A url está inválida. Estou tentando resolver esse problema para que o aplicativo não encerre.</string> <string name="cant_open_invalid_url">A url está inválida. Estou tentando resolver esse problema para que o aplicativo não encerre.</string>
<string name="drawer_report_bug">Reportar erro</string>
<string name="items_number_should_be_number">O número dos itens deve ser um número inteiro.</string> <string name="items_number_should_be_number">O número dos itens deve ser um número inteiro.</string>
<string name="reader_action_more">Leia mais</string>
<string name="reader_action_open">Abrir no navegador</string> <string name="reader_action_open">Abrir no navegador</string>
<string name="reader_action_share">Compartilhar</string> <string name="reader_action_share">Compartilhar</string>
<string name="pref_switch_actions_pager_scroll_on">Se esta configuração estiver ativada, os artigos serão marcados como lidos ao deslizar para a esquerda e para a direita no leitor do artigo.</string> <string name="pref_switch_actions_pager_scroll_on">Se esta configuração estiver ativada, os artigos serão marcados como lidos ao deslizar para a esquerda e para a direita no leitor do artigo.</string>
@ -94,7 +77,6 @@
<string name="markall_dialog_message">Isso marcará todos os itens como lidos.</string> <string name="markall_dialog_message">Isso marcará todos os itens como lidos.</string>
<string name="pref_switch_actions_pager_scroll">Marcar Como Lida ao Abrir</string> <string name="pref_switch_actions_pager_scroll">Marcar Como Lida ao Abrir</string>
<string name="pref_switch_actions_pager_scroll_off">Não marca artigos como lido quando abrir.</string> <string name="pref_switch_actions_pager_scroll_off">Não marca artigos como lido quando abrir.</string>
<string name="drawer_item_hidden_tags">Hidden Tags</string>
<string name="unmark">Mark item as unread</string> <string name="unmark">Mark item as unread</string>
<string name="pref_header_offline">Offline and cache</string> <string name="pref_header_offline">Offline and cache</string>
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string> <string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
@ -109,7 +91,7 @@
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string> <string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string> <string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string>
<string name="pref_switch_refresh_when_charging">Only refresh when phone is charging</string> <string name="pref_switch_refresh_when_charging">Only refresh when phone is charging</string>
<string name="loading_notification_title">Loading ...</string> <string name="loading_notification_title">Loading </string>
<string name="loading_notification_text">Selfoss is syncing your articles</string> <string name="loading_notification_text">Selfoss is syncing your articles</string>
<string name="notification_channel_sync">Sync notification</string> <string name="notification_channel_sync">Sync notification</string>
<string name="new_items_channel_sync">New items notification</string> <string name="new_items_channel_sync">New items notification</string>
@ -124,18 +106,27 @@
<string name="reader_text_align_left">Align left</string> <string name="reader_text_align_left">Align left</string>
<string name="reader_text_align_justify">Justify</string> <string name="reader_text_align_justify">Justify</string>
<string name="settings_reader_font">Reader font</string> <string name="settings_reader_font">Reader font</string>
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string> <string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string> <string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string> <string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string> <string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
<string name="pref_switch_enable_analytics">Enable analytics</string>
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string> <string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string> <string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string> <string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string> <string name="pref_switch_disable_acra">"Disable automatic bug reporting."</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"Nada aqui"</string>
<string name="tab_new">"Novo"</string>
<string name="tab_read">"Todos"</string>
<string name="tab_favs">"Favoritos"</string>
<string name="action_about">"Sobre"</string>
<string name="marked_as_read">"Item lido"</string>
<string name="marked_as_unread">"Item unread"</string>
</resources> </resources>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<string name="app_name">"Leitor para Selfoss"</string> <string name="app_name">"Leitor para Selfoss"</string>
<string name="title_activity_login">"Iniciar sessão"</string> <string name="title_activity_login">"Iniciar sessão"</string>
<string name="prompt_password">"Palavra passe"</string> <string name="prompt_password">"Palavra passe"</string>
@ -7,10 +7,10 @@
<string name="error_invalid_password">"Senha não é longa o suficiente"</string> <string name="error_invalid_password">"Senha não é longa o suficiente"</string>
<string name="error_field_required">"Campo obrigatório"</string> <string name="error_field_required">"Campo obrigatório"</string>
<string name="prompt_url">"Url"</string> <string name="prompt_url">"Url"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"É necessário fazer login?"</string> <string name="withLoginSwitch">"É necessário fazer login?"</string>
<string name="login_url_problem">"Uups. Você pode precisar adicionar uma \"/\" no final da url."</string> <string name="login_url_problem">"Uups. Você pode precisar adicionar uma \"/\" no final da url."</string>
<string name="prompt_login">"Nome do usuário"</string> <string name="prompt_login">"Nome do usuário"</string>
<string name="label_share">"Compartilhar"</string>
<string name="readAll">"Ler tudo"</string> <string name="readAll">"Ler tudo"</string>
<string name="action_disconnect">"Desligar"</string> <string name="action_disconnect">"Desligar"</string>
<string name="title_activity_settings">"Configurações"</string> <string name="title_activity_settings">"Configurações"</string>
@ -23,19 +23,12 @@
<string name="wrong_infos">"Verifique seus dados novamente."</string> <string name="wrong_infos">"Verifique seus dados novamente."</string>
<string name="all_posts_not_read">"Todas as postagens não foram lidas"</string> <string name="all_posts_not_read">"Todas as postagens não foram lidas"</string>
<string name="all_posts_read">"Todas as postagens foram lidas"</string> <string name="all_posts_read">"Todas as postagens foram lidas"</string>
<string name="nothing_here">"Nada aqui"</string>
<string name="tab_new">"Novo"</string>
<string name="tab_read">"Tudo"</string>
<string name="tab_favs">"Favoritos"</string>
<string name="action_about">"Sobre"</string>
<string name="marked_as_read">"Item lido"</string>
<string name="marked_as_unread">"Item unread"</string>
<string name="undo_string">"Desfazer"</string> <string name="undo_string">"Desfazer"</string>
<string name="addStringNoUrl">"Logar para adicionar fontes."</string> <string name="addStringNoUrl">"Logar para adicionar fontes."</string>
<string name="cant_get_sources">"Não é possível obter a lista de fontes."</string> <string name="cant_get_sources">"Não é possível obter a lista de fontes."</string>
<string name="cant_create_source">"Não é possível criar a fonte."</string> <string name="cant_create_source">"Não é possível criar a fonte."</string>
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string> <string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
<string name="cant_get_spouts">"Can't get spouts list. There may ben an api issue."</string> <string name="cant_get_spouts">"Não é possível obter a lista de bicos."</string>
<string name="form_not_complete">"O formulário não está completo"</string> <string name="form_not_complete">"O formulário não está completo"</string>
<string name="pref_header_links">"Links"</string> <string name="pref_header_links">"Links"</string>
<string name="issue_tracker_link">"Rastreador de problemas"</string> <string name="issue_tracker_link">"Rastreador de problemas"</string>
@ -62,28 +55,18 @@
<string name="card_height_on">Altura de cartas irá ajustar ao seu conteúdo</string> <string name="card_height_on">Altura de cartas irá ajustar ao seu conteúdo</string>
<string name="card_height_off">Altura do cartão será corrigida</string> <string name="card_height_off">Altura do cartão será corrigida</string>
<string name="source_code">Código fonte</string> <string name="source_code">Código fonte</string>
<string name="drawer_error_loading_tags">Erro ao carregar etiquetas…</string> <string name="filter_item_tags">Etiquetas</string>
<string name="drawer_item_filters">Filtros</string> <string name="filter_item_sources">Fontes</string>
<string name="drawer_action_clear">limpar</string>
<string name="drawer_item_tags">Etiquetas</string>
<string name="drawer_item_sources">Fontes</string>
<string name="drawer_action_edit">editar</string>
<string name="drawer_loading">A carregar…</string>
<string name="menu_home_search">Buscar</string> <string name="menu_home_search">Buscar</string>
<string name="can_delete_source">Não é possível excluir a fonte…</string> <string name="can_delete_source">Não é possível excluir a fonte…</string>
<string name="base_url_error">Houve um problema ao tentar se comunicar com sua instância de Selfoss. Se o problema persistir, por favor entre em contato comigo.</string> <string name="base_url_error">Houve um problema ao tentar se comunicar com sua instância de Selfoss. Se o problema persistir, por favor entre em contato comigo.</string>
<string name="pref_header_theme">Temas</string> <string name="pref_header_theme">Temas</string>
<string name="default_theme">Predefinição</string>
<string name="default_dark_theme">Padrão/escuro</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 itens carregados</string> <string name="pref_api_items_number_title">Número de itens carregados</string>
<string name="pref_hidden_tags">Hidden Tags</string>
<string name="pref_general_infinite_loading_title">Carregar mais artigos no pergaminho</string> <string name="pref_general_infinite_loading_title">Carregar mais artigos no pergaminho</string>
<string name="translation">Tradução</string> <string name="translation">Tradução</string>
<string name="cant_open_invalid_url">A url do item é inválido. Eu estou olhando para resolver esta questão, para que o app não vai falhar.</string> <string name="cant_open_invalid_url">A url do item é inválido. Eu estou olhando para resolver esta questão, para que o app não vai falhar.</string>
<string name="drawer_report_bug">Reportar falha</string>
<string name="items_number_should_be_number">O número de itens deve ser um número inteiro.</string> <string name="items_number_should_be_number">O número de itens deve ser um número inteiro.</string>
<string name="reader_action_more">Ler mais</string>
<string name="reader_action_open">Abrir no browser</string> <string name="reader_action_open">Abrir no browser</string>
<string name="reader_action_share">Compartilhar</string> <string name="reader_action_share">Compartilhar</string>
<string name="pref_switch_actions_pager_scroll_on">Artigos de marca como lida quando passar entre artigos.</string> <string name="pref_switch_actions_pager_scroll_on">Artigos de marca como lida quando passar entre artigos.</string>
@ -94,7 +77,6 @@
<string name="markall_dialog_message">This will mark all the items as read.</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">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="pref_switch_actions_pager_scroll_off">Don\'t mark articles as read when swiping.</string>
<string name="drawer_item_hidden_tags">Hidden Tags</string>
<string name="unmark">Mark item as unread</string> <string name="unmark">Mark item as unread</string>
<string name="pref_header_offline">Offline and cache</string> <string name="pref_header_offline">Offline and cache</string>
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string> <string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
@ -109,7 +91,7 @@
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string> <string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string> <string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string>
<string name="pref_switch_refresh_when_charging">Only refresh when phone is charging</string> <string name="pref_switch_refresh_when_charging">Only refresh when phone is charging</string>
<string name="loading_notification_title">Loading ...</string> <string name="loading_notification_title">Loading </string>
<string name="loading_notification_text">Selfoss is syncing your articles</string> <string name="loading_notification_text">Selfoss is syncing your articles</string>
<string name="notification_channel_sync">Sync notification</string> <string name="notification_channel_sync">Sync notification</string>
<string name="new_items_channel_sync">New items notification</string> <string name="new_items_channel_sync">New items notification</string>
@ -124,18 +106,27 @@
<string name="reader_text_align_left">Align left</string> <string name="reader_text_align_left">Align left</string>
<string name="reader_text_align_justify">Justify</string> <string name="reader_text_align_justify">Justify</string>
<string name="settings_reader_font">Reader font</string> <string name="settings_reader_font">Reader font</string>
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string> <string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string> <string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string> <string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string> <string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
<string name="pref_switch_enable_analytics">Enable analytics</string>
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string> <string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string> <string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string> <string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string> <string name="pref_switch_disable_acra">"Disable automatic bug reporting."</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"Nada aqui"</string>
<string name="tab_new">"Novo"</string>
<string name="tab_read">"Tudo"</string>
<string name="tab_favs">"Favoritos"</string>
<string name="action_about">"Sobre"</string>
<string name="marked_as_read">"Item lido"</string>
<string name="marked_as_unread">"Item unread"</string>
</resources> </resources>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<string name="app_name">"Reader for Selfoss"</string> <string name="app_name">"Reader for Selfoss"</string>
<string name="title_activity_login">"පිවිසෙන්න"</string> <string name="title_activity_login">"පිවිසෙන්න"</string>
<string name="prompt_password">"මුර පදය"</string> <string name="prompt_password">"මුර පදය"</string>
@ -7,10 +7,10 @@
<string name="error_invalid_password">"Password not long enough"</string> <string name="error_invalid_password">"Password not long enough"</string>
<string name="error_field_required">"Field required"</string> <string name="error_field_required">"Field required"</string>
<string name="prompt_url">"Url"</string> <string name="prompt_url">"Url"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"Login required ?"</string> <string name="withLoginSwitch">"Login required ?"</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">"Oops. You may need to add a \"/\" at the end of the url."</string>
<string name="prompt_login">"පරිශීලක නාමය"</string> <string name="prompt_login">"පරිශීලක නාමය"</string>
<string name="label_share">"Share"</string>
<string name="readAll">"Read all"</string> <string name="readAll">"Read all"</string>
<string name="action_disconnect">"Disconnect"</string> <string name="action_disconnect">"Disconnect"</string>
<string name="title_activity_settings">"සැකසුම්"</string> <string name="title_activity_settings">"සැකසුම්"</string>
@ -23,13 +23,6 @@
<string name="wrong_infos">"Check your details again."</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_not_read">"All posts weren't read"</string>
<string name="all_posts_read">"All posts were read"</string> <string name="all_posts_read">"All posts were read"</string>
<string name="nothing_here">"Nothing here"</string>
<string name="tab_new">"New"</string>
<string name="tab_read">"සියල්ල"</string>
<string name="tab_favs">"Favorites"</string>
<string name="action_about">"මේ ගැන"</string>
<string name="marked_as_read">"Item read"</string>
<string name="marked_as_unread">"Item unread"</string>
<string name="undo_string">"Undo"</string> <string name="undo_string">"Undo"</string>
<string name="addStringNoUrl">"Log in to add sources."</string> <string name="addStringNoUrl">"Log in to add sources."</string>
<string name="cant_get_sources">"Can't get sources list."</string> <string name="cant_get_sources">"Can't get sources list."</string>
@ -62,28 +55,18 @@
<string name="card_height_on">Cards height will adjust to its content</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="card_height_off">Card height will be fixed</string>
<string name="source_code">Source code</string> <string name="source_code">Source code</string>
<string name="drawer_error_loading_tags">Error loading tags…</string> <string name="filter_item_tags">Tags</string>
<string name="drawer_item_filters">Filters</string> <string name="filter_item_sources">Sources</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="drawer_loading">Loading …</string>
<string name="menu_home_search">Search</string> <string name="menu_home_search">Search</string>
<string name="can_delete_source">Can\'t delete the source…</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="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_dark_theme">Default/Dark</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="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">Translation</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="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="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_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_on">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>
@ -94,7 +77,6 @@
<string name="markall_dialog_message">This will mark all the items as read.</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">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="pref_switch_actions_pager_scroll_off">Don\'t mark articles as read when swiping.</string>
<string name="drawer_item_hidden_tags">Hidden Tags</string>
<string name="unmark">Mark item as unread</string> <string name="unmark">Mark item as unread</string>
<string name="pref_header_offline">Offline and cache</string> <string name="pref_header_offline">Offline and cache</string>
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string> <string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
@ -109,7 +91,7 @@
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string> <string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string> <string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string>
<string name="pref_switch_refresh_when_charging">Only refresh when phone is charging</string> <string name="pref_switch_refresh_when_charging">Only refresh when phone is charging</string>
<string name="loading_notification_title">Loading ...</string> <string name="loading_notification_title">Loading </string>
<string name="loading_notification_text">Selfoss is syncing your articles</string> <string name="loading_notification_text">Selfoss is syncing your articles</string>
<string name="notification_channel_sync">Sync notification</string> <string name="notification_channel_sync">Sync notification</string>
<string name="new_items_channel_sync">New items notification</string> <string name="new_items_channel_sync">New items notification</string>
@ -124,18 +106,27 @@
<string name="reader_text_align_left">Align left</string> <string name="reader_text_align_left">Align left</string>
<string name="reader_text_align_justify">Justify</string> <string name="reader_text_align_justify">Justify</string>
<string name="settings_reader_font">Reader font</string> <string name="settings_reader_font">Reader font</string>
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string> <string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string> <string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string> <string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string> <string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
<string name="pref_switch_enable_analytics">Enable analytics</string>
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string> <string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string> <string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string> <string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string> <string name="pref_switch_disable_acra">"Disable automatic bug reporting."</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"Nothing here"</string>
<string name="tab_new">"New"</string>
<string name="tab_read">"සියල්ල"</string>
<string name="tab_favs">"Favorites"</string>
<string name="action_about">"මේ ගැන"</string>
<string name="marked_as_read">"Item read"</string>
<string name="marked_as_unread">"Item unread"</string>
</resources> </resources>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<string name="app_name">"Selfoss için okuyucu"</string> <string name="app_name">"Selfoss için okuyucu"</string>
<string name="title_activity_login">"Giriş"</string> <string name="title_activity_login">"Giriş"</string>
<string name="prompt_password">"Şifre"</string> <string name="prompt_password">"Şifre"</string>
@ -7,10 +7,10 @@
<string name="error_invalid_password">"Parola yeterince uzun değil"</string> <string name="error_invalid_password">"Parola yeterince uzun değil"</string>
<string name="error_field_required">"Alan gereklidir"</string> <string name="error_field_required">"Alan gereklidir"</string>
<string name="prompt_url">"Url"</string> <string name="prompt_url">"Url"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"Kullanıcı Girişi Gerekli?"</string> <string name="withLoginSwitch">"Kullanıcı Girişi Gerekli?"</string>
<string name="login_url_problem">"Oops. Url'nin sonuna \"/\" eklemek gerekebilir."</string> <string name="login_url_problem">"Oops. Url'nin sonuna \"/\" eklemek gerekebilir."</string>
<string name="prompt_login">"Kullanıcı adı"</string> <string name="prompt_login">"Kullanıcı adı"</string>
<string name="label_share">"Paylaş"</string>
<string name="readAll">"Tümünü oku"</string> <string name="readAll">"Tümünü oku"</string>
<string name="action_disconnect">"Bağlantıyı kes"</string> <string name="action_disconnect">"Bağlantıyı kes"</string>
<string name="title_activity_settings">"Ayarlar"</string> <string name="title_activity_settings">"Ayarlar"</string>
@ -23,19 +23,12 @@
<string name="wrong_infos">"Detaylarınızı tekrar kontrol edin."</string> <string name="wrong_infos">"Detaylarınızı tekrar kontrol edin."</string>
<string name="all_posts_not_read">"Tüm mesajlar okunmadı"</string> <string name="all_posts_not_read">"Tüm mesajlar okunmadı"</string>
<string name="all_posts_read">"Tüm mesajlar okundu"</string> <string name="all_posts_read">"Tüm mesajlar okundu"</string>
<string name="nothing_here">"Burada hiçbir şey yok"</string>
<string name="tab_new">"Yeni"</string>
<string name="tab_read">"Tüm"</string>
<string name="tab_favs">"Favoriler"</string>
<string name="action_about">"Hakkında"</string>
<string name="marked_as_read">"Öğeleri oku"</string>
<string name="marked_as_unread">"Item unread"</string>
<string name="undo_string">"Geri al"</string> <string name="undo_string">"Geri al"</string>
<string name="addStringNoUrl">"Kaynakları eklemek için giriş yapın."</string> <string name="addStringNoUrl">"Kaynakları eklemek için giriş yapın."</string>
<string name="cant_get_sources">"Kaynakları listesi alınamıyor."</string> <string name="cant_get_sources">"Kaynakları listesi alınamıyor."</string>
<string name="cant_create_source">"Kaynak oluşturulamıyor."</string> <string name="cant_create_source">"Kaynak oluşturulamıyor."</string>
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string> <string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
<string name="cant_get_spouts">"Can't get spouts list. There may ben an api issue."</string> <string name="cant_get_spouts">"Spouts listesine girilemiyor."</string>
<string name="form_not_complete">"Form tamamlanamadı"</string> <string name="form_not_complete">"Form tamamlanamadı"</string>
<string name="pref_header_links">"Bağlantılar"</string> <string name="pref_header_links">"Bağlantılar"</string>
<string name="issue_tracker_link">"Sorun İzleyici"</string> <string name="issue_tracker_link">"Sorun İzleyici"</string>
@ -62,28 +55,18 @@
<string name="card_height_on">Kartların yüksekliği içeriğine göre ayarlanır</string> <string name="card_height_on">Kartların yüksekliği içeriğine göre ayarlanır</string>
<string name="card_height_off">Kart yüksekliği sabit olacak</string> <string name="card_height_off">Kart yüksekliği sabit olacak</string>
<string name="source_code">Kaynak kodu</string> <string name="source_code">Kaynak kodu</string>
<string name="drawer_error_loading_tags">Etiketler yükleme hatası</string> <string name="filter_item_tags">Etiketler</string>
<string name="drawer_item_filters">Filtreler</string> <string name="filter_item_sources">Kaynaklar</string>
<string name="drawer_action_clear">temizle</string>
<string name="drawer_item_tags">Etiketler</string>
<string name="drawer_item_sources">Kaynaklar</string>
<string name="drawer_action_edit">düzenle</string>
<string name="drawer_loading">Yükleniyor…</string>
<string name="menu_home_search">Ara</string> <string name="menu_home_search">Ara</string>
<string name="can_delete_source">Kaynak silinemiyor…</string> <string name="can_delete_source">Kaynak silinemiyor…</string>
<string name="base_url_error">Selfoss Örneğinizle iletişim kurmaya çalışırken bir sorun oluştu. Sorun devam ederse, lütfen benimle iletişime geçin.</string> <string name="base_url_error">Selfoss Örneğinizle iletişim kurmaya çalışırken bir sorun oluştu. Sorun devam ederse, lütfen benimle iletişime geçin.</string>
<string name="pref_header_theme">Temalar</string> <string name="pref_header_theme">Temalar</string>
<string name="default_theme">Varsayılan</string>
<string name="default_dark_theme">Varsayılan/koyu</string>
<string name="pref_selfoss_category">Selfoss Uygulaması</string> <string name="pref_selfoss_category">Selfoss Uygulaması</string>
<string name="pref_api_items_number_title">Yüklenen öğe numarası</string> <string name="pref_api_items_number_title">Yüklenen öğe numarası</string>
<string name="pref_hidden_tags">Hidden Tags</string>
<string name="pref_general_infinite_loading_title">Kaydırma üzerine daha fazla makale yükleyin</string> <string name="pref_general_infinite_loading_title">Kaydırma üzerine daha fazla makale yükleyin</string>
<string name="translation">Çeviri</string> <string name="translation">Çeviri</string>
<string name="cant_open_invalid_url">Öğe url geçersiz. Uygulama çökmeyeceği için bu sorunu çözmeye çalışıyorum.</string> <string name="cant_open_invalid_url">Öğe url geçersiz. Uygulama çökmeyeceği için bu sorunu çözmeye çalışıyorum.</string>
<string name="drawer_report_bug">Hata bildir</string>
<string name="items_number_should_be_number">Öğe sayısı bir tamsayı olmalıdır.</string> <string name="items_number_should_be_number">Öğe sayısı bir tamsayı olmalıdır.</string>
<string name="reader_action_more">Daha fazlasını görüntüle</string>
<string name="reader_action_open">Tarayıcıda aç</string> <string name="reader_action_open">Tarayıcıda aç</string>
<string name="reader_action_share">Paylaş</string> <string name="reader_action_share">Paylaş</string>
<string name="pref_switch_actions_pager_scroll_on">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>
@ -94,7 +77,6 @@
<string name="markall_dialog_message">This will mark all the items as read.</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">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="pref_switch_actions_pager_scroll_off">Don\'t mark articles as read when swiping.</string>
<string name="drawer_item_hidden_tags">Hidden Tags</string>
<string name="unmark">Mark item as unread</string> <string name="unmark">Mark item as unread</string>
<string name="pref_header_offline">Offline and cache</string> <string name="pref_header_offline">Offline and cache</string>
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string> <string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
@ -109,7 +91,7 @@
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string> <string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string> <string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string>
<string name="pref_switch_refresh_when_charging">Only refresh when phone is charging</string> <string name="pref_switch_refresh_when_charging">Only refresh when phone is charging</string>
<string name="loading_notification_title">Loading ...</string> <string name="loading_notification_title">Loading </string>
<string name="loading_notification_text">Selfoss is syncing your articles</string> <string name="loading_notification_text">Selfoss is syncing your articles</string>
<string name="notification_channel_sync">Sync notification</string> <string name="notification_channel_sync">Sync notification</string>
<string name="new_items_channel_sync">New items notification</string> <string name="new_items_channel_sync">New items notification</string>
@ -124,18 +106,27 @@
<string name="reader_text_align_left">Align left</string> <string name="reader_text_align_left">Align left</string>
<string name="reader_text_align_justify">Justify</string> <string name="reader_text_align_justify">Justify</string>
<string name="settings_reader_font">Reader font</string> <string name="settings_reader_font">Reader font</string>
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string> <string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string> <string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string> <string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string> <string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
<string name="pref_switch_enable_analytics">Enable analytics</string>
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string> <string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string> <string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string> <string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string> <string name="pref_switch_disable_acra">"Disable automatic bug reporting."</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"Burada hiçbir şey yok"</string>
<string name="tab_new">"Yeni"</string>
<string name="tab_read">"Tüm"</string>
<string name="tab_favs">"Favoriler"</string>
<string name="action_about">"Hakkında"</string>
<string name="marked_as_read">"Öğeleri oku"</string>
<string name="marked_as_unread">"Item unread"</string>
</resources> </resources>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<string name="app_name">"Selfoss 阅读器"</string> <string name="app_name">"Selfoss 阅读器"</string>
<string name="title_activity_login">"登录"</string> <string name="title_activity_login">"登录"</string>
<string name="prompt_password">"密码"</string> <string name="prompt_password">"密码"</string>
@ -7,10 +7,10 @@
<string name="error_invalid_password">"密码不够长"</string> <string name="error_invalid_password">"密码不够长"</string>
<string name="error_field_required">"必填字段"</string> <string name="error_field_required">"必填字段"</string>
<string name="prompt_url">"网址"</string> <string name="prompt_url">"网址"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"需要登录?"</string> <string name="withLoginSwitch">"需要登录?"</string>
<string name="login_url_problem">"哎呀。您可能需要在网址的末尾添加一个 \"/\"。"</string> <string name="login_url_problem">"哎呀。您可能需要在网址的末尾添加一个 \"/\"。"</string>
<string name="prompt_login">"用户名"</string> <string name="prompt_login">"用户名"</string>
<string name="label_share">"分享"</string>
<string name="readAll">"全部阅读"</string> <string name="readAll">"全部阅读"</string>
<string name="action_disconnect">"断开连接"</string> <string name="action_disconnect">"断开连接"</string>
<string name="title_activity_settings">"设置"</string> <string name="title_activity_settings">"设置"</string>
@ -23,13 +23,6 @@
<string name="wrong_infos">"再次检查您的详细信息。"</string> <string name="wrong_infos">"再次检查您的详细信息。"</string>
<string name="all_posts_not_read">"所有帖子都未读"</string> <string name="all_posts_not_read">"所有帖子都未读"</string>
<string name="all_posts_read">"所有帖子已读"</string> <string name="all_posts_read">"所有帖子已读"</string>
<string name="nothing_here">"暂无内容!"</string>
<string name="tab_new">"新建"</string>
<string name="tab_read">"所有"</string>
<string name="tab_favs">"收藏夹"</string>
<string name="action_about">"关于我们"</string>
<string name="marked_as_read">"已读"</string>
<string name="marked_as_unread">"未读条目"</string>
<string name="undo_string">"撤销"</string> <string name="undo_string">"撤销"</string>
<string name="addStringNoUrl">"登录以添加数据源。"</string> <string name="addStringNoUrl">"登录以添加数据源。"</string>
<string name="cant_get_sources">"无法获取数据列表。"</string> <string name="cant_get_sources">"无法获取数据列表。"</string>
@ -62,28 +55,18 @@
<string name="card_height_on">卡片高度将根据内容调整</string> <string name="card_height_on">卡片高度将根据内容调整</string>
<string name="card_height_off">卡片高度将被固定</string> <string name="card_height_off">卡片高度将被固定</string>
<string name="source_code">源代码</string> <string name="source_code">源代码</string>
<string name="drawer_error_loading_tags">加载标记时出错..。</string> <string name="filter_item_tags">标签</string>
<string name="drawer_item_filters">搜索条件</string> <string name="filter_item_sources">来源</string>
<string name="drawer_action_clear">清空</string>
<string name="drawer_item_tags">标签</string>
<string name="drawer_item_sources">来源</string>
<string name="drawer_action_edit">编辑</string>
<string name="drawer_loading">正在载入…</string>
<string name="menu_home_search">搜索</string> <string name="menu_home_search">搜索</string>
<string name="can_delete_source">无法删除数据源…</string> <string name="can_delete_source">无法删除数据源…</string>
<string name="base_url_error">与您的 Selfoss 通信时出现问题。如果问题一直存在,请与我联系。</string> <string name="base_url_error">与您的 Selfoss 通信时出现问题。如果问题一直存在,请与我联系。</string>
<string name="pref_header_theme">主题</string> <string name="pref_header_theme">主题</string>
<string name="default_theme">默认​​​​​</string>
<string name="default_dark_theme">默认值/暗</string>
<string name="pref_selfoss_category">塞尔福斯 Api</string> <string name="pref_selfoss_category">塞尔福斯 Api</string>
<string name="pref_api_items_number_title">已加载项目编号</string> <string name="pref_api_items_number_title">已加载项目编号</string>
<string name="pref_hidden_tags">隐藏标签</string>
<string name="pref_general_infinite_loading_title">翻页时载入更多文章</string> <string name="pref_general_infinite_loading_title">翻页时载入更多文章</string>
<string name="translation">翻译</string> <string name="translation">翻译</string>
<string name="cant_open_invalid_url">项目链接地址无效。我正在设法解决这个问题,以避免应用程序崩溃。</string> <string name="cant_open_invalid_url">项目链接地址无效。我正在设法解决这个问题,以避免应用程序崩溃。</string>
<string name="drawer_report_bug">报告错误</string>
<string name="items_number_should_be_number">项目数应为整数。</string> <string name="items_number_should_be_number">项目数应为整数。</string>
<string name="reader_action_more">阅读更多</string>
<string name="reader_action_open">在浏览器中打开</string> <string name="reader_action_open">在浏览器中打开</string>
<string name="reader_action_share">分享</string> <string name="reader_action_share">分享</string>
<string name="pref_switch_actions_pager_scroll_on">切换文章时将文章标记为已读。</string> <string name="pref_switch_actions_pager_scroll_on">切换文章时将文章标记为已读。</string>
@ -94,7 +77,6 @@
<string name="markall_dialog_message">这将标记所有项目为已读。</string> <string name="markall_dialog_message">这将标记所有项目为已读。</string>
<string name="pref_switch_actions_pager_scroll">滑动时标为已读</string> <string name="pref_switch_actions_pager_scroll">滑动时标为已读</string>
<string name="pref_switch_actions_pager_scroll_off">滑动时不标记文章为已读</string> <string name="pref_switch_actions_pager_scroll_off">滑动时不标记文章为已读</string>
<string name="drawer_item_hidden_tags">隐藏标签</string>
<string name="unmark">标记条目为未读</string> <string name="unmark">标记条目为未读</string>
<string name="pref_header_offline">离线和缓存</string> <string name="pref_header_offline">离线和缓存</string>
<string name="pref_switch_items_caching_off">文章不会被保存到设备内存,应用程序在离线时将无法阅读它们</string> <string name="pref_switch_items_caching_off">文章不会被保存到设备内存,应用程序在离线时将无法阅读它们</string>
@ -109,7 +91,7 @@
<string name="pref_switch_periodic_refresh_on">将定期同步文章</string> <string name="pref_switch_periodic_refresh_on">将定期同步文章</string>
<string name="pref_periodic_refresh_minutes_title"><![CDATA[同步间隔 (>= 15分钟)]]></string> <string name="pref_periodic_refresh_minutes_title"><![CDATA[同步间隔 (>= 15分钟)]]></string>
<string name="pref_switch_refresh_when_charging">仅在手机充电时刷新</string> <string name="pref_switch_refresh_when_charging">仅在手机充电时刷新</string>
<string name="loading_notification_title">加载中...</string> <string name="loading_notification_title">加载中</string>
<string name="loading_notification_text">Selfoss 正在同步您的文章</string> <string name="loading_notification_text">Selfoss 正在同步您的文章</string>
<string name="notification_channel_sync">同步通知</string> <string name="notification_channel_sync">同步通知</string>
<string name="new_items_channel_sync">新条目通知</string> <string name="new_items_channel_sync">新条目通知</string>
@ -124,18 +106,27 @@
<string name="reader_text_align_left">左对齐</string> <string name="reader_text_align_left">左对齐</string>
<string name="reader_text_align_justify">左右对齐</string> <string name="reader_text_align_justify">左右对齐</string>
<string name="settings_reader_font">阅读器字体</string> <string name="settings_reader_font">阅读器字体</string>
<string name="reader_static_bar_title">文章查看器中的静态底部栏</string>
<string name="reader_static_bar_on">底部栏将始终显示</string>
<string name="reader_static_bar_off">底部栏可以通过浮动按钮显示</string>
<string name="remove_source">删除源</string> <string name="remove_source">删除源</string>
<string name="pref_theme_title">浅色/深色模式</string> <string name="pref_theme_title">浅色/深色模式</string>
<string name="mode_dark">深色模式</string> <string name="mode_dark">深色模式</string>
<string name="mode_system">遵循系统设置</string> <string name="mode_system">遵循系统设置</string>
<string name="mode_light">浅色模式</string> <string name="mode_light">浅色模式</string>
<string name="drawer_error_loading_sources">Error loading sources…</string> <string name="gdpr_dialog_title">该应用不分享任何关于您的个人数据。</string>
<string name="pref_switch_enable_analytics">Enable analytics</string> <string name="gdpr_dialog_message"><![CDATA[崩溃报告发送现已启用。 可以从设置页面禁用它。 请记住,崩溃报告对于应用程序开发是必需的。]]></string>
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string> <string name="crash_toast_text">发生崩溃。请将细节发送给开发人员。</string>
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string> <string name="pref_switch_disable_acra">"禁用自动错误报告"</string>
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string> <string name="menu_home_filter">筛选器</string>
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string> <string name="application_selfoss_only">此应用只适用于 Selfoss 实例,不适用于其他 RSS 。</string>
<string name="menu_home_sources"></string>
<string name="update_source">更新源</string>
<string name="confirm_disconnect_title">断开连接?</string>
<string name="confirm_disconnect_description">您将断开与 selfoss 实例的连接。</string>
<string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"暂无内容!"</string>
<string name="tab_new">"新建"</string>
<string name="tab_read">"所有"</string>
<string name="tab_favs">"收藏夹"</string>
<string name="action_about">"关于我们"</string>
<string name="marked_as_read">"已读"</string>
<string name="marked_as_unread">"未读条目"</string>
</resources> </resources>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"> <resources>
<string name="app_name">"Selfoss 阅读器"</string> <string name="app_name">"Selfoss 阅读器"</string>
<string name="title_activity_login">"登录"</string> <string name="title_activity_login">"登录"</string>
<string name="prompt_password">"密码"</string> <string name="prompt_password">"密码"</string>
@ -7,10 +7,10 @@
<string name="error_invalid_password">"密码不够长"</string> <string name="error_invalid_password">"密码不够长"</string>
<string name="error_field_required">"欄位必填"</string> <string name="error_field_required">"欄位必填"</string>
<string name="prompt_url">"网址"</string> <string name="prompt_url">"网址"</string>
<string name="disable_ssl">"Disable SSL"</string>
<string name="withLoginSwitch">"需要登入?"</string> <string name="withLoginSwitch">"需要登入?"</string>
<string name="login_url_problem">"哎呀。您可能需要在网址的末尾添加一个 \"/\"。"</string> <string name="login_url_problem">"哎呀。您可能需要在网址的末尾添加一个 \"/\"。"</string>
<string name="prompt_login">"使用者名稱"</string> <string name="prompt_login">"使用者名稱"</string>
<string name="label_share">"分享"</string>
<string name="readAll">"全部阅读"</string> <string name="readAll">"全部阅读"</string>
<string name="action_disconnect">"断开连接"</string> <string name="action_disconnect">"断开连接"</string>
<string name="title_activity_settings">"设置"</string> <string name="title_activity_settings">"设置"</string>
@ -23,19 +23,12 @@
<string name="wrong_infos">"再次检查您的详细信息。"</string> <string name="wrong_infos">"再次检查您的详细信息。"</string>
<string name="all_posts_not_read">"所有帖子都未读"</string> <string name="all_posts_not_read">"所有帖子都未读"</string>
<string name="all_posts_read">"所有帖子已读"</string> <string name="all_posts_read">"所有帖子已读"</string>
<string name="nothing_here">"暂无内容!"</string>
<string name="tab_new">"新建"</string>
<string name="tab_read">"所有"</string>
<string name="tab_favs">"收藏夹"</string>
<string name="action_about">"关于我们"</string>
<string name="marked_as_read">"已读"</string>
<string name="marked_as_unread">"未讀項目"</string>
<string name="undo_string">"撤销"</string> <string name="undo_string">"撤销"</string>
<string name="addStringNoUrl">"登录以添加数据源。"</string> <string name="addStringNoUrl">"登录以添加数据源。"</string>
<string name="cant_get_sources">"无法获取数据列表。"</string> <string name="cant_get_sources">"无法获取数据列表。"</string>
<string name="cant_create_source">"无法创建源数据。"</string> <string name="cant_create_source">"无法创建源数据。"</string>
<string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string> <string name="cant_get_spouts_no_network">"Can't get spouts list because of a network issue."</string>
<string name="cant_get_spouts">"Can't get spouts list. There may ben an api issue."</string> <string name="cant_get_spouts">"无法获取数据列表"</string>
<string name="form_not_complete">"窗体未完成"</string> <string name="form_not_complete">"窗体未完成"</string>
<string name="pref_header_links">"链接"</string> <string name="pref_header_links">"链接"</string>
<string name="issue_tracker_link">"问题追踪器"</string> <string name="issue_tracker_link">"问题追踪器"</string>
@ -62,28 +55,18 @@
<string name="card_height_on">卡片高度将根据内容调整</string> <string name="card_height_on">卡片高度将根据内容调整</string>
<string name="card_height_off">卡片高度将被固定</string> <string name="card_height_off">卡片高度将被固定</string>
<string name="source_code">源代码</string> <string name="source_code">源代码</string>
<string name="drawer_error_loading_tags">加载标记时出错..。</string> <string name="filter_item_tags">标签</string>
<string name="drawer_item_filters">搜索条件</string> <string name="filter_item_sources">来源</string>
<string name="drawer_action_clear">清空</string>
<string name="drawer_item_tags">标签</string>
<string name="drawer_item_sources">来源</string>
<string name="drawer_action_edit">编辑</string>
<string name="drawer_loading">正在载入…</string>
<string name="menu_home_search">搜索</string> <string name="menu_home_search">搜索</string>
<string name="can_delete_source">无法删除数据源…</string> <string name="can_delete_source">无法删除数据源…</string>
<string name="base_url_error">与您的 Selfoss 通信时出现问题。如果问题一直存在,请与我联系。</string> <string name="base_url_error">与您的 Selfoss 通信时出现问题。如果问题一直存在,请与我联系。</string>
<string name="pref_header_theme">主题</string> <string name="pref_header_theme">主题</string>
<string name="default_theme">默认​​​​​</string>
<string name="default_dark_theme">默认值/暗</string>
<string name="pref_selfoss_category">塞尔福斯 Api</string> <string name="pref_selfoss_category">塞尔福斯 Api</string>
<string name="pref_api_items_number_title">已加载项目编号</string> <string name="pref_api_items_number_title">已加载项目编号</string>
<string name="pref_hidden_tags">Hidden Tags</string>
<string name="pref_general_infinite_loading_title">翻页时载入更多文章</string> <string name="pref_general_infinite_loading_title">翻页时载入更多文章</string>
<string name="translation">翻译</string> <string name="translation">翻译</string>
<string name="cant_open_invalid_url">项目链接地址无效。我正在设法解决这个问题,以避免应用程序崩溃。</string> <string name="cant_open_invalid_url">项目链接地址无效。我正在设法解决这个问题,以避免应用程序崩溃。</string>
<string name="drawer_report_bug">报告错误</string>
<string name="items_number_should_be_number">项目数应为整数。</string> <string name="items_number_should_be_number">项目数应为整数。</string>
<string name="reader_action_more">阅读更多</string>
<string name="reader_action_open">在浏览器中打开</string> <string name="reader_action_open">在浏览器中打开</string>
<string name="reader_action_share">分享</string> <string name="reader_action_share">分享</string>
<string name="pref_switch_actions_pager_scroll_on">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>
@ -94,7 +77,6 @@
<string name="markall_dialog_message">This will mark all the items as read.</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">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="pref_switch_actions_pager_scroll_off">Don\'t mark articles as read when swiping.</string>
<string name="drawer_item_hidden_tags">Hidden Tags</string>
<string name="unmark">Mark item as unread</string> <string name="unmark">Mark item as unread</string>
<string name="pref_header_offline">Offline and cache</string> <string name="pref_header_offline">Offline and cache</string>
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string> <string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
@ -109,7 +91,7 @@
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string> <string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string> <string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string>
<string name="pref_switch_refresh_when_charging">Only refresh when phone is charging</string> <string name="pref_switch_refresh_when_charging">Only refresh when phone is charging</string>
<string name="loading_notification_title">Loading ...</string> <string name="loading_notification_title">Loading </string>
<string name="loading_notification_text">Selfoss is syncing your articles</string> <string name="loading_notification_text">Selfoss is syncing your articles</string>
<string name="notification_channel_sync">Sync notification</string> <string name="notification_channel_sync">Sync notification</string>
<string name="new_items_channel_sync">New items notification</string> <string name="new_items_channel_sync">New items notification</string>
@ -124,18 +106,27 @@
<string name="reader_text_align_left">Align left</string> <string name="reader_text_align_left">Align left</string>
<string name="reader_text_align_justify">Justify</string> <string name="reader_text_align_justify">Justify</string>
<string name="settings_reader_font">Reader font</string> <string name="settings_reader_font">Reader font</string>
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
<string name="remove_source">Remove source</string> <string name="remove_source">Remove source</string>
<string name="pref_theme_title">Light/Dark mode</string> <string name="pref_theme_title">Light/Dark mode</string>
<string name="mode_dark">Dark mode</string> <string name="mode_dark">Dark mode</string>
<string name="mode_system">Follow the system setting</string> <string name="mode_system">Follow the system setting</string>
<string name="mode_light">Light mode</string> <string name="mode_light">Light mode</string>
<string name="drawer_error_loading_sources">Error loading sources…</string>
<string name="pref_switch_enable_analytics">Enable analytics</string>
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string> <string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
<string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string> <string name="gdpr_dialog_message"><![CDATA[Crash reports sending is now enabled. It can be disabled from the settings page. Keep in mind that crash reports are essential for the app development.]]></string>
<string name="crash_toast_text">A crash occured. Sending the details to the developper.</string> <string name="crash_toast_text">A crash occured. Sending the details to the developper.</string>
<string name="pref_switch_disable_acra">"Disable automatic bug reporting. "</string> <string name="pref_switch_disable_acra">"Disable automatic bug reporting."</string>
<string name="menu_home_filter">Filters</string>
<string name="application_selfoss_only">This app only works with a Selfoss instance, and no other RSS feed.</string>
<string name="menu_home_sources">Sources</string>
<string name="update_source">Update source</string>
<string name="confirm_disconnect_title">Disconnect ?</string>
<string name="confirm_disconnect_description">You will be disconnected from your selfoss instance.</string>
<string name="no_browser">Can\'t open a link without a browser installed</string>
<string name="nothing_here">"暂无内容!"</string>
<string name="tab_new">"新建"</string>
<string name="tab_read">"所有"</string>
<string name="tab_favs">"收藏夹"</string>
<string name="action_about">"关于我们"</string>
<string name="marked_as_read">"已读"</string>
<string name="marked_as_unread">"未讀項目"</string>
</resources> </resources>

View File

@ -12,4 +12,5 @@
<color name="refresh_progress_2">@color/colorAccent</color> <color name="refresh_progress_2">@color/colorAccent</color>
<color name="refresh_progress_3">@color/pink</color> <color name="refresh_progress_3">@color/pink</color>
<color name="dark">#FF282828</color> <color name="dark">#FF282828</color>
<color name="transparent_dark_background">#33000000</color>
</resources> </resources>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="unread_action" type="id" />
<item name="open_action" type="id" />
<item name="share_action" type="id" />
</resources>

View File

@ -11,22 +11,4 @@
<item>2</item> <!--MODE_NIGHT_YES--> <item>2</item> <!--MODE_NIGHT_YES-->
<item>-1</item> <!--MODE_NIGHT_FOLLOW_SYSTEM--> <item>-1</item> <!--MODE_NIGHT_FOLLOW_SYSTEM-->
</string-array> </string-array>
<string-array name="Voice">
<item>Male</item>
<item>Female</item>
</string-array>
<string-array name="VoiceAlias">
<item>"usenglishmale"</item>
<item>"usenglishfemale"</item>
<item>"ukenglishmale"</item>
<item>"ukenglishfemale"</item>
<item>"eurfrenchmale"</item>
<item>"eurfrenchfemale"</item>
<item>"eurspanishmale"</item>
<item>"eurspanishfemale"</item>
<item>"euritalianmale"</item>
<item>"euritalianfemale"</item>
</string-array>
</resources> </resources>

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