Compare commits
165 Commits
v161805146
...
v171811315
Author | SHA1 | Date | |
---|---|---|---|
7da7d49277 | |||
9b45365441 | |||
91a7464bce | |||
51add226eb | |||
332e9f5108 | |||
0b91087c07 | |||
ebbb1ba0f8 | |||
e9143ae852 | |||
42e8ecee78 | |||
4efd76fcbc | |||
fb1614070e | |||
c473dd7227 | |||
76bddb195d | |||
1e02ad2041 | |||
f6ab909f8b | |||
7e520e9bed | |||
32e2d05014 | |||
40d9c97f73 | |||
1aa68d3449 | |||
aeeac8cccd | |||
7292edf997 | |||
f49256c72f | |||
d02b28b81f | |||
08117043dd | |||
63496c993e | |||
00ef542e49 | |||
a78c6e6b33 | |||
363eaf9bf9 | |||
fec6683701 | |||
1549edb647 | |||
3de48ba162 | |||
a2a3d6f1a7 | |||
ccab2c7648 | |||
880dd1db5c | |||
ed18fea356 | |||
9816b20bf6 | |||
0bb2195bff | |||
ab2d0c4036 | |||
99fc417109 | |||
dc304ef8c1 | |||
c5511880bc | |||
5fe76d735e | |||
3064b3b835 | |||
70dc8af3ce | |||
53c8c241da | |||
bdc4f5680b | |||
ed290573b2 | |||
1616a97a8a | |||
d090183007 | |||
de337fd260 | |||
12dc206323 | |||
d47c508dee | |||
ed75f55437 | |||
5ad3ad4a57 | |||
aeac1bd1d4 | |||
4d18085072 | |||
0c9f8214ca | |||
a7ce7ce02e | |||
820986c7f0 | |||
8079cae745 | |||
6f067bd258 | |||
b6ade0f212 | |||
27dadc1be3 | |||
95e4162b4c | |||
f75557585e | |||
1b4c26919b | |||
ad085bf129 | |||
8fcd551105 | |||
a0954700e2 | |||
9705560442 | |||
1f47a13ce5 | |||
6f0ff2c975 | |||
76e5477986 | |||
7f308d5be3 | |||
54a43c83e8 | |||
8fe7266c84 | |||
d7a46b27b7 | |||
2257d09fdd | |||
047c5481c4 | |||
8a6719f934 | |||
51a692f3be | |||
b333f93171 | |||
89d34a1a71 | |||
8788e920ce | |||
d306fb53d3 | |||
374537b5c7 | |||
598149d4cd | |||
50bcf18096 | |||
a089ced03f | |||
1f18dddf8b | |||
f5934e240e | |||
6b8da2eacf | |||
f4757a67b7 | |||
6edeb9d840 | |||
43ce0fd7bc | |||
5599f5a8fc | |||
6fd45ceb4f | |||
05ad8aac29 | |||
fa4f2476b7 | |||
00818a94e9 | |||
5d5250e44a | |||
3052b33132 | |||
50de6f8b5b | |||
f88a2f415f | |||
96f9813e01 | |||
fee739cb17 | |||
b1814c63b9 | |||
c1d45678f8 | |||
3d34e59a94 | |||
f1133bea8b | |||
ec64c88ff1 | |||
be66dbba6c | |||
8926cdbbf5 | |||
a956870dec | |||
8ed7951c9b | |||
5569a47674 | |||
0dc6981913 | |||
4984f2f7ad | |||
3b6891c84a | |||
4901e7174c | |||
8d70e68fe2 | |||
d3e1527b70 | |||
0c201301f2 | |||
6090590f24 | |||
06b88c783d | |||
bb75ebf635 | |||
7d7d0014be | |||
b3f8d44794 | |||
29d1e38340 | |||
2be872e61e | |||
377c5518f7 | |||
21be7357b5 | |||
d47ba2c820 | |||
a64b14614a | |||
6a88192e77 | |||
aa7c630818 | |||
7fb54f14c7 | |||
3d709c02b7 | |||
339d384561 | |||
50338d51af | |||
92dbabf899 | |||
0043021390 | |||
70ba9b20da | |||
7fda0a04a1 | |||
3db3157dc9 | |||
2089fe60ca | |||
9606d36670 | |||
869cf64c54 | |||
f57ec1f6c0 | |||
361eea9a06 | |||
838b4056ac | |||
0c0a98510b | |||
be642ed06f | |||
fd77f38e95 | |||
c9baab7267 | |||
86985cfd5b | |||
1327a4e069 | |||
c46acbc579 | |||
4c6a403fae | |||
78920022bd | |||
7b16c41e82 | |||
3389f8bd09 | |||
8dc25c527d | |||
46d6bd57c1 | |||
db014fe13d |
2
.gitignore
vendored
@ -217,5 +217,3 @@ gradle-app.setting
|
||||
release/
|
||||
|
||||
crowdin.properties
|
||||
|
||||
publish-version.sh
|
26
CHANGELOG.md
@ -1,5 +1,29 @@
|
||||
**1.7.x**
|
||||
|
||||
- Added experimental issue to set a default timeout. Should work for #238.
|
||||
|
||||
- Closing #220.
|
||||
|
||||
- Start of #238. "Add a quick shortcut to open the app on offline mode ?"
|
||||
|
||||
- Closes #216. Issue with selfoss version 2.19.
|
||||
|
||||
- Closes #179. Sync of read/unread/star/unstar items on background task or on app reload with network available.
|
||||
|
||||
- Closes #33. Background sync with settings.
|
||||
|
||||
- Closing #1. Initial article caching.
|
||||
|
||||
- Closing #228 by removing the list action bar. Action buttons are exclusively on the card view from now on.
|
||||
|
||||
- Closing #38. Only doing api calls on network available.
|
||||
|
||||
**1.6.x**
|
||||
|
||||
- Handling hidden tags.
|
||||
|
||||
- Fixed pre-lolipop issue with automatic theme changes.
|
||||
|
||||
- Removed all Build config things.
|
||||
|
||||
- Removed firebase and fabric.
|
||||
@ -12,6 +36,8 @@
|
||||
|
||||
- Versions updates.
|
||||
|
||||
- Fixes #215, #208.
|
||||
|
||||
**1.5.7.x**
|
||||
|
||||
- Added confirmation to the mark as read and update menues.
|
||||
|
21
README.md
@ -1,19 +1,20 @@
|
||||
# ReaderForSelfoss
|
||||
|
||||
[](https://join.slack.com/t/readerforselfoss/shared_invite/enQtMjkyNzc3NjM2Mjc1LTUzZTZhOGM5YjQ1MTI5MWZiODRjMjE1ZDBmMzQxZmQ3NWZhYTNhMTBjNGEwNmE2ZGFjODU5NjUxZjBkMWJmMDQ)
|
||||
|
||||
[](http://jenkins.amine-bou.fr/job/ReaderForSelfoss/)
|
||||
|
||||
[](https://www.codetriage.com/aminecmi/readerforselfoss)
|
||||
|
||||
[](https://crowdin.com/project/readerforselfoss)
|
||||
|
||||
This is the repo of [Reader For Selfoss](https://play.google.com/store/apps/details?id=apps.amine.bou.readerforselfoss&hl=en).
|
||||
[](https://join.slack.com/t/readerforselfoss/shared_invite/enQtMjkyNzc3NjM2Mjc1LTUzZTZhOGM5YjQ1MTI5MWZiODRjMjE1ZDBmMzQxZmQ3NWZhYTNhMTBjNGEwNmE2ZGFjODU5NjUxZjBkMWJmMDQ) [](https://jenkins.amine-bou.fr/job/ReaderForSelfoss/) [](https://www.codetriage.com/aminecmi/readerforselfoss) [](https://crowdin.com/project/readerforselfoss)
|
||||
|
||||
It's an RSS Reader for Android, that **only** works with [Selfoss](https://selfoss.aditu.de/)
|
||||
|
||||
The last APK built from source is available [here](https://jenkins.amine-bou.fr/job/ReaderForSelfoss/lastSuccessfulBuild/artifact/SignApksBuilder-out/selfoss-key/selfoss/app-githubConfig-release-unsigned.apk/app-githubConfig-release.apk).
|
||||
<a href='https://play.google.com/store/apps/details?id=apps.amine.bou.readerforselfoss'><img alt='Get it on Google Play' src='https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png' height="100"/></a> <a href="https://f-droid.org/packages/apps.amine.bou.readerforselfoss"><img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="100"></a>
|
||||
|
||||
Also, the last APK built from source is available [here](https://jenkins.amine-bou.fr/job/ReaderForSelfoss/lastSuccessfulBuild/artifact/SignApksBuilder-out/selfoss-key/selfoss/app-githubConfig-release-unsigned.apk/app-githubConfig-release.apk).
|
||||
|
||||
## Join the alpha channel
|
||||
|
||||
**Keep in mind, it could be instable, but you'll have the new updates faster**
|
||||
|
||||
- First, join the google [group](https://groups.google.com/d/forum/reader-for-selfoss-alpha-testing).
|
||||
- Then, join the [alpha channel](https://play.google.com/apps/testing/apps.amine.bou.readerforselfoss) of the app.
|
||||
- You'll be able to update the app for the current alpha version.
|
||||
|
||||
## Want to help ?
|
||||
|
||||
|
@ -24,26 +24,25 @@ def versionNameFromGit() {
|
||||
return gitVersion()
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
repositories {
|
||||
}
|
||||
|
||||
android {
|
||||
compileOptions {
|
||||
sourceCompatibility 1.8
|
||||
targetCompatibility 1.8
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
compileSdkVersion 27
|
||||
buildToolsVersion '27.0.3'
|
||||
compileSdkVersion 28
|
||||
buildToolsVersion '28.0.3'
|
||||
defaultConfig {
|
||||
applicationId "apps.amine.bou.readerforselfoss"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 27
|
||||
targetSdkVersion 28
|
||||
versionCode versionCodeFromGit()
|
||||
versionName versionNameFromGit()
|
||||
|
||||
@ -56,7 +55,14 @@ android {
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
|
||||
// tests
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
javaCompileOptions {
|
||||
annotationProcessorOptions {
|
||||
arguments = ["room.schemaLocation":
|
||||
"$projectDir/schemas".toString()]
|
||||
}
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
@ -87,35 +93,29 @@ android {
|
||||
|
||||
dependencies {
|
||||
// Testing
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
|
||||
androidTestImplementation 'com.android.support.test:runner:1.0.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-beta02'
|
||||
androidTestImplementation 'androidx.test:runner:1.1.0-beta02'
|
||||
// Espresso-contrib for DatePicker, RecyclerView, Drawer actions, Accessibility checks, CountingIdlingResource
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-contrib:3.0.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.1.0-beta02'
|
||||
// Espresso-intents for validation and stubbing of Intents
|
||||
androidTestImplementation 'com.android.support.test.espresso:espresso-intents:3.0.1'
|
||||
|
||||
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.0-beta02'
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
|
||||
// Android Support
|
||||
implementation 'com.android.support:appcompat-v7:27.1.1'
|
||||
implementation 'com.android.support:design:27.1.1'
|
||||
implementation 'com.android.support:recyclerview-v7:27.1.1'
|
||||
implementation 'com.android.support:support-v4:27.1.1'
|
||||
implementation 'com.android.support:support-vector-drawable:27.1.1'
|
||||
implementation 'com.android.support:customtabs:27.1.1'
|
||||
implementation 'com.android.support:cardview-v7:27.1.1'
|
||||
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
|
||||
implementation "androidx.appcompat:appcompat:$android_version"
|
||||
implementation "com.google.android.material:material:$android_version"
|
||||
implementation "androidx.recyclerview:recyclerview:$android_version"
|
||||
implementation "androidx.legacy:legacy-support-v4:$android_version"
|
||||
implementation "androidx.vectordrawable:vectordrawable:$android_version"
|
||||
implementation "androidx.browser:browser:$android_version"
|
||||
implementation "androidx.cardview:cardview:$android_version"
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2'
|
||||
|
||||
//multidex
|
||||
implementation 'com.android.support:multidex:1.0.3'
|
||||
|
||||
// Intro
|
||||
implementation 'agency.tango.android:material-intro-screen:0.0.5'
|
||||
implementation 'androidx.multidex:multidex:2.0.0'
|
||||
|
||||
// About
|
||||
implementation('com.mikepenz:aboutlibraries:6.0.0@aar') {
|
||||
implementation('com.mikepenz:aboutlibraries:6.2.0@aar') {
|
||||
transitive = true
|
||||
}
|
||||
|
||||
@ -126,8 +126,8 @@ dependencies {
|
||||
implementation 'com.burgstaller:okhttp-digest:1.12'
|
||||
|
||||
// Material-ish things
|
||||
implementation 'com.ashokvarma.android:bottom-navigation-bar:2.0.3'
|
||||
implementation 'com.github.jd-alexander:LikeButton:0.2.1'
|
||||
implementation 'com.ashokvarma.android:bottom-navigation-bar:2.0.5'
|
||||
implementation 'com.github.jd-alexander:LikeButton:0.2.3'
|
||||
implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
|
||||
|
||||
// glide
|
||||
@ -135,26 +135,32 @@ dependencies {
|
||||
implementation 'com.github.bumptech.glide:okhttp3-integration:4.1.1'
|
||||
|
||||
// Asking politely users to rate the app
|
||||
implementation 'com.github.stkent:amplify:2.1.0'
|
||||
implementation 'com.github.stkent:amplify:2.2.0'
|
||||
|
||||
// Drawer
|
||||
implementation 'co.zsmb:materialdrawer-kt:1.2.1'
|
||||
implementation 'com.anupcowkur:reservoir:3.1.0'
|
||||
implementation 'co.zsmb:materialdrawer-kt:2.0.1'
|
||||
|
||||
// Themes
|
||||
implementation 'com.52inc:scoops:1.0.0'
|
||||
implementation'com.jrummyapps:colorpicker:2.1.7'
|
||||
|
||||
implementation 'com.jaredrummler:colorpicker:1.0.2'
|
||||
implementation 'com.github.rubensousa:floatingtoolbar:1.5.1'
|
||||
|
||||
// Pager
|
||||
implementation 'me.relex:circleindicator:1.2.2@aar'
|
||||
implementation 'me.relex:circleindicator:2.0.0@aar'
|
||||
|
||||
implementation 'androidx.core:core-ktx:0.3'
|
||||
implementation 'androidx.core:core-ktx:1.0.0'
|
||||
|
||||
// Crash
|
||||
implementation "ch.acra:acra-http:5.1.3"
|
||||
implementation "ch.acra:acra-dialog:5.1.3"
|
||||
implementation 'ch.acra:acra-http:5.2.1'
|
||||
implementation 'ch.acra:acra-dialog:5.2.1'
|
||||
|
||||
implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
|
||||
|
||||
implementation "androidx.room:room-runtime:$room_version"
|
||||
kapt "androidx.room:room-compiler:$room_version"
|
||||
|
||||
implementation "android.arch.work:work-runtime-ktx:$work_version"
|
||||
}
|
||||
|
||||
|
||||
|
19
app/proguard-rules.pro
vendored
@ -30,22 +30,6 @@
|
||||
<fields>;
|
||||
}
|
||||
|
||||
|
||||
##Retrofit
|
||||
#-keep class com.google.gson.** { *; }
|
||||
#-keep class com.google.inject.** { *; }
|
||||
#-keep class org.apache.http.** { *; }
|
||||
#-keep class org.apache.james.mime4j.** { *; }
|
||||
#-keep class javax.inject.** { *; }
|
||||
#-keep class retrofit.** { *; }
|
||||
#-keepclassmembernames interface * {
|
||||
# @retrofit.http.* <methods>;
|
||||
#}
|
||||
#-keep class retrofit.** { *; }
|
||||
#-keep class apps.amine.bou.readerforselfoss.api.selfoss.model.** { *; }
|
||||
#-keepclassmembernames interface * {
|
||||
# @retrofit.http.* <methods>;
|
||||
#}
|
||||
-dontwarn okio.**
|
||||
-dontwarn retrofit2.Platform$Java8
|
||||
-keep class retrofit.** { *; }
|
||||
@ -76,3 +60,6 @@
|
||||
-dontwarn javax.annotation.**
|
||||
|
||||
-keep class android.support.v7.widget.SearchView { *; }
|
||||
|
||||
# maybe remove later ?
|
||||
-keep class * extends androidx.fragment.app.Fragment
|
||||
|
@ -0,0 +1,96 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "08ca537d7ac9d4dd216e8e395d70801a",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "tags",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tag` TEXT NOT NULL, `color` TEXT NOT NULL, `unread` INTEGER NOT NULL, PRIMARY KEY(`tag`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "tag",
|
||||
"columnName": "tag",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "color",
|
||||
"columnName": "color",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unread",
|
||||
"columnName": "unread",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"tag"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "sources",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `title` TEXT NOT NULL, `tags` TEXT NOT NULL, `spout` TEXT NOT NULL, `error` TEXT NOT NULL, `icon` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tags",
|
||||
"columnName": "tags",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "spout",
|
||||
"columnName": "spout",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "error",
|
||||
"columnName": "error",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "icon",
|
||||
"columnName": "icon",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"08ca537d7ac9d4dd216e8e395d70801a\")"
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,176 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 2,
|
||||
"identityHash": "6fa6944b04100d68eab61039876a8804",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "tags",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tag` TEXT NOT NULL, `color` TEXT NOT NULL, `unread` INTEGER NOT NULL, PRIMARY KEY(`tag`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "tag",
|
||||
"columnName": "tag",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "color",
|
||||
"columnName": "color",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unread",
|
||||
"columnName": "unread",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"tag"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "sources",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `title` TEXT NOT NULL, `tags` TEXT NOT NULL, `spout` TEXT NOT NULL, `error` TEXT NOT NULL, `icon` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tags",
|
||||
"columnName": "tags",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "spout",
|
||||
"columnName": "spout",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "error",
|
||||
"columnName": "error",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "icon",
|
||||
"columnName": "icon",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "items",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `datetime` TEXT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `unread` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `thumbnail` TEXT NOT NULL, `icon` TEXT NOT NULL, `link` TEXT NOT NULL, `sourcetitle` TEXT NOT NULL, `tags` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "datetime",
|
||||
"columnName": "datetime",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "content",
|
||||
"columnName": "content",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unread",
|
||||
"columnName": "unread",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbnail",
|
||||
"columnName": "thumbnail",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "icon",
|
||||
"columnName": "icon",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "link",
|
||||
"columnName": "link",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "sourcetitle",
|
||||
"columnName": "sourcetitle",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tags",
|
||||
"columnName": "tags",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"6fa6944b04100d68eab61039876a8804\")"
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,226 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 3,
|
||||
"identityHash": "7ad9c4961992c13b670128485ebb3efc",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "tags",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tag` TEXT NOT NULL, `color` TEXT NOT NULL, `unread` INTEGER NOT NULL, PRIMARY KEY(`tag`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "tag",
|
||||
"columnName": "tag",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "color",
|
||||
"columnName": "color",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unread",
|
||||
"columnName": "unread",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"tag"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "sources",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `title` TEXT NOT NULL, `tags` TEXT NOT NULL, `spout` TEXT NOT NULL, `error` TEXT NOT NULL, `icon` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tags",
|
||||
"columnName": "tags",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "spout",
|
||||
"columnName": "spout",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "error",
|
||||
"columnName": "error",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "icon",
|
||||
"columnName": "icon",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "items",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `datetime` TEXT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `unread` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `thumbnail` TEXT NOT NULL, `icon` TEXT NOT NULL, `link` TEXT NOT NULL, `sourcetitle` TEXT NOT NULL, `tags` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "datetime",
|
||||
"columnName": "datetime",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "content",
|
||||
"columnName": "content",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unread",
|
||||
"columnName": "unread",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbnail",
|
||||
"columnName": "thumbnail",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "icon",
|
||||
"columnName": "icon",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "link",
|
||||
"columnName": "link",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "sourcetitle",
|
||||
"columnName": "sourcetitle",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tags",
|
||||
"columnName": "tags",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "actions",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `articleid` TEXT NOT NULL, `read` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `unstarred` INTEGER NOT NULL)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "articleId",
|
||||
"columnName": "articleid",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "read",
|
||||
"columnName": "read",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unread",
|
||||
"columnName": "unread",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unstarred",
|
||||
"columnName": "unstarred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"7ad9c4961992c13b670128485ebb3efc\")"
|
||||
]
|
||||
}
|
||||
}
|
@ -2,27 +2,27 @@ package apps.amine.bou.readerforselfoss
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.support.test.InstrumentationRegistry
|
||||
import android.support.test.espresso.Espresso.onView
|
||||
import android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
||||
import android.support.test.espresso.action.ViewActions.click
|
||||
import android.support.test.espresso.action.ViewActions.closeSoftKeyboard
|
||||
import android.support.test.espresso.action.ViewActions.pressBack
|
||||
import android.support.test.espresso.action.ViewActions.pressKey
|
||||
import android.support.test.espresso.action.ViewActions.typeText
|
||||
import android.support.test.espresso.assertion.ViewAssertions.matches
|
||||
import android.support.test.espresso.contrib.DrawerActions
|
||||
import android.support.test.espresso.intent.Intents
|
||||
import android.support.test.espresso.intent.Intents.intended
|
||||
import android.support.test.espresso.intent.Intents.times
|
||||
import android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
||||
import android.support.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||
import android.support.test.espresso.matcher.ViewMatchers.isRoot
|
||||
import android.support.test.espresso.matcher.ViewMatchers.withContentDescription
|
||||
import android.support.test.espresso.matcher.ViewMatchers.withId
|
||||
import android.support.test.espresso.matcher.ViewMatchers.withText
|
||||
import android.support.test.rule.ActivityTestRule
|
||||
import android.support.test.runner.AndroidJUnit4
|
||||
import androidx.test.InstrumentationRegistry
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
||||
import androidx.test.espresso.action.ViewActions.click
|
||||
import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
|
||||
import androidx.test.espresso.action.ViewActions.pressBack
|
||||
import androidx.test.espresso.action.ViewActions.pressKey
|
||||
import androidx.test.espresso.action.ViewActions.typeText
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.contrib.DrawerActions
|
||||
import androidx.test.espresso.intent.Intents
|
||||
import androidx.test.espresso.intent.Intents.intended
|
||||
import androidx.test.espresso.intent.Intents.times
|
||||
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isRoot
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||
import androidx.test.rule.ActivityTestRule
|
||||
import androidx.test.runner.AndroidJUnit4
|
||||
import android.view.KeyEvent
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import org.junit.After
|
||||
|
@ -1,91 +0,0 @@
|
||||
package apps.amine.bou.readerforselfoss
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.support.test.InstrumentationRegistry.getInstrumentation
|
||||
import android.support.test.espresso.Espresso.onView
|
||||
import android.support.test.espresso.action.ViewActions.click
|
||||
import android.support.test.espresso.assertion.ViewAssertions.matches
|
||||
import android.support.test.espresso.intent.Intents
|
||||
import android.support.test.espresso.intent.Intents.intended
|
||||
import android.support.test.espresso.intent.Intents.times
|
||||
import android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
||||
import android.support.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||
import android.support.test.espresso.matcher.ViewMatchers.withId
|
||||
import android.support.test.espresso.matcher.ViewMatchers.withText
|
||||
import android.support.test.rule.ActivityTestRule
|
||||
import android.support.test.runner.AndroidJUnit4
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.util.*
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class IntroActivityEspressoTest {
|
||||
|
||||
@Rule @JvmField
|
||||
val rule = ActivityTestRule(IntroActivity::class.java, true, false)
|
||||
|
||||
@Before
|
||||
fun clearData() {
|
||||
val editor =
|
||||
getInstrumentation().targetContext
|
||||
.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
editor.clear()
|
||||
editor.commit()
|
||||
|
||||
Intents.init()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nextEachTimes() {
|
||||
|
||||
rule.launchActivity(Intent())
|
||||
|
||||
onView(withText(R.string.intro_hello_title)).check(matches(isDisplayed()))
|
||||
onView(withId(R.id.button_next)).perform(click())
|
||||
onView(withText(R.string.intro_needs_selfoss_message)).check(matches(isDisplayed()))
|
||||
onView(withId(R.id.button_next)).perform(click())
|
||||
onView(withText(R.string.intro_all_set_message)).check(matches(isDisplayed()))
|
||||
onView(withId(R.id.button_next)).perform(click())
|
||||
|
||||
intended(hasComponent(IntroActivity::class.java.name), times(1))
|
||||
intended(hasComponent(LoginActivity::class.java.name), times(1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nextBackRandomTimes() {
|
||||
val max = 5
|
||||
val min = 1
|
||||
|
||||
val random = (Random().nextInt(max + 1 - min)) + min
|
||||
|
||||
rule.launchActivity(Intent())
|
||||
|
||||
onView(withText(R.string.intro_hello_title)).check(matches(isDisplayed()))
|
||||
onView(withId(R.id.button_next)).perform(click())
|
||||
|
||||
repeat(random) { _ ->
|
||||
onView(withText(R.string.intro_needs_selfoss_message)).check(matches(isDisplayed()))
|
||||
onView(withId(R.id.button_next)).perform(click())
|
||||
onView(withText(R.string.intro_all_set_message)).check(matches(isDisplayed()))
|
||||
onView(withId(R.id.button_back)).perform(click())
|
||||
}
|
||||
|
||||
onView(withId(R.id.button_next)).perform(click())
|
||||
onView(withText(R.string.intro_all_set_message)).check(matches(isDisplayed()))
|
||||
onView(withId(R.id.button_next)).perform(click())
|
||||
|
||||
intended(hasComponent(IntroActivity::class.java.name), times(1))
|
||||
intended(hasComponent(LoginActivity::class.java.name), times(1))
|
||||
}
|
||||
|
||||
@After
|
||||
fun releaseIntents() {
|
||||
Intents.release()
|
||||
}
|
||||
}
|
@ -2,25 +2,25 @@ package apps.amine.bou.readerforselfoss
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.support.test.InstrumentationRegistry
|
||||
import android.support.test.espresso.Espresso.onView
|
||||
import android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
||||
import android.support.test.espresso.action.ViewActions.click
|
||||
import android.support.test.espresso.action.ViewActions.closeSoftKeyboard
|
||||
import android.support.test.espresso.action.ViewActions.pressBack
|
||||
import android.support.test.espresso.action.ViewActions.typeText
|
||||
import android.support.test.espresso.assertion.ViewAssertions.matches
|
||||
import android.support.test.espresso.intent.Intents
|
||||
import android.support.test.espresso.intent.Intents.intended
|
||||
import android.support.test.espresso.intent.Intents.times
|
||||
import android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
||||
import android.support.test.espresso.matcher.ViewMatchers
|
||||
import android.support.test.espresso.matcher.ViewMatchers.isRoot
|
||||
import android.support.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
|
||||
import android.support.test.espresso.matcher.ViewMatchers.withId
|
||||
import android.support.test.espresso.matcher.ViewMatchers.withText
|
||||
import android.support.test.rule.ActivityTestRule
|
||||
import android.support.test.runner.AndroidJUnit4
|
||||
import androidx.test.InstrumentationRegistry
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
||||
import androidx.test.espresso.action.ViewActions.click
|
||||
import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
|
||||
import androidx.test.espresso.action.ViewActions.pressBack
|
||||
import androidx.test.espresso.action.ViewActions.typeText
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.intent.Intents
|
||||
import androidx.test.espresso.intent.Intents.intended
|
||||
import androidx.test.espresso.intent.Intents.times
|
||||
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
||||
import androidx.test.espresso.matcher.ViewMatchers
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isRoot
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||
import androidx.test.rule.ActivityTestRule
|
||||
import androidx.test.runner.AndroidJUnit4
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import com.mikepenz.aboutlibraries.ui.LibsActivity
|
||||
import org.junit.After
|
||||
|
@ -3,13 +3,13 @@ package apps.amine.bou.readerforselfoss
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.test.InstrumentationRegistry.getInstrumentation
|
||||
import android.support.test.espresso.intent.Intents
|
||||
import android.support.test.espresso.intent.Intents.intended
|
||||
import android.support.test.espresso.intent.Intents.times
|
||||
import android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
||||
import android.support.test.rule.ActivityTestRule
|
||||
import android.support.test.runner.AndroidJUnit4
|
||||
import androidx.test.InstrumentationRegistry.getInstrumentation
|
||||
import androidx.test.espresso.intent.Intents
|
||||
import androidx.test.espresso.intent.Intents.intended
|
||||
import androidx.test.espresso.intent.Intents.times
|
||||
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
||||
import androidx.test.rule.ActivityTestRule
|
||||
import androidx.test.runner.AndroidJUnit4
|
||||
import org.junit.After
|
||||
|
||||
import org.junit.Before
|
||||
@ -45,7 +45,6 @@ class MainActivityEspressoTest {
|
||||
rule.launchActivity(intent)
|
||||
|
||||
intended(hasComponent(MainActivity::class.java.name))
|
||||
intended(hasComponent(IntroActivity::class.java.name))
|
||||
intended(hasComponent(LoginActivity::class.java.name), times(0))
|
||||
}
|
||||
|
||||
@ -58,7 +57,6 @@ class MainActivityEspressoTest {
|
||||
|
||||
intended(hasComponent(MainActivity::class.java.name))
|
||||
intended(hasComponent(LoginActivity::class.java.name))
|
||||
intended(hasComponent(IntroActivity::class.java.name), times(0))
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -1,7 +1,7 @@
|
||||
package apps.amine.bou.readerforselfoss
|
||||
|
||||
import android.support.design.widget.TextInputLayout
|
||||
import android.support.test.espresso.matcher.ViewMatchers
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import androidx.test.espresso.matcher.ViewMatchers
|
||||
import android.view.View
|
||||
import org.hamcrest.Description
|
||||
import org.hamcrest.Matcher
|
||||
|
@ -3,6 +3,7 @@
|
||||
package="apps.amine.bou.readerforselfoss"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
@ -20,10 +21,9 @@
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".IntroActivity"
|
||||
android:theme="@style/Theme.Intro">
|
||||
|
||||
<meta-data android:name="android.app.shortcuts"
|
||||
android:resource="@xml/shortcuts" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".LoginActivity"
|
||||
|
@ -4,8 +4,8 @@ import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.constraint.ConstraintLayout
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
@ -44,10 +44,11 @@ class AddSourceActivity : AppCompatActivity() {
|
||||
|
||||
setContentView(R.layout.activity_add_source)
|
||||
|
||||
// TODO: input bubble cursor
|
||||
Scoop.getInstance()
|
||||
.bind(this, Toppings.PRIMARY.value, toolbar)
|
||||
.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
|
||||
val scoop = Scoop.getInstance()
|
||||
scoop.bind(this, Toppings.PRIMARY.value, toolbar)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
|
||||
}
|
||||
|
||||
val drawable = nameInput.background
|
||||
drawable.setColorFilter(appColors.colorAccent, PorterDuff.Mode.SRC_ATOP)
|
||||
@ -88,6 +89,7 @@ class AddSourceActivity : AppCompatActivity() {
|
||||
this,
|
||||
this@AddSourceActivity,
|
||||
prefs.getBoolean("isSelfSignedCert", false),
|
||||
prefs.getString("api_timeout", "-1").toLong(),
|
||||
prefs.getBoolean("should_log_everything", false)
|
||||
)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
|
@ -6,31 +6,45 @@ import android.content.SharedPreferences
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.v4.view.MenuItemCompat
|
||||
import android.support.v7.app.AlertDialog
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.support.v7.widget.DividerItemDecoration
|
||||
import android.support.v7.widget.GridLayoutManager
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import android.support.v7.widget.SearchView
|
||||
import android.support.v7.widget.StaggeredGridLayoutManager
|
||||
import android.support.v7.widget.helper.ItemTouchHelper
|
||||
import androidx.core.view.MenuItemCompat
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.DividerItemDecoration
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import android.util.Log
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.work.Constraints
|
||||
import androidx.work.ExistingPeriodicWorkPolicy
|
||||
import androidx.work.NetworkType
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
import androidx.work.PeriodicWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import apps.amine.bou.readerforselfoss.adapters.ItemCardAdapter
|
||||
import apps.amine.bou.readerforselfoss.adapters.ItemListAdapter
|
||||
import apps.amine.bou.readerforselfoss.adapters.ItemsAdapter
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Sources
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Source
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Stats
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Tag
|
||||
import apps.amine.bou.readerforselfoss.background.LoadingWorker
|
||||
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity
|
||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_1_2
|
||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_2_3
|
||||
import apps.amine.bou.readerforselfoss.settings.SettingsActivity
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.themes.Toppings
|
||||
@ -42,14 +56,14 @@ import apps.amine.bou.readerforselfoss.utils.drawer.CustomUrlPrimaryDrawerItem
|
||||
import apps.amine.bou.readerforselfoss.utils.flattenTags
|
||||
import apps.amine.bou.readerforselfoss.utils.longHash
|
||||
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
|
||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
||||
import apps.amine.bou.readerforselfoss.utils.persistence.toEntity
|
||||
import apps.amine.bou.readerforselfoss.utils.persistence.toView
|
||||
import co.zsmb.materialdrawerkt.builders.accountHeader
|
||||
import co.zsmb.materialdrawerkt.builders.drawer
|
||||
import co.zsmb.materialdrawerkt.builders.footer
|
||||
import co.zsmb.materialdrawerkt.draweritems.badgeable.primaryItem
|
||||
import co.zsmb.materialdrawerkt.draweritems.profile.profile
|
||||
import com.anupcowkur.reservoir.Reservoir
|
||||
import com.anupcowkur.reservoir.ReservoirGetCallback
|
||||
import com.anupcowkur.reservoir.ReservoirPutCallback
|
||||
import com.ashokvarma.bottomnavigation.BottomNavigationBar
|
||||
import com.ashokvarma.bottomnavigation.BottomNavigationItem
|
||||
import com.ashokvarma.bottomnavigation.TextBadgeItem
|
||||
@ -65,14 +79,19 @@ import com.mikepenz.materialdrawer.model.DividerDrawerItem
|
||||
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
|
||||
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem
|
||||
import kotlinx.android.synthetic.main.activity_home.*
|
||||
import kotlinx.android.synthetic.main.fragment_article.*
|
||||
import org.acra.ACRA
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
|
||||
private val MENU_PREFERENCES = 12302
|
||||
private val DRAWER_ID_TAGS = 100101L
|
||||
private val DRAWER_ID_HIDDEN_TAGS = 101100L
|
||||
private val DRAWER_ID_SOURCES = 100110L
|
||||
private val DRAWER_ID_FILTERS = 100111L
|
||||
private val UNREAD_SHOWN = 1
|
||||
@ -82,7 +101,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
private var items: ArrayList<Item> = ArrayList()
|
||||
private var allItems: ArrayList<Item> = ArrayList()
|
||||
|
||||
private var clickBehavior = false
|
||||
private var debugReadingItems = false
|
||||
private var shouldLogEverything = false
|
||||
private var internalBrowser = false
|
||||
@ -94,13 +112,18 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
private var itemsNumber: Int = 200
|
||||
private var elementsShown: Int = 0
|
||||
private var maybeTagFilter: Tag? = null
|
||||
private var maybeSourceFilter: Sources? = null
|
||||
private var maybeSourceFilter: Source? = null
|
||||
private var maybeSearchFilter: String? = null
|
||||
private var userIdentifier: String = ""
|
||||
private var displayAccountHeader: Boolean = false
|
||||
private var infiniteScroll: Boolean = false
|
||||
private var lastFetchDone: Boolean = false
|
||||
private var itemsCaching: Boolean = false
|
||||
private var hiddenTags: List<String> = emptyList()
|
||||
|
||||
private var periodicRefresh = false
|
||||
private var refreshMinutes: Long = 360L
|
||||
private var refreshWhenChargingOnly = false
|
||||
|
||||
private lateinit var tabNewBadge: TextBadgeItem
|
||||
private lateinit var tabArchiveBadge: TextBadgeItem
|
||||
@ -113,7 +136,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
private lateinit var appColors: AppColors
|
||||
private var offset: Int = 0
|
||||
private var firstVisible: Int = 0
|
||||
private var recyclerViewScrollListener: RecyclerView.OnScrollListener? = null
|
||||
private lateinit var recyclerViewScrollListener: RecyclerView.OnScrollListener
|
||||
private lateinit var settings: SharedPreferences
|
||||
|
||||
private var recyclerAdapter: RecyclerView.Adapter<*>? = null
|
||||
@ -122,10 +145,14 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
private var badgeAll: Int = -1
|
||||
private var badgeFavs: Int = -1
|
||||
|
||||
private var fromTabShortcut: Boolean = false
|
||||
private var offlineShortcut: Boolean = false
|
||||
|
||||
private lateinit var tagsBadge: Map<Long, Int>
|
||||
|
||||
data class DrawerData(val tags: List<Tag>?, val sources: List<Sources>?)
|
||||
private lateinit var db: AppDatabase
|
||||
|
||||
data class DrawerData(val tags: List<Tag>?, val sources: List<Source>?)
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
@ -137,6 +164,13 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
fromTabShortcut = intent.getIntExtra("shortcutTab", -1) != -1
|
||||
offlineShortcut = intent.getBooleanExtra("startOffline", false)
|
||||
|
||||
if (fromTabShortcut) {
|
||||
elementsShown = intent.getIntExtra("shortcutTab", UNREAD_SHOWN)
|
||||
}
|
||||
|
||||
setContentView(R.layout.activity_home)
|
||||
|
||||
handleThemeBinding()
|
||||
@ -146,6 +180,12 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
Amplify.getSharedInstance().promptIfReady(promptView)
|
||||
}
|
||||
|
||||
db = Room.databaseBuilder(
|
||||
applicationContext,
|
||||
AppDatabase::class.java, "selfoss-database"
|
||||
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).build()
|
||||
|
||||
|
||||
customTabActivityHelper = CustomTabActivityHelper()
|
||||
|
||||
sharedPref = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
@ -155,6 +195,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
this,
|
||||
this@HomeActivity,
|
||||
settings.getBoolean("isSelfSignedCert", false),
|
||||
sharedPref.getString("api_timeout", "-1").toLong(),
|
||||
shouldLogEverything
|
||||
)
|
||||
items = ArrayList()
|
||||
@ -166,30 +207,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
handleSwipeRefreshLayout()
|
||||
}
|
||||
|
||||
private fun handleGDPRDialog(GDPRShown: Boolean) {
|
||||
val sharedEditor = sharedPref.edit()
|
||||
if (!GDPRShown) {
|
||||
val alertDialog = AlertDialog.Builder(this).create()
|
||||
alertDialog.setTitle(getString(R.string.gdpr_dialog_title))
|
||||
alertDialog.setMessage(getString(R.string.gdpr_dialog_message))
|
||||
alertDialog.setButton(
|
||||
AlertDialog.BUTTON_NEUTRAL,
|
||||
"OK",
|
||||
{ dialog, _ ->
|
||||
sharedEditor.putBoolean("GDPR_shown", true)
|
||||
sharedEditor.commit()
|
||||
dialog.dismiss()
|
||||
}
|
||||
)
|
||||
alertDialog.show()
|
||||
}
|
||||
|
||||
if (sharedPref.getString("acra.user.email", "").isNotEmpty()) {
|
||||
sharedEditor.remove("acra.user.email");
|
||||
sharedEditor.commit();
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleSwipeRefreshLayout() {
|
||||
swipeRefreshLayout.setColorSchemeResources(
|
||||
R.color.refresh_progress_1,
|
||||
@ -197,6 +214,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
R.color.refresh_progress_3
|
||||
)
|
||||
swipeRefreshLayout.setOnRefreshListener {
|
||||
offlineShortcut = false
|
||||
allItems = ArrayList()
|
||||
lastFetchDone = false
|
||||
handleDrawerItems()
|
||||
@ -209,8 +227,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
|
||||
) {
|
||||
override fun getSwipeDirs(
|
||||
recyclerView: RecyclerView?,
|
||||
viewHolder: RecyclerView.ViewHolder?
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder
|
||||
): Int =
|
||||
if (elementsShown != UNREAD_SHOWN) {
|
||||
0
|
||||
@ -239,11 +257,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
is ItemListAdapter -> adapter.removeItemAtIndex(position)
|
||||
}
|
||||
|
||||
if (items.size > 0) {
|
||||
badgeNew--
|
||||
reloadBadgeContent()
|
||||
|
||||
val tagHashes = i.tags.split(",").map { it.longHash() }
|
||||
val tagHashes = i.tags.tags.split(",").map { it.longHash() }
|
||||
tagsBadge = tagsBadge.map {
|
||||
if (tagHashes.contains(it.key)) {
|
||||
(it.key to (it.value - 1))
|
||||
@ -252,31 +269,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
}
|
||||
}.toMap()
|
||||
reloadTagsBadges()
|
||||
} else {
|
||||
tabNewBadge.hide()
|
||||
}
|
||||
|
||||
val manager = recyclerView.layoutManager
|
||||
val lastVisibleItem: Int = when (manager) {
|
||||
is StaggeredGridLayoutManager -> manager.findLastCompletelyVisibleItemPositions(
|
||||
null
|
||||
).last()
|
||||
is GridLayoutManager -> manager.findLastCompletelyVisibleItemPosition()
|
||||
else -> 0
|
||||
}
|
||||
|
||||
Log.e("TAGTAG", "$lastVisibleItem === ${items.size} && ${items.size} <= ${maxItemNumber()} && ${!lastFetchDone}x")
|
||||
if (lastVisibleItem === items.size &&
|
||||
items.size <= maxItemNumber() &&
|
||||
!lastFetchDone
|
||||
) {
|
||||
if (maxItemNumber() <= itemsNumber) {
|
||||
lastFetchDone = true
|
||||
}
|
||||
getElementsAccordingToTab(
|
||||
appendResults = true,
|
||||
offsetOverride = lastVisibleItem
|
||||
)
|
||||
// Just load everythin
|
||||
if (items.size <= 0) {
|
||||
getElementsAccordingToTab()
|
||||
}
|
||||
} else {
|
||||
Toast.makeText(
|
||||
@ -334,6 +330,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
|
||||
bottomBar.setMode(BottomNavigationBar.MODE_SHIFTING)
|
||||
bottomBar.setBackgroundStyle(BottomNavigationBar.BACKGROUND_STYLE_STATIC)
|
||||
|
||||
if (fromTabShortcut) {
|
||||
bottomBar.selectTab(elementsShown - 1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
@ -342,14 +342,14 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
// TODO: Make this the only appcolors init
|
||||
appColors = AppColors(this@HomeActivity)
|
||||
|
||||
handleDrawerItems()
|
||||
|
||||
sharedPref = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
|
||||
editor = settings.edit()
|
||||
|
||||
handleSharedPrefs()
|
||||
|
||||
handleDrawerItems()
|
||||
|
||||
handleThemeUpdate()
|
||||
|
||||
reloadLayoutManager()
|
||||
@ -365,6 +365,33 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
getElementsAccordingToTab()
|
||||
|
||||
handleGDPRDialog(sharedPref.getBoolean("GDPR_shown", false))
|
||||
|
||||
handleRecurringTask()
|
||||
|
||||
handleOfflineActions()
|
||||
}
|
||||
|
||||
private fun getAndStoreAllItems() {
|
||||
api.allItems().enqueue(object : Callback<List<Item>> {
|
||||
override fun onFailure(call: Call<List<Item>>, t: Throwable) {
|
||||
}
|
||||
|
||||
override fun onResponse(
|
||||
call: Call<List<Item>>,
|
||||
response: Response<List<Item>>
|
||||
) {
|
||||
thread {
|
||||
if (response.body() != null) {
|
||||
val apiItems = (response.body() as ArrayList<Item>).filter {
|
||||
maybeTagFilter != null || filter(it.tags.tags)
|
||||
} as ArrayList<Item>
|
||||
db.itemsDao().deleteAllItems()
|
||||
db.itemsDao()
|
||||
.insertAllItems(*(apiItems.map { it.toEntity() }).toTypedArray())
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
@ -375,7 +402,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
private fun handleSharedPrefs() {
|
||||
debugReadingItems = sharedPref.getBoolean("read_debug", false)
|
||||
shouldLogEverything = sharedPref.getBoolean("should_log_everything", false)
|
||||
clickBehavior = sharedPref.getBoolean("tab_on_tap", false)
|
||||
internalBrowser = sharedPref.getBoolean("prefer_internal_browser", true)
|
||||
articleViewer = sharedPref.getBoolean("prefer_article_viewer", true)
|
||||
shouldBeCardView = sharedPref.getBoolean("card_view_active", false)
|
||||
@ -386,19 +412,37 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
userIdentifier = sharedPref.getString("unique_id", "")
|
||||
displayAccountHeader = sharedPref.getBoolean("account_header_displaying", false)
|
||||
infiniteScroll = sharedPref.getBoolean("infinite_loading", false)
|
||||
itemsCaching = sharedPref.getBoolean("items_caching", false)
|
||||
hiddenTags = if (sharedPref.getString("hidden_tags", "").isNotEmpty()) {
|
||||
sharedPref.getString("hidden_tags", "").replace("\\s".toRegex(), "").split(",")
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
periodicRefresh = sharedPref.getBoolean("periodic_refresh", false)
|
||||
refreshWhenChargingOnly = sharedPref.getBoolean("refresh_when_charging", false)
|
||||
refreshMinutes = sharedPref.getString("periodic_refresh_minutes", "360").toLong()
|
||||
|
||||
if (refreshMinutes <= 15) {
|
||||
refreshMinutes = 15
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleThemeBinding() {
|
||||
Scoop.getInstance()
|
||||
.bind(this, Toppings.PRIMARY.value, toolBar)
|
||||
.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
|
||||
val scoop = Scoop.getInstance()
|
||||
scoop.bind(this, Toppings.PRIMARY.value, toolBar)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleThemeUpdate() {
|
||||
|
||||
Scoop.getInstance()
|
||||
.update(Toppings.PRIMARY.value, appColors.colorPrimary)
|
||||
.update(Toppings.PRIMARY_DARK.value, appColors.colorPrimaryDark)
|
||||
val scoop = Scoop.getInstance()
|
||||
scoop.update(Toppings.PRIMARY.value, appColors.colorPrimary)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
scoop.update(Toppings.PRIMARY_DARK.value, appColors.colorPrimaryDark)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleDrawer() {
|
||||
@ -474,7 +518,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
)
|
||||
}
|
||||
} else {
|
||||
tagsBadge = maybeTags.map {
|
||||
val filteredTags = maybeTags.filterNot { hiddenTags.contains(it.tag) }
|
||||
tagsBadge = filteredTags.map {
|
||||
val gd = GradientDrawable()
|
||||
val color = try {
|
||||
Color.parseColor(it.color)
|
||||
@ -509,7 +554,54 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
}
|
||||
}
|
||||
|
||||
fun handleSources(maybeSources: List<Sources>?) {
|
||||
fun handleHiddenTags(maybeTags: List<Tag>?) {
|
||||
if (maybeTags == null) {
|
||||
if (loadedFromCache) {
|
||||
drawer.addItem(
|
||||
SecondaryDrawerItem()
|
||||
.withName(getString(R.string.drawer_error_loading_tags))
|
||||
.withSelectable(false)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
val filteredHiddenTags: List<Tag> =
|
||||
maybeTags.filter { hiddenTags.contains(it.tag) }
|
||||
tagsBadge = filteredHiddenTags.map {
|
||||
val gd = GradientDrawable()
|
||||
val color = try {
|
||||
Color.parseColor(it.color)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
appColors.colorPrimary
|
||||
}
|
||||
|
||||
gd.setColor(color)
|
||||
gd.shape = GradientDrawable.RECTANGLE
|
||||
gd.setSize(30, 30)
|
||||
gd.cornerRadius = 30F
|
||||
drawer.addItem(
|
||||
PrimaryDrawerItem()
|
||||
.withName(it.tag)
|
||||
.withIdentifier(it.tag.longHash())
|
||||
.withIcon(gd)
|
||||
.withBadge("${it.unread}")
|
||||
.withBadgeStyle(
|
||||
BadgeStyle().withTextColor(Color.WHITE)
|
||||
.withColor(appColors.colorAccent)
|
||||
)
|
||||
.withOnDrawerItemClickListener { _, _, _ ->
|
||||
allItems = ArrayList()
|
||||
maybeTagFilter = it
|
||||
getElementsAccordingToTab()
|
||||
false
|
||||
}
|
||||
)
|
||||
|
||||
(it.tag.longHash() to it.unread)
|
||||
}.toMap()
|
||||
}
|
||||
}
|
||||
|
||||
fun handleSources(maybeSources: List<Source>?) {
|
||||
if (maybeSources == null) {
|
||||
if (loadedFromCache) {
|
||||
drawer.addItem(
|
||||
@ -552,6 +644,16 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
false
|
||||
}
|
||||
)
|
||||
if (hiddenTags.isNotEmpty()) {
|
||||
drawer.addItem(DividerDrawerItem())
|
||||
drawer.addItem(
|
||||
SecondaryDrawerItem()
|
||||
.withName(getString(R.string.drawer_item_hidden_tags))
|
||||
.withIdentifier(DRAWER_ID_HIDDEN_TAGS)
|
||||
.withSelectable(false)
|
||||
)
|
||||
handleHiddenTags(maybeDrawerData.tags)
|
||||
}
|
||||
drawer.addItem(DividerDrawerItem())
|
||||
drawer.addItem(
|
||||
SecondaryDrawerItem()
|
||||
@ -598,14 +700,19 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
|
||||
|
||||
if (!loadedFromCache) {
|
||||
Reservoir.putAsync(
|
||||
"drawerData", maybeDrawerData, object : ReservoirPutCallback {
|
||||
override fun onSuccess() {
|
||||
if (maybeDrawerData.tags != null) {
|
||||
thread {
|
||||
val tagEntities = maybeDrawerData.tags.map { it.toEntity() }
|
||||
db.drawerDataDao().insertAllTags(*tagEntities.toTypedArray())
|
||||
}
|
||||
}
|
||||
if (maybeDrawerData.sources != null) {
|
||||
thread {
|
||||
val sourceEntities =
|
||||
maybeDrawerData.sources.map { it.toEntity() }
|
||||
db.drawerDataDao().insertAllSources(*sourceEntities.toTypedArray())
|
||||
}
|
||||
|
||||
override fun onFailure(p0: Exception?) {
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if (!loadedFromCache) {
|
||||
@ -627,13 +734,14 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
|
||||
fun drawerApiCalls(maybeDrawerData: DrawerData?) {
|
||||
var tags: List<Tag>? = null
|
||||
var sources: List<Sources>?
|
||||
var sources: List<Source>?
|
||||
|
||||
fun sourcesApiCall() {
|
||||
api.sources.enqueue(object : Callback<List<Sources>> {
|
||||
if (this@HomeActivity.isNetworkAccessible(null, offlineShortcut)) {
|
||||
api.sources.enqueue(object : Callback<List<Source>> {
|
||||
override fun onResponse(
|
||||
call: Call<List<Sources>>?,
|
||||
response: Response<List<Sources>>
|
||||
call: Call<List<Source>>?,
|
||||
response: Response<List<Source>>
|
||||
) {
|
||||
sources = response.body()
|
||||
val apiDrawerData = DrawerData(tags, sources)
|
||||
@ -642,11 +750,17 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<List<Sources>>?, t: Throwable?) {
|
||||
override fun onFailure(call: Call<List<Source>>?, t: Throwable?) {
|
||||
val apiDrawerData = DrawerData(tags, null)
|
||||
if ((maybeDrawerData != null && maybeDrawerData != apiDrawerData) || maybeDrawerData == null) {
|
||||
handleDrawerData(apiDrawerData)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (this@HomeActivity.isNetworkAccessible(null, offlineShortcut)) {
|
||||
api.tags.enqueue(object : Callback<List<Tag>> {
|
||||
override fun onResponse(
|
||||
call: Call<List<Tag>>,
|
||||
@ -661,6 +775,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
drawer.addItem(
|
||||
PrimaryDrawerItem().withName(getString(R.string.drawer_loading)).withSelectable(
|
||||
@ -668,18 +783,14 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
)
|
||||
)
|
||||
|
||||
val resultType = object : TypeToken<DrawerData>() {}.type
|
||||
Reservoir.getAsync(
|
||||
"drawerData", resultType, object : ReservoirGetCallback<DrawerData> {
|
||||
override fun onSuccess(maybeDrawerData: DrawerData?) {
|
||||
handleDrawerData(maybeDrawerData, loadedFromCache = true)
|
||||
drawerApiCalls(maybeDrawerData)
|
||||
thread {
|
||||
var drawerData = DrawerData(db.drawerDataDao().tags().map { it.toView() },
|
||||
db.drawerDataDao().sources().map { it.toView() })
|
||||
runOnUiThread {
|
||||
handleDrawerData(drawerData, loadedFromCache = true)
|
||||
drawerApiCalls(drawerData)
|
||||
}
|
||||
|
||||
override fun onFailure(p0: Exception?) {
|
||||
drawerApiCalls(null)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun reloadLayoutManager() {
|
||||
@ -690,7 +801,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
when (currentManager) {
|
||||
is StaggeredGridLayoutManager ->
|
||||
if (!shouldBeCardView) {
|
||||
layoutManager = GridLayoutManager(this, calculateNoOfColumns())
|
||||
layoutManager = GridLayoutManager(
|
||||
this,
|
||||
calculateNoOfColumns()
|
||||
)
|
||||
recyclerView.layoutManager = layoutManager
|
||||
}
|
||||
is GridLayoutManager ->
|
||||
@ -706,7 +820,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
else ->
|
||||
if (currentManager == null) {
|
||||
if (!shouldBeCardView) {
|
||||
layoutManager = GridLayoutManager(this, calculateNoOfColumns())
|
||||
layoutManager = GridLayoutManager(
|
||||
this,
|
||||
calculateNoOfColumns()
|
||||
)
|
||||
recyclerView.layoutManager = layoutManager
|
||||
} else {
|
||||
layoutManager = StaggeredGridLayoutManager(
|
||||
@ -749,6 +866,45 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
override fun onTabSelected(position: Int) {
|
||||
offset = 0
|
||||
lastFetchDone = false
|
||||
|
||||
if (itemsCaching) {
|
||||
|
||||
if (!swipeRefreshLayout.isRefreshing) {
|
||||
swipeRefreshLayout.post { swipeRefreshLayout.isRefreshing = true }
|
||||
}
|
||||
|
||||
thread {
|
||||
val dbItems = db.itemsDao().items().map { it.toView() }
|
||||
runOnUiThread {
|
||||
if (dbItems.isNotEmpty()) {
|
||||
items = when (position) {
|
||||
0 -> ArrayList(dbItems.filter { it.unread })
|
||||
1 -> ArrayList(dbItems.filter { !it.unread })
|
||||
2 -> ArrayList(dbItems.filter { it.starred })
|
||||
else -> ArrayList(dbItems.filter { it.unread })
|
||||
}
|
||||
handleListResult()
|
||||
when (position) {
|
||||
0 -> getUnRead()
|
||||
1 -> getRead()
|
||||
2 -> getStarred()
|
||||
else -> Unit
|
||||
}
|
||||
} else {
|
||||
if (this@HomeActivity.isNetworkAccessible(this@HomeActivity.findViewById(R.id.coordLayout), offlineShortcut)) {
|
||||
when (position) {
|
||||
0 -> getUnRead()
|
||||
1 -> getRead()
|
||||
2 -> getStarred()
|
||||
else -> Unit
|
||||
}
|
||||
getAndStoreAllItems()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
when (position) {
|
||||
0 -> getUnRead()
|
||||
1 -> getRead()
|
||||
@ -756,14 +912,14 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun handleInfiniteScroll() {
|
||||
if (recyclerViewScrollListener == null) {
|
||||
recyclerViewScrollListener = object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(localRecycler: RecyclerView?, dx: Int, dy: Int) {
|
||||
if (localRecycler != null && dy > 0) {
|
||||
override fun onScrolled(localRecycler: RecyclerView, dx: Int, dy: Int) {
|
||||
if (dy > 0) {
|
||||
val manager = recyclerView.layoutManager
|
||||
val lastVisibleItem: Int = when (manager) {
|
||||
is StaggeredGridLayoutManager -> manager.findLastCompletelyVisibleItemPositions(
|
||||
@ -779,7 +935,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
recyclerView.clearOnScrollListeners()
|
||||
recyclerView.addOnScrollListener(recyclerViewScrollListener)
|
||||
@ -798,13 +953,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
appendResults: Boolean = false,
|
||||
offsetOverride: Int? = null
|
||||
) {
|
||||
offset = if (appendResults && offsetOverride === null) {
|
||||
(offset + itemsNumber)
|
||||
} else {
|
||||
offsetOverride ?: 0
|
||||
}
|
||||
firstVisible = if (appendResults) firstVisible else 0
|
||||
|
||||
fun doGetAccordingToTab() {
|
||||
when (elementsShown) {
|
||||
UNREAD_SHOWN -> getUnRead(appendResults)
|
||||
READ_SHOWN -> getRead(appendResults)
|
||||
@ -813,16 +962,65 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
}
|
||||
}
|
||||
|
||||
offset = if (appendResults && offsetOverride === null) {
|
||||
(offset + itemsNumber)
|
||||
} else {
|
||||
offsetOverride ?: 0
|
||||
}
|
||||
firstVisible = if (appendResults) firstVisible else 0
|
||||
|
||||
if (itemsCaching) {
|
||||
|
||||
if (!swipeRefreshLayout.isRefreshing) {
|
||||
swipeRefreshLayout.post { swipeRefreshLayout.isRefreshing = true }
|
||||
}
|
||||
|
||||
thread {
|
||||
val dbItems = db.itemsDao().items().map { it.toView() }
|
||||
runOnUiThread {
|
||||
if (dbItems.isNotEmpty()) {
|
||||
items = when (elementsShown) {
|
||||
UNREAD_SHOWN -> ArrayList(dbItems.filter { it.unread })
|
||||
READ_SHOWN -> ArrayList(dbItems.filter { !it.unread })
|
||||
FAV_SHOWN -> ArrayList(dbItems.filter { it.starred })
|
||||
else -> ArrayList(dbItems.filter { it.unread })
|
||||
}
|
||||
handleListResult()
|
||||
doGetAccordingToTab()
|
||||
} else {
|
||||
if (this@HomeActivity.isNetworkAccessible(this@HomeActivity.findViewById(R.id.coordLayout), offlineShortcut)) {
|
||||
doGetAccordingToTab()
|
||||
getAndStoreAllItems()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
doGetAccordingToTab()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun filter(tags: String): Boolean {
|
||||
val tagsList = tags.replace("\\s".toRegex(), "").split(",")
|
||||
return tagsList.intersect(hiddenTags).isEmpty()
|
||||
}
|
||||
|
||||
private fun doCallTo(
|
||||
appendResults: Boolean,
|
||||
toastMessage: Int,
|
||||
call: (String?, Long?, String?) -> Call<List<Item>>
|
||||
) {
|
||||
fun handleItemsResponse(response: Response<List<Item>>) {
|
||||
val shouldUpdate = (response.body() != items)
|
||||
val shouldUpdate = (response.body()?.toSet() != items.toSet())
|
||||
if (response.body() != null) {
|
||||
if (shouldUpdate) {
|
||||
getAndStoreAllItems()
|
||||
items = response.body() as ArrayList<Item>
|
||||
items = items.filter {
|
||||
maybeTagFilter != null || filter(it.tags.tags)
|
||||
} as ArrayList<Item>
|
||||
|
||||
if (allItems.isEmpty()) {
|
||||
allItems = items
|
||||
@ -838,9 +1036,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
allItems = ArrayList()
|
||||
}
|
||||
}
|
||||
if (shouldUpdate) {
|
||||
|
||||
handleListResult(appendResults)
|
||||
}
|
||||
|
||||
if (!appendResults) mayBeEmpty()
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
@ -850,6 +1047,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
swipeRefreshLayout.post { swipeRefreshLayout.isRefreshing = true }
|
||||
}
|
||||
|
||||
if (this@HomeActivity.isNetworkAccessible(this@HomeActivity.findViewById(R.id.coordLayout), offlineShortcut)) {
|
||||
call(maybeTagFilter?.tag, maybeSourceFilter?.id?.toLong(), maybeSearchFilter)
|
||||
.enqueue(object : Callback<List<Item>> {
|
||||
override fun onResponse(
|
||||
@ -868,6 +1066,9 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
).show()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
swipeRefreshLayout.post { swipeRefreshLayout.isRefreshing = false }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getUnRead(appendResults: Boolean = false) {
|
||||
@ -928,34 +1129,33 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
this,
|
||||
items,
|
||||
api,
|
||||
db,
|
||||
customTabActivityHelper,
|
||||
internalBrowser,
|
||||
articleViewer,
|
||||
fullHeightCards,
|
||||
appColors,
|
||||
debugReadingItems,
|
||||
userIdentifier,
|
||||
{
|
||||
userIdentifier
|
||||
) {
|
||||
updateItems(it)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
recyclerAdapter =
|
||||
ItemListAdapter(
|
||||
this,
|
||||
items,
|
||||
api,
|
||||
db,
|
||||
customTabActivityHelper,
|
||||
clickBehavior,
|
||||
internalBrowser,
|
||||
articleViewer,
|
||||
debugReadingItems,
|
||||
userIdentifier,
|
||||
appColors,
|
||||
{
|
||||
appColors
|
||||
) {
|
||||
updateItems(it)
|
||||
}
|
||||
)
|
||||
|
||||
recyclerView.addItemDecoration(
|
||||
DividerItemDecoration(
|
||||
@ -977,7 +1177,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
}
|
||||
|
||||
private fun reloadBadges() {
|
||||
if (displayUnreadCount || displayAllCount) {
|
||||
if (this@HomeActivity.isNetworkAccessible(null, offlineShortcut) && (displayUnreadCount || displayAllCount)) {
|
||||
api.stats.enqueue(object : Callback<Stats> {
|
||||
override fun onResponse(call: Call<Stats>, response: Response<Stats>) {
|
||||
if (response.body() != null) {
|
||||
@ -1083,7 +1283,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
when (item.itemId) {
|
||||
R.id.refresh -> {
|
||||
needsConfirmation(R.string.menu_home_refresh, R.string.refresh_dialog_message, {
|
||||
if (this@HomeActivity.isNetworkAccessible(null, offlineShortcut)) {
|
||||
needsConfirmation(R.string.menu_home_refresh, R.string.refresh_dialog_message) {
|
||||
api.update().enqueue(object : Callback<String> {
|
||||
override fun onResponse(
|
||||
call: Call<String>,
|
||||
@ -1105,25 +1306,28 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
}
|
||||
})
|
||||
Toast.makeText(this, R.string.refresh_in_progress, Toast.LENGTH_SHORT).show()
|
||||
})
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
R.id.readAll -> {
|
||||
if (elementsShown == UNREAD_SHOWN) {
|
||||
needsConfirmation(R.string.readAll, R.string.markall_dialog_message, {
|
||||
needsConfirmation(R.string.readAll, R.string.markall_dialog_message) {
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
val ids = allItems.map { it.id }
|
||||
val itemsByTag: Map<Long, Int> =
|
||||
allItems.flattenTags()
|
||||
.groupBy { it.tags.longHash() }
|
||||
.groupBy { it.tags.tags.longHash() }
|
||||
.map { it.key to it.value.size }
|
||||
.toMap()
|
||||
|
||||
fun readAllDebug(e: Throwable) {
|
||||
// TODO: debug
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, this@HomeActivity)
|
||||
}
|
||||
|
||||
if (ids.isNotEmpty()) {
|
||||
if (ids.isNotEmpty() && this@HomeActivity.isNetworkAccessible(null, offlineShortcut)) {
|
||||
api.readAll(ids).enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<SuccessResponse>,
|
||||
@ -1160,7 +1364,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
@ -1190,7 +1393,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
).show()
|
||||
}
|
||||
handleListResult()
|
||||
})
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
@ -1209,8 +1412,79 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
else -> badgeNew // if !elementsShown then unread are fetched.
|
||||
}
|
||||
|
||||
fun updateItems(adapterItems: ArrayList<Item>) {
|
||||
private fun updateItems(adapterItems: ArrayList<Item>) {
|
||||
items = adapterItems
|
||||
}
|
||||
|
||||
private fun handleGDPRDialog(GDPRShown: Boolean) {
|
||||
val sharedEditor = sharedPref.edit()
|
||||
if (!GDPRShown) {
|
||||
val alertDialog = AlertDialog.Builder(this).create()
|
||||
alertDialog.setTitle(getString(R.string.gdpr_dialog_title))
|
||||
alertDialog.setMessage(getString(R.string.gdpr_dialog_message))
|
||||
alertDialog.setButton(
|
||||
AlertDialog.BUTTON_NEUTRAL,
|
||||
"OK"
|
||||
) { dialog, _ ->
|
||||
sharedEditor.putBoolean("GDPR_shown", true)
|
||||
sharedEditor.commit()
|
||||
dialog.dismiss()
|
||||
}
|
||||
alertDialog.show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleRecurringTask() {
|
||||
if (periodicRefresh) {
|
||||
val myConstraints = Constraints.Builder()
|
||||
.setRequiresBatteryNotLow(true)
|
||||
.setRequiresCharging(refreshWhenChargingOnly)
|
||||
.setRequiresStorageNotLow(true)
|
||||
.build()
|
||||
|
||||
val backgroundWork =
|
||||
PeriodicWorkRequestBuilder<LoadingWorker>(refreshMinutes, TimeUnit.MINUTES)
|
||||
.setConstraints(myConstraints)
|
||||
.addTag("selfoss-loading")
|
||||
.build()
|
||||
|
||||
|
||||
WorkManager.getInstance().enqueueUniquePeriodicWork("selfoss-loading", ExistingPeriodicWorkPolicy.KEEP, backgroundWork)
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleOfflineActions() {
|
||||
fun <T>doAndReportOnFail(call: Call<T>, action: ActionEntity) {
|
||||
call.enqueue(object: Callback<T> {
|
||||
override fun onResponse(
|
||||
call: Call<T>,
|
||||
response: Response<T>
|
||||
) {
|
||||
thread {
|
||||
db.actionsDao().delete(action)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<T>, t: Throwable) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(t, this@HomeActivity)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (this@HomeActivity.isNetworkAccessible(null, offlineShortcut)) {
|
||||
thread {
|
||||
val actions = db.actionsDao().actions()
|
||||
|
||||
actions.forEach { action ->
|
||||
when {
|
||||
action.read -> doAndReportOnFail(api.markItem(action.articleId), action)
|
||||
action.unread -> doAndReportOnFail(api.unmarkItem(action.articleId), action)
|
||||
action.starred -> doAndReportOnFail(api.starrItem(action.articleId), action)
|
||||
action.unstarred -> doAndReportOnFail(api.unstarrItem(action.articleId), action)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,70 +0,0 @@
|
||||
package apps.amine.bou.readerforselfoss
|
||||
|
||||
import agency.tango.materialintroscreen.MaterialIntroActivity
|
||||
import agency.tango.materialintroscreen.MessageButtonBehaviour
|
||||
import agency.tango.materialintroscreen.SlideFragmentBuilder
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.v7.app.AppCompatDelegate
|
||||
import android.view.View
|
||||
|
||||
class IntroActivity : MaterialIntroActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||
|
||||
addSlide(
|
||||
SlideFragmentBuilder()
|
||||
.backgroundColor(R.color.colorPrimary)
|
||||
.buttonsColor(R.color.colorAccent)
|
||||
.image(R.drawable.web_hi_res_512)
|
||||
.title(getString(R.string.intro_hello_title))
|
||||
.description(getString(R.string.intro_hello_message))
|
||||
.build()
|
||||
)
|
||||
|
||||
addSlide(
|
||||
SlideFragmentBuilder()
|
||||
.backgroundColor(R.color.colorAccent)
|
||||
.buttonsColor(R.color.colorPrimary)
|
||||
.image(R.drawable.ic_info_outline_white_48px)
|
||||
.title(getString(R.string.intro_needs_selfoss_title))
|
||||
.description(getString(R.string.intro_needs_selfoss_message))
|
||||
.build(),
|
||||
MessageButtonBehaviour(
|
||||
View.OnClickListener {
|
||||
val browserIntent = Intent(
|
||||
Intent.ACTION_VIEW,
|
||||
Uri.parse("https://selfoss.aditu.de")
|
||||
)
|
||||
startActivity(browserIntent)
|
||||
}, getString(R.string.intro_needs_selfoss_link)
|
||||
)
|
||||
)
|
||||
|
||||
addSlide(
|
||||
SlideFragmentBuilder()
|
||||
.backgroundColor(R.color.colorPrimaryDark)
|
||||
.buttonsColor(R.color.colorAccentDark)
|
||||
.image(R.drawable.ic_thumb_up_white_48px)
|
||||
.title(getString(R.string.intro_all_set_title))
|
||||
.description(getString(R.string.intro_all_set_message))
|
||||
.build()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onFinish() {
|
||||
super.onFinish()
|
||||
val getPrefs = PreferenceManager.getDefaultSharedPreferences(baseContext)
|
||||
val e = getPrefs.edit()
|
||||
e.putBoolean("firstStart", false)
|
||||
e.apply()
|
||||
val intent = Intent(this, LoginActivity::class.java)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
}
|
@ -6,8 +6,8 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AlertDialog
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.text.TextUtils
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
@ -21,6 +21,7 @@ import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import apps.amine.bou.readerforselfoss.utils.isBaseUrlValid
|
||||
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
|
||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
||||
import com.mikepenz.aboutlibraries.Libs
|
||||
import com.mikepenz.aboutlibraries.LibsBuilder
|
||||
import kotlinx.android.synthetic.main.activity_login.*
|
||||
@ -194,8 +195,11 @@ class LoginActivity : AppCompatActivity() {
|
||||
this,
|
||||
this@LoginActivity,
|
||||
isWithSelfSignedCert,
|
||||
-1L,
|
||||
isWithSelfSignedCert
|
||||
)
|
||||
|
||||
if (this@LoginActivity.isNetworkAccessible(this@LoginActivity.findViewById(R.id.loginForm))) {
|
||||
api.login().enqueue(object : Callback<SuccessResponse> {
|
||||
private fun preferenceError(t: Throwable) {
|
||||
editor.remove("url")
|
||||
@ -235,6 +239,9 @@ class LoginActivity : AppCompatActivity() {
|
||||
preferenceError(t)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
showProgress(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ package apps.amine.bou.readerforselfoss
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
@ -11,17 +11,9 @@ class MainActivity : AppCompatActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
if (PreferenceManager.getDefaultSharedPreferences(baseContext).getBoolean(
|
||||
"firstStart",
|
||||
true
|
||||
)) {
|
||||
val i = Intent(this@MainActivity, IntroActivity::class.java)
|
||||
startActivity(i)
|
||||
} else {
|
||||
val intent = Intent(this, LoginActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,15 @@
|
||||
package apps.amine.bou.readerforselfoss
|
||||
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.multidex.MultiDexApplication
|
||||
import androidx.multidex.MultiDexApplication
|
||||
import android.widget.ImageView
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import com.anupcowkur.reservoir.Reservoir
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.ftinc.scoop.Scoop
|
||||
@ -49,8 +50,6 @@ class MyApp : MultiDexApplication() {
|
||||
|
||||
initAmplify()
|
||||
|
||||
initCache()
|
||||
|
||||
val prefs = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||
if (prefs.getString("unique_id", "").isEmpty()) {
|
||||
val editor = prefs.edit()
|
||||
@ -63,6 +62,25 @@ class MyApp : MultiDexApplication() {
|
||||
initTheme()
|
||||
|
||||
tryToHandleBug()
|
||||
|
||||
handleNotificationChannels()
|
||||
}
|
||||
|
||||
private fun handleNotificationChannels() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
||||
val name = getString(R.string.notification_channel_sync)
|
||||
val importance = NotificationManager.IMPORTANCE_LOW
|
||||
val mChannel = NotificationChannel(Config.syncChannelId, name, importance)
|
||||
|
||||
val newItemsChannelname = getString(R.string.new_items_channel_sync)
|
||||
val newItemsChannelimportance = NotificationManager.IMPORTANCE_DEFAULT
|
||||
val newItemsChannelmChannel = NotificationChannel(Config.newItemsChannelId, newItemsChannelname, newItemsChannelimportance)
|
||||
|
||||
notificationManager.createNotificationChannel(mChannel)
|
||||
notificationManager.createNotificationChannel(newItemsChannelmChannel)
|
||||
}
|
||||
}
|
||||
|
||||
override fun attachBaseContext(base: Context?) {
|
||||
@ -80,14 +98,6 @@ class MyApp : MultiDexApplication() {
|
||||
.applyAllDefaultRules()
|
||||
}
|
||||
|
||||
private fun initCache() {
|
||||
try {
|
||||
Reservoir.init(this, 8192) //in bytes
|
||||
} catch (e: IOException) {
|
||||
//failure
|
||||
}
|
||||
}
|
||||
|
||||
private fun initDrawerImageLoader() {
|
||||
DrawerImageLoader.init(object : AbstractDrawerImageLoader() {
|
||||
override fun set(
|
||||
|
@ -1,28 +1,33 @@
|
||||
package apps.amine.bou.readerforselfoss
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.v4.app.FragmentManager
|
||||
import android.support.v4.app.FragmentStatePagerAdapter
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.support.v4.view.ViewPager
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import androidx.room.Room
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.fragments.ArticleFragment
|
||||
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity
|
||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_1_2
|
||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_2_3
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.themes.Toppings
|
||||
import apps.amine.bou.readerforselfoss.transformers.DepthPageTransformer
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
|
||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
||||
import apps.amine.bou.readerforselfoss.utils.persistence.toEntity
|
||||
import apps.amine.bou.readerforselfoss.utils.succeeded
|
||||
import apps.amine.bou.readerforselfoss.utils.toggleStar
|
||||
import com.ftinc.scoop.Scoop
|
||||
@ -32,6 +37,7 @@ import org.acra.ACRA
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class ReaderActivity : AppCompatActivity() {
|
||||
|
||||
@ -44,6 +50,8 @@ class ReaderActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var toolbarMenu: Menu
|
||||
|
||||
private lateinit var db: AppDatabase
|
||||
|
||||
private fun showMenuItem(willAddToFavorite: Boolean) {
|
||||
toolbarMenu.findItem(R.id.save).isVisible = willAddToFavorite
|
||||
toolbarMenu.findItem(R.id.unsave).isVisible = !willAddToFavorite
|
||||
@ -62,63 +70,79 @@ class ReaderActivity : AppCompatActivity() {
|
||||
|
||||
setContentView(R.layout.activity_reader)
|
||||
|
||||
Scoop.getInstance()
|
||||
.bind(this, Toppings.PRIMARY.value, toolBar)
|
||||
.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
|
||||
db = Room.databaseBuilder(
|
||||
applicationContext,
|
||||
AppDatabase::class.java, "selfoss-database"
|
||||
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).build()
|
||||
|
||||
val scoop = Scoop.getInstance()
|
||||
scoop.bind(this, Toppings.PRIMARY.value, toolBar)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
|
||||
}
|
||||
|
||||
setSupportActionBar(toolBar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||
|
||||
val settings = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||
val sharedPref = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
|
||||
debugReadingItems = sharedPref.getBoolean("read_debug", false)
|
||||
userIdentifier = sharedPref.getString("unique_id", "")
|
||||
markOnScroll = sharedPref.getBoolean("mark_on_scroll", false)
|
||||
debugReadingItems = prefs.getBoolean("read_debug", false)
|
||||
userIdentifier = prefs.getString("unique_id", "")
|
||||
markOnScroll = prefs.getBoolean("mark_on_scroll", false)
|
||||
|
||||
api = SelfossApi(
|
||||
this,
|
||||
this@ReaderActivity,
|
||||
prefs.getBoolean("isSelfSignedCert", false),
|
||||
prefs.getString("api_timeout", "-1").toLong(),
|
||||
prefs.getBoolean("should_log_everything", false)
|
||||
)
|
||||
|
||||
if (allItems.isEmpty()) {
|
||||
finish()
|
||||
}
|
||||
|
||||
api = SelfossApi(
|
||||
this,
|
||||
this@ReaderActivity,
|
||||
settings.getBoolean("isSelfSignedCert", false),
|
||||
sharedPref.getBoolean("should_log_everything", false)
|
||||
)
|
||||
|
||||
currentItem = intent.getIntExtra("currentItem", 0)
|
||||
|
||||
pager.adapter = ScreenSlidePagerAdapter(supportFragmentManager, AppColors(this@ReaderActivity))
|
||||
readItem(allItems[currentItem])
|
||||
|
||||
pager.adapter =
|
||||
ScreenSlidePagerAdapter(supportFragmentManager, AppColors(this@ReaderActivity))
|
||||
pager.currentItem = currentItem
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
(pager.adapter as ScreenSlidePagerAdapter).notifyDataSetChanged()
|
||||
notifyAdapter()
|
||||
|
||||
pager.setPageTransformer(true, DepthPageTransformer())
|
||||
(indicator as CircleIndicator).setViewPager(pager)
|
||||
|
||||
pager.addOnPageChangeListener(
|
||||
object : ViewPager.SimpleOnPageChangeListener() {
|
||||
var isLastItem = false
|
||||
|
||||
override fun onPageSelected(position: Int) {
|
||||
isLastItem = (position === (allItems.size - 1))
|
||||
|
||||
if (allItems[position].starred) {
|
||||
canRemoveFromFavorite()
|
||||
} else {
|
||||
canFavorite()
|
||||
}
|
||||
readItem(allItems[pager.currentItem])
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun onPageScrollStateChanged(state: Int) {
|
||||
if (markOnScroll && (state === ViewPager.SCROLL_STATE_DRAGGING || (state === ViewPager.SCROLL_STATE_IDLE && isLastItem))) {
|
||||
api.markItem(allItems[pager.currentItem].id).enqueue(
|
||||
fun readItem(item: Item) {
|
||||
if (markOnScroll) {
|
||||
thread {
|
||||
db.itemsDao().delete(item.toEntity())
|
||||
}
|
||||
if (this@ReaderActivity.isNetworkAccessible(this@ReaderActivity.findViewById(R.id.reader_activity_view))) {
|
||||
api.markItem(item.id).enqueue(
|
||||
object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<SuccessResponse>,
|
||||
@ -133,7 +157,8 @@ class ReaderActivity : AppCompatActivity() {
|
||||
"response errorBody: ${response.errorBody()?.string()} " +
|
||||
"body success: ${response.body()?.success} " +
|
||||
"body isSuccess: ${response.body()?.isSuccess}"
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(Exception(message), this@ReaderActivity)
|
||||
ACRA.getErrorReporter()
|
||||
.maybeHandleSilentException(Exception(message), this@ReaderActivity)
|
||||
}
|
||||
}
|
||||
|
||||
@ -141,16 +166,26 @@ class ReaderActivity : AppCompatActivity() {
|
||||
call: Call<SuccessResponse>,
|
||||
t: Throwable
|
||||
) {
|
||||
thread {
|
||||
db.itemsDao().insertAllItems(item.toEntity())
|
||||
}
|
||||
if (debugReadingItems) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(t, this@ReaderActivity)
|
||||
ACRA.getErrorReporter()
|
||||
.maybeHandleSilentException(t, this@ReaderActivity)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
thread {
|
||||
db.actionsDao().insertAllActions(ActionEntity(item.id, true, false, false, false))
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun notifyAdapter() {
|
||||
(pager.adapter as ScreenSlidePagerAdapter).notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
@ -168,7 +203,6 @@ class ReaderActivity : AppCompatActivity() {
|
||||
private inner class ScreenSlidePagerAdapter(fm: FragmentManager, val appColors: AppColors) :
|
||||
FragmentStatePagerAdapter(fm) {
|
||||
|
||||
|
||||
override fun getCount(): Int {
|
||||
return allItems.size
|
||||
}
|
||||
@ -180,7 +214,12 @@ class ReaderActivity : AppCompatActivity() {
|
||||
override fun startUpdate(container: ViewGroup) {
|
||||
super.startUpdate(container)
|
||||
|
||||
container.background = ColorDrawable(ContextCompat.getColor(this@ReaderActivity, appColors.colorBackground))
|
||||
container.background = ColorDrawable(
|
||||
ContextCompat.getColor(
|
||||
this@ReaderActivity,
|
||||
appColors.colorBackground
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -199,20 +238,33 @@ class ReaderActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
fun afterSave() {
|
||||
allItems[pager.currentItem] =
|
||||
allItems[pager.currentItem].toggleStar()
|
||||
notifyAdapter()
|
||||
canRemoveFromFavorite()
|
||||
}
|
||||
|
||||
fun afterUnsave() {
|
||||
allItems[pager.currentItem] = allItems[pager.currentItem].toggleStar()
|
||||
notifyAdapter()
|
||||
canFavorite()
|
||||
}
|
||||
|
||||
when (item.itemId) {
|
||||
android.R.id.home -> {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
R.id.save -> {
|
||||
if (this@ReaderActivity.isNetworkAccessible(null)) {
|
||||
api.starrItem(allItems[pager.currentItem].id)
|
||||
.enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<SuccessResponse>,
|
||||
response: Response<SuccessResponse>
|
||||
) {
|
||||
allItems[pager.currentItem] = allItems[pager.currentItem].toggleStar()
|
||||
canRemoveFromFavorite()
|
||||
afterSave()
|
||||
}
|
||||
|
||||
override fun onFailure(
|
||||
@ -226,16 +278,22 @@ class ReaderActivity : AppCompatActivity() {
|
||||
).show()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
thread {
|
||||
db.actionsDao().insertAllActions(ActionEntity(allItems[pager.currentItem].id, false, false, true, false))
|
||||
afterSave()
|
||||
}
|
||||
}
|
||||
}
|
||||
R.id.unsave -> {
|
||||
if (this@ReaderActivity.isNetworkAccessible(null)) {
|
||||
api.unstarrItem(allItems[pager.currentItem].id)
|
||||
.enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<SuccessResponse>,
|
||||
response: Response<SuccessResponse>
|
||||
) {
|
||||
allItems[pager.currentItem] = allItems[pager.currentItem].toggleStar()
|
||||
canFavorite()
|
||||
afterUnsave()
|
||||
}
|
||||
|
||||
override fun onFailure(
|
||||
@ -249,6 +307,12 @@ class ReaderActivity : AppCompatActivity() {
|
||||
).show()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
thread {
|
||||
db.actionsDao().insertAllActions(ActionEntity(allItems[pager.currentItem].id, false, false, false, true))
|
||||
afterUnsave()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
|
@ -2,16 +2,18 @@ package apps.amine.bou.readerforselfoss
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.support.v7.widget.LinearLayoutManager
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import android.widget.Toast
|
||||
import apps.amine.bou.readerforselfoss.adapters.SourcesListAdapter
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Sources
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Source
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.themes.Toppings
|
||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
||||
import com.ftinc.scoop.Scoop
|
||||
import kotlinx.android.synthetic.main.activity_sources.*
|
||||
import retrofit2.Call
|
||||
@ -29,9 +31,11 @@ class SourcesActivity : AppCompatActivity() {
|
||||
|
||||
setContentView(R.layout.activity_sources)
|
||||
|
||||
Scoop.getInstance()
|
||||
.bind(this, Toppings.PRIMARY.value, toolbar)
|
||||
.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
|
||||
val scoop = Scoop.getInstance()
|
||||
scoop.bind(this, Toppings.PRIMARY.value, toolbar)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
|
||||
}
|
||||
|
||||
setSupportActionBar(toolbar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
@ -56,20 +60,22 @@ class SourcesActivity : AppCompatActivity() {
|
||||
this,
|
||||
this@SourcesActivity,
|
||||
prefs.getBoolean("isSelfSignedCert", false),
|
||||
prefs.getString("api_timeout", "-1").toLong(),
|
||||
prefs.getBoolean("should_log_everything", false)
|
||||
)
|
||||
var items: ArrayList<Sources> = ArrayList()
|
||||
var items: ArrayList<Source> = ArrayList()
|
||||
|
||||
recyclerView.setHasFixedSize(true)
|
||||
recyclerView.layoutManager = mLayoutManager
|
||||
|
||||
api.sources.enqueue(object : Callback<List<Sources>> {
|
||||
if (this@SourcesActivity.isNetworkAccessible(this@SourcesActivity.findViewById(R.id.recyclerView))) {
|
||||
api.sources.enqueue(object : Callback<List<Source>> {
|
||||
override fun onResponse(
|
||||
call: Call<List<Sources>>,
|
||||
response: Response<List<Sources>>
|
||||
call: Call<List<Source>>,
|
||||
response: Response<List<Source>>
|
||||
) {
|
||||
if (response.body() != null && response.body()!!.isNotEmpty()) {
|
||||
items = response.body() as ArrayList<Sources>
|
||||
items = response.body() as ArrayList<Source>
|
||||
}
|
||||
val mAdapter = SourcesListAdapter(this@SourcesActivity, items, api)
|
||||
recyclerView.adapter = mAdapter
|
||||
@ -83,7 +89,7 @@ class SourcesActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<List<Sources>>, t: Throwable) {
|
||||
override fun onFailure(call: Call<List<Source>>, t: Throwable) {
|
||||
Toast.makeText(
|
||||
this@SourcesActivity,
|
||||
R.string.cant_get_sources,
|
||||
@ -91,6 +97,7 @@ class SourcesActivity : AppCompatActivity() {
|
||||
).show()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fab.setOnClickListener {
|
||||
startActivity(Intent(this@SourcesActivity, AddSourceActivity::class.java))
|
||||
|
@ -2,8 +2,8 @@ package apps.amine.bou.readerforselfoss.adapters
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.support.v7.widget.CardView
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import androidx.cardview.widget.CardView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.text.Html
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
@ -14,11 +14,15 @@ import apps.amine.bou.readerforselfoss.R
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.utils.LinkOnTouchListener
|
||||
import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent
|
||||
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
||||
import apps.amine.bou.readerforselfoss.utils.glide.bitmapCenterCrop
|
||||
import apps.amine.bou.readerforselfoss.utils.glide.circularBitmapDrawable
|
||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
||||
import apps.amine.bou.readerforselfoss.utils.openInBrowserAsNewTask
|
||||
import apps.amine.bou.readerforselfoss.utils.openItemUrl
|
||||
import apps.amine.bou.readerforselfoss.utils.shareLink
|
||||
@ -33,11 +37,13 @@ import kotlinx.android.synthetic.main.card_item.view.*
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class ItemCardAdapter(
|
||||
override val app: Activity,
|
||||
override var items: ArrayList<Item>,
|
||||
override val api: SelfossApi,
|
||||
override val db: AppDatabase,
|
||||
private val helper: CustomTabActivityHelper,
|
||||
private val internalBrowser: Boolean,
|
||||
private val articleViewer: Boolean,
|
||||
@ -63,6 +69,7 @@ class ItemCardAdapter(
|
||||
|
||||
holder.mView.favButton.isLiked = itm.starred
|
||||
holder.mView.title.text = Html.fromHtml(itm.title)
|
||||
holder.mView.title.setOnTouchListener(LinkOnTouchListener())
|
||||
|
||||
holder.mView.title.setLinkTextColor(appColors.colorAccent)
|
||||
|
||||
@ -114,6 +121,7 @@ class ItemCardAdapter(
|
||||
mView.favButton.setOnLikeListener(object : OnLikeListener {
|
||||
override fun liked(likeButton: LikeButton) {
|
||||
val (id) = items[adapterPosition]
|
||||
if (c.isNetworkAccessible(null)) {
|
||||
api.starrItem(id).enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<SuccessResponse>,
|
||||
@ -133,10 +141,16 @@ class ItemCardAdapter(
|
||||
).show()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
thread {
|
||||
db.actionsDao().insertAllActions(ActionEntity(id, false, false, true, false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun unLiked(likeButton: LikeButton) {
|
||||
val (id) = items[adapterPosition]
|
||||
if (c.isNetworkAccessible(null)) {
|
||||
api.unstarrItem(id).enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<SuccessResponse>,
|
||||
@ -156,6 +170,11 @@ class ItemCardAdapter(
|
||||
).show()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
thread {
|
||||
db.actionsDao().insertAllActions(ActionEntity(id, false, false, false, true))
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -2,19 +2,25 @@ package apps.amine.bou.readerforselfoss.adapters
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.support.constraint.ConstraintLayout
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.text.Html
|
||||
import android.text.Spannable
|
||||
import android.text.style.ClickableSpan
|
||||
import android.util.TypedValue
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.utils.LinkOnTouchListener
|
||||
import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent
|
||||
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
||||
import apps.amine.bou.readerforselfoss.utils.glide.bitmapCenterCrop
|
||||
@ -39,8 +45,8 @@ class ItemListAdapter(
|
||||
override val app: Activity,
|
||||
override var items: ArrayList<Item>,
|
||||
override val api: SelfossApi,
|
||||
override val db: AppDatabase,
|
||||
private val helper: CustomTabActivityHelper,
|
||||
private val clickBehavior: Boolean,
|
||||
private val internalBrowser: Boolean,
|
||||
private val articleViewer: Boolean,
|
||||
override val debugReadingItems: Boolean,
|
||||
@ -50,7 +56,6 @@ class ItemListAdapter(
|
||||
) : ItemsAdapter<ItemListAdapter.ViewHolder>() {
|
||||
private val generator: ColorGenerator = ColorGenerator.MATERIAL
|
||||
private val c: Context = app.baseContext
|
||||
private val bars: ArrayList<Boolean> = ArrayList(Collections.nCopies(items.size + 1, false))
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val v = LayoutInflater.from(c).inflate(
|
||||
@ -67,6 +72,8 @@ class ItemListAdapter(
|
||||
|
||||
holder.mView.title.text = Html.fromHtml(itm.title)
|
||||
|
||||
holder.mView.title.setOnTouchListener(LinkOnTouchListener())
|
||||
|
||||
holder.mView.title.setLinkTextColor(appColors.colorAccent)
|
||||
|
||||
holder.mView.sourceTitleAndDate.text = itm.sourceAndDateText()
|
||||
@ -106,19 +113,6 @@ class ItemListAdapter(
|
||||
} else {
|
||||
c.bitmapCenterCrop(itm.getThumbnail(c), holder.mView.itemImage)
|
||||
}
|
||||
|
||||
// TODO: maybe handle this differently. It crashes when changing tab
|
||||
try {
|
||||
if (bars[position]) {
|
||||
holder.mView.actionBar.visibility = View.VISIBLE
|
||||
} else {
|
||||
holder.mView.actionBar.visibility = View.GONE
|
||||
}
|
||||
} catch (e: IndexOutOfBoundsException) {
|
||||
holder.mView.actionBar.visibility = View.GONE
|
||||
}
|
||||
|
||||
holder.mView.favButton.isLiked = itm.starred
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = items.size
|
||||
@ -126,76 +120,13 @@ class ItemListAdapter(
|
||||
inner class ViewHolder(val mView: ConstraintLayout) : RecyclerView.ViewHolder(mView) {
|
||||
|
||||
init {
|
||||
handleClickListeners()
|
||||
handleCustomTabActions()
|
||||
}
|
||||
|
||||
private fun handleClickListeners() {
|
||||
|
||||
mView.favButton.setOnLikeListener(object : OnLikeListener {
|
||||
override fun liked(likeButton: LikeButton) {
|
||||
val (id) = items[adapterPosition]
|
||||
api.starrItem(id).enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<SuccessResponse>,
|
||||
response: Response<SuccessResponse>
|
||||
) {
|
||||
}
|
||||
|
||||
override fun onFailure(
|
||||
call: Call<SuccessResponse>,
|
||||
t: Throwable
|
||||
) {
|
||||
mView.favButton.isLiked = false
|
||||
Toast.makeText(
|
||||
c,
|
||||
R.string.cant_mark_favortie,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun unLiked(likeButton: LikeButton) {
|
||||
val (id) = items[adapterPosition]
|
||||
api.unstarrItem(id).enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<SuccessResponse>,
|
||||
response: Response<SuccessResponse>
|
||||
) {
|
||||
}
|
||||
|
||||
override fun onFailure(
|
||||
call: Call<SuccessResponse>,
|
||||
t: Throwable
|
||||
) {
|
||||
mView.favButton.isLiked = true
|
||||
Toast.makeText(
|
||||
c,
|
||||
R.string.cant_unmark_favortie,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
mView.shareBtn.setOnClickListener {
|
||||
c.shareLink(items[adapterPosition].getLinkDecoded())
|
||||
}
|
||||
|
||||
mView.browserBtn.setOnClickListener {
|
||||
c.openInBrowserAsNewTask(items[adapterPosition])
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleCustomTabActions() {
|
||||
val customTabsIntent = c.buildCustomTabsIntent()
|
||||
helper.bindCustomTabsService(app)
|
||||
|
||||
|
||||
if (!clickBehavior) {
|
||||
mView.setOnClickListener {
|
||||
c.openItemUrl(
|
||||
items,
|
||||
@ -207,34 +138,6 @@ class ItemListAdapter(
|
||||
app
|
||||
)
|
||||
}
|
||||
mView.setOnLongClickListener {
|
||||
actionBarShowHide()
|
||||
true
|
||||
}
|
||||
} else {
|
||||
mView.setOnClickListener { actionBarShowHide() }
|
||||
mView.setOnLongClickListener {
|
||||
c.openItemUrl(
|
||||
items,
|
||||
adapterPosition,
|
||||
items[adapterPosition].getLinkDecoded(),
|
||||
customTabsIntent,
|
||||
internalBrowser,
|
||||
articleViewer,
|
||||
app
|
||||
)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun actionBarShowHide() {
|
||||
bars[adapterPosition] = true
|
||||
if (mView.actionBar.visibility == View.GONE) {
|
||||
mView.actionBar.visibility = View.VISIBLE
|
||||
} else {
|
||||
mView.actionBar.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,25 +2,31 @@ package apps.amine.bou.readerforselfoss.adapters
|
||||
|
||||
import android.app.Activity
|
||||
import android.graphics.Color
|
||||
import android.support.design.widget.Snackbar
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
|
||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
||||
import apps.amine.bou.readerforselfoss.utils.persistence.toEntity
|
||||
import apps.amine.bou.readerforselfoss.utils.succeeded
|
||||
import org.acra.ACRA
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapter<VH>() {
|
||||
abstract var items: ArrayList<Item>
|
||||
abstract val api: SelfossApi
|
||||
abstract val db: AppDatabase
|
||||
abstract val debugReadingItems: Boolean
|
||||
abstract val userIdentifier: String
|
||||
abstract val app: Activity
|
||||
@ -42,9 +48,13 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
|
||||
)
|
||||
.setAction(R.string.undo_string) {
|
||||
items.add(position, i)
|
||||
thread {
|
||||
db.itemsDao().insertAllItems(i.toEntity())
|
||||
}
|
||||
notifyItemInserted(position)
|
||||
updateItems(items)
|
||||
|
||||
if (app.isNetworkAccessible(null)) {
|
||||
api.unmarkItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<SuccessResponse>,
|
||||
@ -54,28 +64,38 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
|
||||
|
||||
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||
items.remove(i)
|
||||
thread {
|
||||
db.itemsDao().delete(i.toEntity())
|
||||
}
|
||||
notifyItemRemoved(position)
|
||||
updateItems(items)
|
||||
doUnmark(i, position)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
thread {
|
||||
db.actionsDao().deleteReadActionForArticle(i.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val view = s.view
|
||||
val tv: TextView = view.findViewById(android.support.design.R.id.snackbar_text)
|
||||
val tv: TextView = view.findViewById(com.google.android.material.R.id.snackbar_text)
|
||||
tv.setTextColor(Color.WHITE)
|
||||
s.show()
|
||||
}
|
||||
|
||||
fun removeItemAtIndex(position: Int) {
|
||||
|
||||
val i = items[position]
|
||||
|
||||
items.remove(i)
|
||||
notifyItemRemoved(position)
|
||||
updateItems(items)
|
||||
|
||||
thread {
|
||||
db.itemsDao().delete(i.toEntity())
|
||||
}
|
||||
|
||||
if (app.isNetworkAccessible(null)) {
|
||||
api.markItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<SuccessResponse>,
|
||||
@ -93,6 +113,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(Exception(message), app)
|
||||
Toast.makeText(app.baseContext, message, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
|
||||
doUnmark(i, position)
|
||||
}
|
||||
|
||||
@ -110,8 +131,17 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
|
||||
notifyItemInserted(position)
|
||||
updateItems(items)
|
||||
|
||||
thread {
|
||||
db.itemsDao().insertAllItems(i.toEntity())
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
thread {
|
||||
db.actionsDao().insertAllActions(ActionEntity(i.id, true, false, false, false))
|
||||
doUnmark(i, position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun addItemAtIndex(item: Item, position: Int) {
|
||||
|
@ -2,17 +2,18 @@ package apps.amine.bou.readerforselfoss.adapters
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.support.constraint.ConstraintLayout
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.Toast
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Sources
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Source
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.utils.glide.circularBitmapDrawable
|
||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
||||
import apps.amine.bou.readerforselfoss.utils.toTextDrawableString
|
||||
import com.amulyakhare.textdrawable.TextDrawable
|
||||
import com.amulyakhare.textdrawable.util.ColorGenerator
|
||||
@ -23,7 +24,7 @@ import retrofit2.Response
|
||||
|
||||
class SourcesListAdapter(
|
||||
private val app: Activity,
|
||||
private val items: ArrayList<Sources>,
|
||||
private val items: ArrayList<Source>,
|
||||
private val api: SelfossApi
|
||||
) : RecyclerView.Adapter<SourcesListAdapter.ViewHolder>() {
|
||||
private val c: Context = app.baseContext
|
||||
@ -70,6 +71,7 @@ class SourcesListAdapter(
|
||||
val deleteBtn: Button = mView.findViewById(R.id.deleteBtn)
|
||||
|
||||
deleteBtn.setOnClickListener {
|
||||
if (c.isNetworkAccessible(null)) {
|
||||
val (id) = items[adapterPosition]
|
||||
api.deleteSource(id).enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
@ -100,4 +102,5 @@ class SourcesListAdapter(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,11 +18,13 @@ import retrofit2.Call
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class SelfossApi(
|
||||
c: Context,
|
||||
callingActivity: Activity,
|
||||
callingActivity: Activity?,
|
||||
isWithSelfSignedCert: Boolean,
|
||||
timeout: Long,
|
||||
shouldLog: Boolean
|
||||
) {
|
||||
|
||||
@ -38,16 +40,25 @@ class SelfossApi(
|
||||
this
|
||||
}
|
||||
|
||||
fun OkHttpClient.Builder.maybeWithSettingsTimeout(timeout: Long): OkHttpClient.Builder =
|
||||
if (timeout != -1L) {
|
||||
this.readTimeout(timeout, TimeUnit.SECONDS)
|
||||
.connectTimeout(timeout, TimeUnit.SECONDS)
|
||||
} else {
|
||||
this
|
||||
}
|
||||
|
||||
fun Credentials.createAuthenticator(): DispatchingAuthenticator =
|
||||
DispatchingAuthenticator.Builder()
|
||||
.with("digest", DigestAuthenticator(this))
|
||||
.with("basic", BasicAuthenticator(this))
|
||||
.build()
|
||||
|
||||
fun DispatchingAuthenticator.getHttpClien(isWithSelfSignedCert: Boolean): OkHttpClient.Builder {
|
||||
fun DispatchingAuthenticator.getHttpClien(isWithSelfSignedCert: Boolean, timeout: Long): OkHttpClient.Builder {
|
||||
val authCache = ConcurrentHashMap<String, CachingAuthenticator>()
|
||||
return OkHttpClient
|
||||
.Builder()
|
||||
.maybeWithSettingsTimeout(timeout)
|
||||
.maybeWithSelfSigned(isWithSelfSignedCert)
|
||||
.authenticator(CachingAuthenticatorDecorator(this, authCache))
|
||||
.addInterceptor(AuthenticationCacheInterceptor(authCache))
|
||||
@ -66,6 +77,7 @@ class SelfossApi(
|
||||
val gson =
|
||||
GsonBuilder()
|
||||
.registerTypeAdapter(Boolean::class.javaPrimitiveType, BooleanTypeAdapter())
|
||||
.registerTypeAdapter(SelfossTagType::class.java, SelfossTagTypeTypeAdapter())
|
||||
.setLenient()
|
||||
.create()
|
||||
|
||||
@ -77,7 +89,7 @@ class SelfossApi(
|
||||
HttpLoggingInterceptor.Level.NONE
|
||||
}
|
||||
|
||||
val httpClient = authenticator.getHttpClien(isWithSelfSignedCert)
|
||||
val httpClient = authenticator.getHttpClien(isWithSelfSignedCert, timeout)
|
||||
|
||||
httpClient.addInterceptor(logging)
|
||||
|
||||
@ -91,9 +103,11 @@ class SelfossApi(
|
||||
.build()
|
||||
service = retrofit.create(SelfossService::class.java)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
if (callingActivity != null) {
|
||||
Config.logoutAndRedirect(c, callingActivity, config.settings.edit(), baseUrlFail = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun login(): Call<SuccessResponse> =
|
||||
service.loginToSelfoss(config.userLogin, config.userPassword)
|
||||
@ -125,6 +139,9 @@ class SelfossApi(
|
||||
): Call<List<Item>> =
|
||||
getItems("starred", tag, sourceId, search, itemsNumber, offset)
|
||||
|
||||
fun allItems(): Call<List<Item>> =
|
||||
service.allItems(userName, password)
|
||||
|
||||
private fun getItems(
|
||||
type: String,
|
||||
tag: String?,
|
||||
@ -159,7 +176,7 @@ class SelfossApi(
|
||||
fun update(): Call<String> =
|
||||
service.update(userName, password)
|
||||
|
||||
val sources: Call<List<Sources>>
|
||||
val sources: Call<List<Source>>
|
||||
get() = service.sources(userName, password)
|
||||
|
||||
fun deleteSource(id: String): Call<SuccessResponse> =
|
||||
|
@ -9,13 +9,13 @@ import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import apps.amine.bou.readerforselfoss.utils.isEmptyOrNullOrNullString
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
private fun constructUrl(config: Config?, path: String, file: String): String {
|
||||
val baseUriBuilder = Uri.parse(config!!.baseUrl).buildUpon()
|
||||
baseUriBuilder.appendPath(path).appendPath(file)
|
||||
|
||||
private fun constructUrl(config: Config?, path: String, file: String?): String {
|
||||
return if (file.isEmptyOrNullOrNullString()) {
|
||||
""
|
||||
} else {
|
||||
val baseUriBuilder = Uri.parse(config!!.baseUrl).buildUpon()
|
||||
baseUriBuilder.appendPath(path).appendPath(file)
|
||||
|
||||
baseUriBuilder.toString()
|
||||
}
|
||||
}
|
||||
@ -42,10 +42,10 @@ data class Spout(
|
||||
@SerializedName("description") val description: String
|
||||
)
|
||||
|
||||
data class Sources(
|
||||
data class Source(
|
||||
@SerializedName("id") val id: String,
|
||||
@SerializedName("title") val title: String,
|
||||
@SerializedName("tags") val tags: String,
|
||||
@SerializedName("tags") val tags: SelfossTagType,
|
||||
@SerializedName("spout") val spout: String,
|
||||
@SerializedName("error") val error: String,
|
||||
@SerializedName("icon") val icon: String
|
||||
@ -71,7 +71,7 @@ data class Item(
|
||||
@SerializedName("icon") val icon: String,
|
||||
@SerializedName("link") val link: String,
|
||||
@SerializedName("sourcetitle") val sourcetitle: String,
|
||||
@SerializedName("tags") val tags: String
|
||||
@SerializedName("tags") val tags: SelfossTagType
|
||||
) : Parcelable {
|
||||
|
||||
var config: Config? = null
|
||||
@ -94,7 +94,7 @@ data class Item(
|
||||
icon = source.readString(),
|
||||
link = source.readString(),
|
||||
sourcetitle = source.readString(),
|
||||
tags = source.readString()
|
||||
tags = source.readParcelable(ClassLoader.getSystemClassLoader())
|
||||
)
|
||||
|
||||
override fun describeContents() = 0
|
||||
@ -110,7 +110,7 @@ data class Item(
|
||||
dest.writeString(icon)
|
||||
dest.writeString(link)
|
||||
dest.writeString(sourcetitle)
|
||||
dest.writeString(tags)
|
||||
dest.writeParcelable(tags, flags)
|
||||
}
|
||||
|
||||
fun getIcon(app: Context): String {
|
||||
@ -154,3 +154,26 @@ data class Item(
|
||||
return stringUrl
|
||||
}
|
||||
}
|
||||
|
||||
data class SelfossTagType(val tags: String) : Parcelable {
|
||||
|
||||
companion object {
|
||||
@JvmField val CREATOR: Parcelable.Creator<SelfossTagType> =
|
||||
object : Parcelable.Creator<SelfossTagType> {
|
||||
override fun createFromParcel(source: Parcel): SelfossTagType =
|
||||
SelfossTagType(source)
|
||||
|
||||
override fun newArray(size: Int): Array<SelfossTagType?> = arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
|
||||
constructor(source: Parcel) : this(
|
||||
tags = source.readString()
|
||||
)
|
||||
|
||||
override fun describeContents() = 0
|
||||
|
||||
override fun writeToParcel(dest: Parcel, flags: Int) {
|
||||
dest.writeString(tags)
|
||||
}
|
||||
}
|
@ -27,6 +27,12 @@ internal interface SelfossService {
|
||||
@Query("offset") offset: Int
|
||||
): Call<List<Item>>
|
||||
|
||||
@GET("items")
|
||||
fun allItems(
|
||||
@Query("username") username: String,
|
||||
@Query("password") password: String
|
||||
): Call<List<Item>>
|
||||
|
||||
@Headers("Content-Type: application/x-www-form-urlencoded")
|
||||
@POST("mark/{id}")
|
||||
fun markAsRead(
|
||||
@ -95,7 +101,7 @@ internal interface SelfossService {
|
||||
fun sources(
|
||||
@Query("username") username: String,
|
||||
@Query("password") password: String
|
||||
): Call<List<Sources>>
|
||||
): Call<List<Source>>
|
||||
|
||||
@DELETE("source/{id}")
|
||||
fun deleteSource(
|
||||
|
@ -0,0 +1,22 @@
|
||||
package apps.amine.bou.readerforselfoss.api.selfoss
|
||||
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonParseException
|
||||
import java.lang.reflect.Type
|
||||
|
||||
internal class SelfossTagTypeTypeAdapter : JsonDeserializer<SelfossTagType> {
|
||||
|
||||
@Throws(JsonParseException::class)
|
||||
override fun deserialize(
|
||||
json: JsonElement,
|
||||
typeOfT: Type,
|
||||
context: JsonDeserializationContext
|
||||
): SelfossTagType? =
|
||||
if (json.isJsonArray) {
|
||||
SelfossTagType(json.asJsonArray.joinToString(",") { it.toString() })
|
||||
} else {
|
||||
SelfossTagType(json.toString())
|
||||
}
|
||||
}
|
@ -0,0 +1,152 @@
|
||||
package apps.amine.bou.readerforselfoss.background
|
||||
|
||||
import android.app.NotificationManager
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.preference.PreferenceManager
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.NotificationCompat.PRIORITY_DEFAULT
|
||||
import androidx.core.app.NotificationCompat.PRIORITY_LOW
|
||||
import androidx.room.Room
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import apps.amine.bou.readerforselfoss.MainActivity
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity
|
||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_1_2
|
||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_2_3
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
|
||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
||||
import apps.amine.bou.readerforselfoss.utils.persistence.toEntity
|
||||
import org.acra.ACRA
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.util.*
|
||||
import kotlin.concurrent.schedule
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class LoadingWorker(val context: Context, params: WorkerParameters) : Worker(context, params) {
|
||||
lateinit var db: AppDatabase
|
||||
|
||||
override fun doWork(): Result {
|
||||
if (context.isNetworkAccessible(null)) {
|
||||
|
||||
val notificationManager =
|
||||
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||
|
||||
val notification = NotificationCompat.Builder(applicationContext, Config.syncChannelId)
|
||||
.setContentTitle(context.getString(R.string.loading_notification_title))
|
||||
.setContentText(context.getString(R.string.loading_notification_text))
|
||||
.setOngoing(true)
|
||||
.setPriority(PRIORITY_LOW)
|
||||
.setChannelId(Config.syncChannelId)
|
||||
.setSmallIcon(R.drawable.ic_cloud_download)
|
||||
|
||||
notificationManager.notify(1, notification.build())
|
||||
|
||||
val settings =
|
||||
this.context.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||
val sharedPref = PreferenceManager.getDefaultSharedPreferences(this.context)
|
||||
val notifyNewItems = sharedPref.getBoolean("notify_new_items", false)
|
||||
|
||||
db = Room.databaseBuilder(
|
||||
applicationContext,
|
||||
AppDatabase::class.java, "selfoss-database"
|
||||
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).build()
|
||||
|
||||
val api = SelfossApi(
|
||||
this.context,
|
||||
null,
|
||||
settings.getBoolean("isSelfSignedCert", false),
|
||||
sharedPref.getString("api_timeout", "-1").toLong(),
|
||||
sharedPref.getBoolean("should_log_everything", false)
|
||||
)
|
||||
|
||||
api.allItems().enqueue(object : Callback<List<Item>> {
|
||||
override fun onFailure(call: Call<List<Item>>, t: Throwable) {
|
||||
Timer("", false).schedule(4000) {
|
||||
notificationManager.cancel(1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResponse(
|
||||
call: Call<List<Item>>,
|
||||
response: Response<List<Item>>
|
||||
) {
|
||||
thread {
|
||||
if (response.body() != null) {
|
||||
val apiItems = (response.body() as ArrayList<Item>)
|
||||
db.itemsDao().deleteAllItems()
|
||||
db.itemsDao()
|
||||
.insertAllItems(*(apiItems.map { it.toEntity() }).toTypedArray())
|
||||
|
||||
val newSize = apiItems.filter { it.unread }.size
|
||||
if (notifyNewItems && newSize > 0) {
|
||||
|
||||
val intent = Intent(context, MainActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
}
|
||||
val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, 0)
|
||||
|
||||
val newItemsNotification = NotificationCompat.Builder(applicationContext, Config.newItemsChannelId)
|
||||
.setContentTitle(context.getString(R.string.new_items_notification_title))
|
||||
.setContentText(context.getString(R.string.new_items_notification_text, newSize))
|
||||
.setPriority(PRIORITY_DEFAULT)
|
||||
.setChannelId(Config.newItemsChannelId)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true)
|
||||
.setSmallIcon(R.drawable.ic_fiber_new_black_24dp)
|
||||
|
||||
Timer("", false).schedule(4000) {
|
||||
notificationManager.notify(2, newItemsNotification.build())
|
||||
}
|
||||
}
|
||||
}
|
||||
Timer("", false).schedule(4000) {
|
||||
notificationManager.cancel(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
thread {
|
||||
val actions = db.actionsDao().actions()
|
||||
|
||||
actions.forEach { action ->
|
||||
when {
|
||||
action.read -> doAndReportOnFail(api.markItem(action.articleId), action)
|
||||
action.unread -> doAndReportOnFail(api.unmarkItem(action.articleId), action)
|
||||
action.starred -> doAndReportOnFail(api.starrItem(action.articleId), action)
|
||||
action.unstarred -> doAndReportOnFail(
|
||||
api.unstarrItem(action.articleId),
|
||||
action
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Result.SUCCESS
|
||||
}
|
||||
|
||||
private fun <T> doAndReportOnFail(call: Call<T>, action: ActionEntity) {
|
||||
call.enqueue(object : Callback<T> {
|
||||
override fun onResponse(
|
||||
call: Call<T>,
|
||||
response: Response<T>
|
||||
) {
|
||||
thread {
|
||||
db.actionsDao().delete(action)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<T>, t: Throwable) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(t, context)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -1,39 +1,44 @@
|
||||
package apps.amine.bou.readerforselfoss.fragments
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.customtabs.CustomTabsIntent
|
||||
import android.support.design.widget.FloatingActionButton
|
||||
import android.support.v4.app.Fragment
|
||||
import android.support.v4.content.ContextCompat
|
||||
import android.support.v4.widget.NestedScrollView
|
||||
import android.support.v7.app.AlertDialog
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.WebSettings
|
||||
import apps.amine.bou.readerforselfoss.BuildConfig
|
||||
import androidx.room.Room
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
import apps.amine.bou.readerforselfoss.api.mercury.MercuryApi
|
||||
import apps.amine.bou.readerforselfoss.api.mercury.ParsedContent
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity
|
||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_1_2
|
||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_2_3
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent
|
||||
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
||||
import apps.amine.bou.readerforselfoss.utils.isEmptyOrNullOrNullString
|
||||
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
|
||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
||||
import apps.amine.bou.readerforselfoss.utils.openItemUrl
|
||||
import apps.amine.bou.readerforselfoss.utils.shareLink
|
||||
import apps.amine.bou.readerforselfoss.utils.sourceAndDateText
|
||||
import apps.amine.bou.readerforselfoss.utils.succeeded
|
||||
import apps.amine.bou.readerforselfoss.utils.toPx
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
@ -45,6 +50,7 @@ import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.net.MalformedURLException
|
||||
import java.net.URL
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class ArticleFragment : Fragment() {
|
||||
private lateinit var pageNumber: Number
|
||||
@ -56,10 +62,10 @@ class ArticleFragment : Fragment() {
|
||||
private lateinit var contentSource: String
|
||||
private lateinit var contentImage: String
|
||||
private lateinit var contentTitle: String
|
||||
private var showMalformedUrl: Boolean = false
|
||||
private lateinit var editor: SharedPreferences.Editor
|
||||
private lateinit var fab: FloatingActionButton
|
||||
private lateinit var appColors: AppColors
|
||||
private lateinit var db: AppDatabase
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
@ -73,6 +79,11 @@ class ArticleFragment : Fragment() {
|
||||
|
||||
pageNumber = arguments!!.getInt(ARG_POSITION)
|
||||
allItems = arguments!!.getParcelableArrayList(ARG_ITEMS)
|
||||
|
||||
db = Room.databaseBuilder(
|
||||
context!!,
|
||||
AppDatabase::class.java, "selfoss-database"
|
||||
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).build()
|
||||
}
|
||||
|
||||
private lateinit var rootView: ViewGroup
|
||||
@ -86,14 +97,27 @@ class ArticleFragment : Fragment() {
|
||||
rootView = inflater
|
||||
.inflate(R.layout.fragment_article, container, false) as ViewGroup
|
||||
|
||||
val context: Context = activity!!
|
||||
|
||||
url = allItems[pageNumber.toInt()].getLinkDecoded()
|
||||
contentText = allItems[pageNumber.toInt()].content
|
||||
contentTitle = allItems[pageNumber.toInt()].title
|
||||
contentImage = allItems[pageNumber.toInt()].getThumbnail(activity!!)
|
||||
contentSource = allItems[pageNumber.toInt()].sourceAndDateText()
|
||||
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
editor = prefs.edit()
|
||||
fontSize = prefs.getString("reader_font_size", "14").toInt()
|
||||
|
||||
val settings = activity!!.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||
val debugReadingItems = prefs.getBoolean("read_debug", false)
|
||||
|
||||
val api = SelfossApi(
|
||||
context!!,
|
||||
activity!!,
|
||||
settings.getBoolean("isSelfSignedCert", false),
|
||||
prefs.getString("api_timeout", "-1").toLong(),
|
||||
prefs.getBoolean("should_log_everything", false)
|
||||
)
|
||||
|
||||
fab = rootView.fab
|
||||
|
||||
fab.backgroundTintList = ColorStateList.valueOf(appColors.colorAccent)
|
||||
@ -109,17 +133,12 @@ class ArticleFragment : Fragment() {
|
||||
mCustomTabActivityHelper = CustomTabActivityHelper()
|
||||
mCustomTabActivityHelper.bindCustomTabsService(activity)
|
||||
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
editor = prefs.edit()
|
||||
fontSize = prefs.getString("reader_font_size", "14").toInt()
|
||||
showMalformedUrl = prefs.getBoolean("show_error_malformed_url", true)
|
||||
|
||||
|
||||
floatingToolbar.setClickListener(
|
||||
object : FloatingToolbar.ItemClickListener {
|
||||
override fun onItemClick(item: MenuItem) {
|
||||
when (item.itemId) {
|
||||
R.id.more_action -> getContentFromMercury(customTabsIntent, prefs, context)
|
||||
R.id.more_action -> getContentFromMercury(customTabsIntent, prefs)
|
||||
R.id.share_action -> activity!!.shareLink(url)
|
||||
R.id.open_action -> activity!!.openItemUrl(
|
||||
allItems,
|
||||
@ -130,6 +149,41 @@ class ArticleFragment : Fragment() {
|
||||
false,
|
||||
activity!!
|
||||
)
|
||||
R.id.unread_action -> if ((context != null && context!!.isNetworkAccessible(null)) || context == null) {
|
||||
api.unmarkItem(allItems[pageNumber.toInt()].id).enqueue(
|
||||
object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<SuccessResponse>,
|
||||
response: Response<SuccessResponse>
|
||||
) {
|
||||
if (!response.succeeded() && debugReadingItems) {
|
||||
val message =
|
||||
"message: ${response.message()} " +
|
||||
"response isSuccess: ${response.isSuccessful} " +
|
||||
"response code: ${response.code()} " +
|
||||
"response message: ${response.message()} " +
|
||||
"response errorBody: ${response.errorBody()?.string()} " +
|
||||
"body success: ${response.body()?.success} " +
|
||||
"body isSuccess: ${response.body()?.isSuccess}"
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(Exception(message), activity!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(
|
||||
call: Call<SuccessResponse>,
|
||||
t: Throwable
|
||||
) {
|
||||
if (debugReadingItems) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(t, activity!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
thread {
|
||||
db.actionsDao().insertAllActions(ActionEntity(allItems[pageNumber.toInt()].id, false, true, false, false))
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
@ -142,16 +196,16 @@ class ArticleFragment : Fragment() {
|
||||
rootView.source.text = contentSource
|
||||
|
||||
if (contentText.isEmptyOrNullOrNullString()) {
|
||||
getContentFromMercury(customTabsIntent, prefs, context)
|
||||
getContentFromMercury(customTabsIntent, prefs)
|
||||
} else {
|
||||
rootView.titleView.text = contentTitle
|
||||
|
||||
htmlToWebview(contentText, prefs, context)
|
||||
htmlToWebview(contentText, prefs)
|
||||
|
||||
if (!contentImage.isEmptyOrNullOrNullString()) {
|
||||
if (!contentImage.isEmptyOrNullOrNullString() && context != null) {
|
||||
rootView.imageView.visibility = View.VISIBLE
|
||||
Glide
|
||||
.with(context)
|
||||
.with(context!!)
|
||||
.asBitmap()
|
||||
.load(contentImage)
|
||||
.apply(RequestOptions.fitCenterTransform())
|
||||
@ -176,9 +230,9 @@ class ArticleFragment : Fragment() {
|
||||
|
||||
private fun getContentFromMercury(
|
||||
customTabsIntent: CustomTabsIntent,
|
||||
prefs: SharedPreferences,
|
||||
context: Context
|
||||
prefs: SharedPreferences
|
||||
) {
|
||||
if ((context != null && context!!.isNetworkAccessible(null)) || context == null) {
|
||||
rootView.progressBar.visibility = View.VISIBLE
|
||||
val parser = MercuryApi(
|
||||
prefs.getBoolean("should_log_everything", false)
|
||||
@ -195,35 +249,47 @@ class ArticleFragment : Fragment() {
|
||||
if (response.body() != null && response.body()!!.content != null && !response.body()!!.content.isNullOrEmpty()) {
|
||||
try {
|
||||
rootView.titleView.text = response.body()!!.title
|
||||
try {
|
||||
// Note: Mercury may return relative urls... If it does the url val will not be changed.
|
||||
URL(response.body()!!.url)
|
||||
url = response.body()!!.url
|
||||
} catch (e: MalformedURLException) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, activity!!)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context)
|
||||
if (context != null) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
htmlToWebview(response.body()!!.content.orEmpty(), prefs, context)
|
||||
htmlToWebview(response.body()!!.content.orEmpty(), prefs)
|
||||
} catch (e: Exception) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context)
|
||||
if (context != null) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (response.body()!!.lead_image_url != null && !response.body()!!.lead_image_url.isNullOrEmpty()) {
|
||||
if (response.body()!!.lead_image_url != null && !response.body()!!.lead_image_url.isNullOrEmpty() && context != null) {
|
||||
rootView.imageView.visibility = View.VISIBLE
|
||||
try {
|
||||
Glide
|
||||
.with(context)
|
||||
.with(context!!)
|
||||
.asBitmap()
|
||||
.load(response.body()!!.lead_image_url)
|
||||
.apply(RequestOptions.fitCenterTransform())
|
||||
.into(rootView.imageView)
|
||||
} catch (e: Exception) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context)
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
|
||||
}
|
||||
} else {
|
||||
rootView.imageView.visibility = View.GONE
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context)
|
||||
if (context != null) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
@ -231,17 +297,23 @@ class ArticleFragment : Fragment() {
|
||||
|
||||
rootView.progressBar.visibility = View.GONE
|
||||
} catch (e: Exception) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context)
|
||||
if (context != null) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
openInBrowserAfterFailing(customTabsIntent)
|
||||
} catch (e: Exception) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context)
|
||||
if (context != null) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context)
|
||||
if (context != null) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -252,32 +324,49 @@ class ArticleFragment : Fragment() {
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun htmlToWebview(c: String, prefs: SharedPreferences, context: Context) {
|
||||
|
||||
private fun htmlToWebview(c: String, prefs: SharedPreferences) {
|
||||
val stringColor = String.format("#%06X", 0xFFFFFF and appColors.colorAccent)
|
||||
|
||||
rootView.webcontent.visibility = View.VISIBLE
|
||||
val (textColor, backgroundColor) = if (appColors.isDarkTheme) {
|
||||
if (context != null) {
|
||||
rootView.webcontent.setBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
context!!,
|
||||
R.color.dark_webview
|
||||
)
|
||||
)
|
||||
Pair(ContextCompat.getColor(context, R.color.dark_webview_text), ContextCompat.getColor(context, R.color.light_webview_text))
|
||||
Pair(ContextCompat.getColor(context!!, R.color.dark_webview_text), ContextCompat.getColor(context!!, R.color.light_webview_text))
|
||||
} else {
|
||||
Pair(null, null)
|
||||
}
|
||||
} else {
|
||||
if (context != null) {
|
||||
rootView.webcontent.setBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
context!!,
|
||||
R.color.light_webview
|
||||
)
|
||||
)
|
||||
Pair(ContextCompat.getColor(context, R.color.light_webview_text), ContextCompat.getColor(context, R.color.dark_webview_text))
|
||||
Pair(ContextCompat.getColor(context!!, R.color.light_webview_text), ContextCompat.getColor(context!!, R.color.dark_webview_text))
|
||||
} else {
|
||||
Pair(null, null)
|
||||
}
|
||||
}
|
||||
|
||||
val stringTextColor = String.format("#%06X", 0xFFFFFF and textColor)
|
||||
val stringBackgroundColor = String.format("#%06X", 0xFFFFFF and backgroundColor)
|
||||
val stringTextColor: String = if (textColor != null) {
|
||||
String.format("#%06X", 0xFFFFFF and textColor)
|
||||
} else {
|
||||
"#000000"
|
||||
}
|
||||
|
||||
val stringBackgroundColor = if (backgroundColor != null) {
|
||||
String.format("#%06X", 0xFFFFFF and backgroundColor)
|
||||
} else {
|
||||
"#FFFFFF"
|
||||
}
|
||||
|
||||
rootView.webcontent.settings.useWideViewPort = true
|
||||
rootView.webcontent.settings.loadWithOverviewMode = true
|
||||
@ -296,76 +385,46 @@ class ArticleFragment : Fragment() {
|
||||
val itemUrl = URL(url)
|
||||
baseUrl = itemUrl.protocol + "://" + itemUrl.host
|
||||
} catch (e: MalformedURLException) {
|
||||
if (showMalformedUrl) {
|
||||
val alertDialog = AlertDialog.Builder(context).create()
|
||||
alertDialog.setTitle("Error")
|
||||
alertDialog.setMessage("You are encountering a bug that I can't solve. Can you please contact me to solve the issue, please ?")
|
||||
alertDialog.setButton(
|
||||
AlertDialog.BUTTON_POSITIVE,
|
||||
"Send mail",
|
||||
{ dialog, _ ->
|
||||
|
||||
// This won't be translated because it should only be temporary.
|
||||
val to = Config.feedbackEmail
|
||||
val subject= "[ReaderForSelfoss MalformedURLException]"
|
||||
val body= "Please specify the source, item and spout you are using for the url below : \n ${e.message}"
|
||||
val mailTo = "mailto:" + to + "?&subject=" + Uri.encode(subject) + "&body=" + Uri.encode(body)
|
||||
|
||||
val emailIntent = Intent(Intent.ACTION_VIEW)
|
||||
emailIntent.data = Uri.parse(mailTo)
|
||||
startActivity(emailIntent)
|
||||
|
||||
dialog.dismiss()
|
||||
}
|
||||
)
|
||||
alertDialog.setButton(
|
||||
AlertDialog.BUTTON_NEUTRAL,
|
||||
"Not now",
|
||||
{ dialog, _ -> dialog.dismiss() }
|
||||
)
|
||||
alertDialog.setButton(
|
||||
AlertDialog.BUTTON_NEGATIVE,
|
||||
"Don't show anymore.",
|
||||
{ dialog, _ ->
|
||||
editor.putBoolean("show_error_malformed_url", false)
|
||||
editor.apply()
|
||||
dialog.dismiss()
|
||||
}
|
||||
)
|
||||
alertDialog.show()
|
||||
}
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, activity!!)
|
||||
}
|
||||
|
||||
rootView.webcontent.loadDataWithBaseURL(
|
||||
baseUrl,
|
||||
"""<style>
|
||||
|img {
|
||||
"""<html>
|
||||
|<head>
|
||||
| <style>
|
||||
| img {
|
||||
| display: inline-block;
|
||||
| height: auto;
|
||||
| width: 100%;
|
||||
| max-width: 100%;
|
||||
|}
|
||||
|a {
|
||||
| }
|
||||
| a {
|
||||
| color: $stringColor !important;
|
||||
|}
|
||||
|*:not(a) {
|
||||
| }
|
||||
| *:not(a) {
|
||||
| color: $stringTextColor;
|
||||
|}
|
||||
|* {
|
||||
| }
|
||||
| * {
|
||||
| font-size: ${fontSize.toPx}px;
|
||||
| text-align: justify;
|
||||
| word-break: break-word;
|
||||
| overflow:hidden;
|
||||
|}
|
||||
|a, pre, code {
|
||||
| }
|
||||
| a, pre, code {
|
||||
| text-align: left;
|
||||
|}
|
||||
|pre, code {
|
||||
| }
|
||||
| pre, code {
|
||||
| white-space: pre-wrap;
|
||||
| width:100%;
|
||||
| background-color: $stringBackgroundColor;
|
||||
|}</style>$c""".trimMargin(),
|
||||
"text/html; charset=utf-8",
|
||||
| }
|
||||
| </style>
|
||||
|</head>
|
||||
|<body>
|
||||
| $c
|
||||
|</body>""".trimMargin(),
|
||||
"text/html",
|
||||
"utf-8",
|
||||
null
|
||||
)
|
||||
|
@ -0,0 +1,23 @@
|
||||
package apps.amine.bou.readerforselfoss.persistence.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity
|
||||
|
||||
@Dao
|
||||
interface ActionsDao {
|
||||
@Query("SELECT * FROM actions order by id asc")
|
||||
fun actions(): List<ActionEntity>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertAllActions(vararg actions: ActionEntity)
|
||||
|
||||
@Query("DELETE FROM actions WHERE articleid = :article_id AND read = 1")
|
||||
fun deleteReadActionForArticle(article_id: String)
|
||||
|
||||
@Delete
|
||||
fun delete(action: ActionEntity)
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
package apps.amine.bou.readerforselfoss.persistence.dao
|
||||
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.SourceEntity
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.TagEntity
|
||||
|
||||
@Dao
|
||||
interface DrawerDataDao {
|
||||
@Query("SELECT * FROM tags")
|
||||
fun tags(): List<TagEntity>
|
||||
|
||||
@Query("SELECT * FROM sources")
|
||||
fun sources(): List<SourceEntity>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertAllTags(vararg tags: TagEntity)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertAllSources(vararg sources: SourceEntity)
|
||||
|
||||
@Query("DELETE FROM tags")
|
||||
fun deleteAllTags()
|
||||
|
||||
@Query("DELETE FROM sources")
|
||||
fun deleteAllSources()
|
||||
|
||||
@Delete
|
||||
fun deleteTag(tag: TagEntity)
|
||||
|
||||
@Delete
|
||||
fun deleteSource(source: SourceEntity)
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
package apps.amine.bou.readerforselfoss.persistence.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.ItemEntity
|
||||
import androidx.room.Update
|
||||
|
||||
|
||||
|
||||
@Dao
|
||||
interface ItemsDao {
|
||||
@Query("SELECT * FROM items order by id desc")
|
||||
fun items(): List<ItemEntity>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertAllItems(vararg items: ItemEntity)
|
||||
|
||||
@Query("DELETE FROM items")
|
||||
fun deleteAllItems()
|
||||
|
||||
@Delete
|
||||
fun delete(item: ItemEntity)
|
||||
|
||||
@Update
|
||||
fun updateItem(item: ItemEntity)
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package apps.amine.bou.readerforselfoss.persistence.database
|
||||
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.Database
|
||||
import apps.amine.bou.readerforselfoss.persistence.dao.ActionsDao
|
||||
import apps.amine.bou.readerforselfoss.persistence.dao.DrawerDataDao
|
||||
import apps.amine.bou.readerforselfoss.persistence.dao.ItemsDao
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.ItemEntity
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.SourceEntity
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.TagEntity
|
||||
|
||||
@Database(entities = [TagEntity::class, SourceEntity::class, ItemEntity::class, ActionEntity::class], version = 3)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
abstract fun drawerDataDao(): DrawerDataDao
|
||||
|
||||
abstract fun itemsDao(): ItemsDao
|
||||
|
||||
abstract fun actionsDao(): ActionsDao
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
package apps.amine.bou.readerforselfoss.persistence.entities
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "actions")
|
||||
data class ActionEntity(
|
||||
@ColumnInfo(name = "articleid")
|
||||
val articleId: String,
|
||||
@ColumnInfo(name = "read")
|
||||
val read: Boolean,
|
||||
@ColumnInfo(name = "unread")
|
||||
val unread: Boolean,
|
||||
@ColumnInfo(name = "starred")
|
||||
var starred: Boolean,
|
||||
@ColumnInfo(name = "unstarred")
|
||||
var unstarred: Boolean
|
||||
) {
|
||||
@PrimaryKey(autoGenerate = true)
|
||||
var id: Int = 0
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package apps.amine.bou.readerforselfoss.persistence.entities
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "tags")
|
||||
data class TagEntity(
|
||||
@PrimaryKey
|
||||
@ColumnInfo(name = "tag")
|
||||
val tag: String,
|
||||
@ColumnInfo(name = "color")
|
||||
val color: String,
|
||||
@ColumnInfo(name = "unread")
|
||||
val unread: Int
|
||||
)
|
||||
|
||||
@Entity(tableName = "sources")
|
||||
data class SourceEntity(
|
||||
@PrimaryKey
|
||||
@ColumnInfo(name = "id")
|
||||
val id: String,
|
||||
@ColumnInfo(name = "title")
|
||||
val title: String,
|
||||
@ColumnInfo(name = "tags")
|
||||
val tags: String,
|
||||
@ColumnInfo(name = "spout")
|
||||
val spout: String,
|
||||
@ColumnInfo(name = "error")
|
||||
val error: String,
|
||||
@ColumnInfo(name = "icon")
|
||||
val icon: String
|
||||
)
|
@ -0,0 +1,32 @@
|
||||
package apps.amine.bou.readerforselfoss.persistence.entities
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "items")
|
||||
data class ItemEntity(
|
||||
@PrimaryKey
|
||||
@ColumnInfo(name = "id")
|
||||
val id: String,
|
||||
@ColumnInfo(name = "datetime")
|
||||
val datetime: String,
|
||||
@ColumnInfo(name = "title")
|
||||
val title: String,
|
||||
@ColumnInfo(name = "content")
|
||||
val content: String,
|
||||
@ColumnInfo(name = "unread")
|
||||
val unread: Boolean,
|
||||
@ColumnInfo(name = "starred")
|
||||
var starred: Boolean,
|
||||
@ColumnInfo(name = "thumbnail")
|
||||
val thumbnail: String,
|
||||
@ColumnInfo(name = "icon")
|
||||
val icon: String,
|
||||
@ColumnInfo(name = "link")
|
||||
val link: String,
|
||||
@ColumnInfo(name = "sourcetitle")
|
||||
val sourcetitle: String,
|
||||
@ColumnInfo(name = "tags")
|
||||
val tags: String
|
||||
)
|
@ -0,0 +1,16 @@
|
||||
package apps.amine.bou.readerforselfoss.persistence.migrations
|
||||
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import androidx.room.migration.Migration
|
||||
|
||||
val MIGRATION_1_2: Migration = object : Migration(1, 2) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE IF NOT EXISTS `items` (`id` TEXT NOT NULL, `datetime` TEXT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `unread` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `thumbnail` TEXT NOT NULL, `icon` TEXT NOT NULL, `link` TEXT NOT NULL, `sourcetitle` TEXT NOT NULL, `tags` TEXT NOT NULL, PRIMARY KEY(`id`))")
|
||||
}
|
||||
}
|
||||
|
||||
val MIGRATION_2_3: Migration = object : Migration(2, 3) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE IF NOT EXISTS `actions` (`id` INTEGER NOT NULL, `articleid` TEXT NOT NULL, `read` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `unstarred` INTEGER NOT NULL, PRIMARY KEY(`id`))")
|
||||
}
|
||||
}
|
@ -1,15 +1,16 @@
|
||||
package apps.amine.bou.readerforselfoss.settings;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.support.annotation.LayoutRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.AppBarLayout;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AppCompatDelegate;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.material.appbar.AppBarLayout;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
@ -48,10 +49,11 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity {
|
||||
AppBarLayout bar = (AppBarLayout) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
|
||||
Toolbar toolbar = bar.findViewById(R.id.toolbar);
|
||||
|
||||
// TODO: all switches
|
||||
Scoop.getInstance()
|
||||
.bind(this, Toppings.PRIMARY.getValue(), toolbar)
|
||||
.bindStatusBar(this, Toppings.PRIMARY_DARK.getValue());
|
||||
Scoop scoop = Scoop.getInstance();
|
||||
scoop.bind(this, Toppings.PRIMARY.getValue(), toolbar);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.getValue());
|
||||
}
|
||||
|
||||
setSupportActionBar(toolbar);
|
||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||
|
@ -19,7 +19,7 @@ import android.preference.PreferenceActivity;
|
||||
import android.preference.PreferenceFragment;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.preference.SwitchPreference;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import android.text.Editable;
|
||||
import android.text.InputFilter;
|
||||
import android.text.Spanned;
|
||||
@ -31,7 +31,6 @@ import android.widget.Toast;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import apps.amine.bou.readerforselfoss.BuildConfig;
|
||||
import apps.amine.bou.readerforselfoss.R;
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors;
|
||||
import apps.amine.bou.readerforselfoss.utils.Config;
|
||||
@ -136,6 +135,8 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
||||
return PreferenceFragment.class.getName().equals(fragmentName)
|
||||
|| GeneralPreferenceFragment.class.getName().equals(fragmentName)
|
||||
|| ArticleViewerPreferenceFragment.class.getName().equals(fragmentName)
|
||||
|| OfflinePreferenceFragment.class.getName().equals(fragmentName)
|
||||
|| ExperimentalPreferenceFragment.class.getName().equals(fragmentName)
|
||||
|| DebugPreferenceFragment.class.getName().equals(fragmentName)
|
||||
|| LinksPreferenceFragment.class.getName().equals(fragmentName)
|
||||
|| ThemePreferenceFragment.class.getName().equals(fragmentName);
|
||||
@ -153,17 +154,6 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
||||
addPreferencesFromResource(R.xml.pref_general);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
SwitchPreference cardViewActive = (SwitchPreference) findPreference("card_view_active");
|
||||
final SwitchPreference tabOnTap = (SwitchPreference) findPreference("tab_on_tap");
|
||||
tabOnTap.setEnabled(!cardViewActive.isChecked());
|
||||
cardViewActive.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue) {
|
||||
boolean isEnabled = (Boolean) newValue;
|
||||
tabOnTap.setEnabled(!isEnabled);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
EditTextPreference itemsNumber = (EditTextPreference) findPreference("prefer_api_items_number");
|
||||
itemsNumber.getEditText().setFilters(new InputFilter[]{
|
||||
new InputFilter() {
|
||||
@ -181,6 +171,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -374,6 +365,47 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public static class OfflinePreferenceFragment extends PreferenceFragment {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
addPreferencesFromResource(R.xml.pref_offline);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
if (id == android.R.id.home) {
|
||||
getActivity().finish();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public static class ExperimentalPreferenceFragment extends PreferenceFragment {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
addPreferencesFromResource(R.xml.pref_experimental);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
if (id == android.R.id.home) {
|
||||
getActivity().finish();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
|
@ -3,8 +3,8 @@ package apps.amine.bou.readerforselfoss.themes
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.annotation.ColorInt
|
||||
import android.support.v7.view.ContextThemeWrapper
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import android.util.TypedValue
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
import android.view.LayoutInflater
|
||||
|
@ -1,6 +1,6 @@
|
||||
package apps.amine.bou.readerforselfoss.transformers
|
||||
|
||||
import android.support.v4.view.ViewPager
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import android.view.View
|
||||
|
||||
class DepthPageTransformer : ViewPager.PageTransformer {
|
||||
|
@ -36,6 +36,10 @@ class Config(c: Context) {
|
||||
|
||||
const val trackerUrl = "https://github.com/aminecmi/ReaderforSelfoss/issues"
|
||||
|
||||
const val syncChannelId = "sync-channel-id"
|
||||
|
||||
const val newItemsChannelId = "new-items-channel-id"
|
||||
|
||||
fun logoutAndRedirect(
|
||||
c: Context,
|
||||
callingActivity: Activity,
|
||||
|
@ -3,6 +3,7 @@ package apps.amine.bou.readerforselfoss.utils
|
||||
import android.content.Context
|
||||
import android.text.format.DateUtils
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossTagType
|
||||
import org.acra.ACRA
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
@ -44,8 +45,8 @@ fun Item.toggleStar(): Item {
|
||||
fun List<Item>.flattenTags(): List<Item> =
|
||||
this.flatMap {
|
||||
val item = it
|
||||
val tags: List<String> = it.tags.split(",")
|
||||
tags.map {
|
||||
item.copy(tags = it.trim())
|
||||
val tags: List<String> = it.tags.tags.split(",")
|
||||
tags.map { t ->
|
||||
item.copy(tags = SelfossTagType(t.trim()))
|
||||
}
|
||||
}
|
@ -6,8 +6,13 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import android.support.customtabs.CustomTabsIntent
|
||||
import android.text.Spannable
|
||||
import android.text.style.ClickableSpan
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import android.util.Patterns
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
import apps.amine.bou.readerforselfoss.ReaderActivity
|
||||
@ -146,3 +151,40 @@ fun Context.openInBrowserAsNewTask(i: Item) {
|
||||
intent.data = Uri.parse(i.getLinkDecoded().toStringUriWithHttp())
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
class LinkOnTouchListener: View.OnTouchListener {
|
||||
override fun onTouch(v: View?, event: MotionEvent?): Boolean {
|
||||
var ret = false
|
||||
val widget: TextView = v as TextView
|
||||
val text: CharSequence = widget.text
|
||||
val stext = Spannable.Factory.getInstance().newSpannable(text)
|
||||
|
||||
val action = event!!.action
|
||||
|
||||
if (action == MotionEvent.ACTION_UP ||
|
||||
action == MotionEvent.ACTION_DOWN) {
|
||||
var x: Float = event.x
|
||||
var y: Float = event.y
|
||||
|
||||
x -= widget.totalPaddingLeft
|
||||
y -= widget.totalPaddingTop
|
||||
|
||||
x += widget.scrollX
|
||||
y += widget.scrollY
|
||||
|
||||
val layout = widget.layout
|
||||
val line = layout.getLineForVertical(y.toInt())
|
||||
val off = layout.getOffsetForHorizontal(line, x)
|
||||
|
||||
val link = stext.getSpans(off, off, ClickableSpan::class.java)
|
||||
|
||||
if (link.isNotEmpty()) {
|
||||
if (action == MotionEvent.ACTION_UP) {
|
||||
link[0].onClick(widget)
|
||||
}
|
||||
ret = true
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
}
|
||||
|
@ -1,54 +0,0 @@
|
||||
package apps.amine.bou.readerforselfoss.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.support.design.widget.CoordinatorLayout
|
||||
import android.support.design.widget.FloatingActionButton
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
|
||||
class ScrollAwareFABBehavior(
|
||||
context: Context,
|
||||
attrs: AttributeSet
|
||||
) : CoordinatorLayout.Behavior<FloatingActionButton>() {
|
||||
|
||||
|
||||
override fun onStartNestedScroll(
|
||||
coordinatorLayout: CoordinatorLayout,
|
||||
child: FloatingActionButton,
|
||||
directTargetChild: View,
|
||||
target: View,
|
||||
nestedScrollAxes: Int
|
||||
): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onNestedScroll(
|
||||
coordinatorLayout: CoordinatorLayout,
|
||||
child: FloatingActionButton,
|
||||
target: View,
|
||||
dxConsumed: Int,
|
||||
dyConsumed: Int,
|
||||
dxUnconsumed: Int,
|
||||
dyUnconsumed: Int
|
||||
) {
|
||||
super.onNestedScroll(
|
||||
coordinatorLayout,
|
||||
child,
|
||||
target,
|
||||
dxConsumed,
|
||||
dyConsumed,
|
||||
dxUnconsumed,
|
||||
dyUnconsumed
|
||||
)
|
||||
if (dyConsumed > 0 && child.visibility == View.VISIBLE) {
|
||||
child.hide(object : FloatingActionButton.OnVisibilityChangedListener() {
|
||||
override fun onHidden(fab: FloatingActionButton?) {
|
||||
super.onHidden(fab)
|
||||
fab!!.visibility = View.INVISIBLE
|
||||
}
|
||||
})
|
||||
} else if (dyConsumed < 0 && child.visibility != View.VISIBLE) {
|
||||
child.show()
|
||||
}
|
||||
}
|
||||
}
|
@ -4,10 +4,10 @@ package apps.amine.bou.readerforselfoss.utils.customtabs;
|
||||
import android.app.Activity;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.customtabs.CustomTabsClient;
|
||||
import android.support.customtabs.CustomTabsIntent;
|
||||
import android.support.customtabs.CustomTabsServiceConnection;
|
||||
import android.support.customtabs.CustomTabsSession;
|
||||
import androidx.browser.customtabs.CustomTabsClient;
|
||||
import androidx.browser.customtabs.CustomTabsIntent;
|
||||
import androidx.browser.customtabs.CustomTabsServiceConnection;
|
||||
import androidx.browser.customtabs.CustomTabsSession;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -7,7 +7,7 @@ import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.support.customtabs.CustomTabsService;
|
||||
import androidx.browser.customtabs.CustomTabsService;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
|
@ -2,8 +2,8 @@ package apps.amine.bou.readerforselfoss.utils.customtabs;
|
||||
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.support.customtabs.CustomTabsClient;
|
||||
import android.support.customtabs.CustomTabsServiceConnection;
|
||||
import androidx.browser.customtabs.CustomTabsClient;
|
||||
import androidx.browser.customtabs.CustomTabsServiceConnection;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
package apps.amine.bou.readerforselfoss.utils.customtabs;
|
||||
|
||||
|
||||
import android.support.customtabs.CustomTabsClient;
|
||||
import androidx.browser.customtabs.CustomTabsClient;
|
||||
|
||||
|
||||
public interface ServiceConnectionCallback {
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* From https://github.com/mikepenz/MaterialDrawer/blob/develop/app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/CustomBaseViewHolder.java */
|
||||
package apps.amine.bou.readerforselfoss.utils.drawer
|
||||
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
|
@ -2,10 +2,10 @@
|
||||
package apps.amine.bou.readerforselfoss.utils.drawer
|
||||
|
||||
import android.net.Uri
|
||||
import android.support.annotation.ColorInt
|
||||
import android.support.annotation.ColorRes
|
||||
import android.support.annotation.StringRes
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
import com.mikepenz.materialdrawer.holder.ColorHolder
|
||||
import com.mikepenz.materialdrawer.holder.ImageHolder
|
||||
|
@ -1,8 +1,8 @@
|
||||
/* From https://github.com/mikepenz/MaterialDrawer/blob/develop/app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/CustomUrlPrimaryDrawerItem.java */
|
||||
package apps.amine.bou.readerforselfoss.utils.drawer
|
||||
|
||||
import android.support.annotation.LayoutRes
|
||||
import android.support.annotation.StringRes
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.annotation.StringRes
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
|
@ -2,7 +2,7 @@ package apps.amine.bou.readerforselfoss.utils.glide
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory
|
||||
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
|
||||
import android.widget.ImageView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
|
@ -0,0 +1,45 @@
|
||||
package apps.amine.bou.readerforselfoss.utils.network
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkInfo
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
|
||||
var snackBarShown = false
|
||||
var view: View? = null
|
||||
lateinit var s: Snackbar
|
||||
|
||||
fun Context.isNetworkAccessible(v: View?, overrideOffline: Boolean = false): Boolean {
|
||||
val cm = this.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
val activeNetwork: NetworkInfo? = cm.activeNetworkInfo
|
||||
val networkIsAccessible = activeNetwork != null && activeNetwork.isConnectedOrConnecting
|
||||
|
||||
if (v != null && (!networkIsAccessible || overrideOffline) && (!snackBarShown || v != view)) {
|
||||
view = v
|
||||
s = Snackbar
|
||||
.make(
|
||||
v,
|
||||
R.string.no_network_connectivity,
|
||||
Snackbar.LENGTH_INDEFINITE
|
||||
)
|
||||
|
||||
s.setAction(android.R.string.ok) {
|
||||
snackBarShown = false
|
||||
s.dismiss()
|
||||
}
|
||||
|
||||
val view = s.view
|
||||
val tv: TextView = view.findViewById(com.google.android.material.R.id.snackbar_text)
|
||||
tv.setTextColor(Color.WHITE)
|
||||
s.show()
|
||||
snackBarShown = true
|
||||
}
|
||||
if (snackBarShown && networkIsAccessible && !overrideOffline) {
|
||||
s.dismiss()
|
||||
}
|
||||
return if(overrideOffline) overrideOffline else networkIsAccessible
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package apps.amine.bou.readerforselfoss.utils.persistence
|
||||
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossTagType
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Source
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Tag
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.ItemEntity
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.SourceEntity
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.TagEntity
|
||||
|
||||
fun TagEntity.toView(): Tag =
|
||||
Tag(
|
||||
this.tag,
|
||||
this.color,
|
||||
this.unread
|
||||
)
|
||||
|
||||
fun SourceEntity.toView(): Source =
|
||||
Source(
|
||||
this.id,
|
||||
this.title,
|
||||
SelfossTagType(this.tags),
|
||||
this.spout,
|
||||
this.error,
|
||||
this.icon
|
||||
)
|
||||
|
||||
fun Source.toEntity(): SourceEntity =
|
||||
SourceEntity(
|
||||
this.id,
|
||||
this.title,
|
||||
this.tags.tags,
|
||||
this.spout,
|
||||
this.error,
|
||||
this.icon.orEmpty()
|
||||
)
|
||||
|
||||
fun Tag.toEntity(): TagEntity =
|
||||
TagEntity(
|
||||
this.tag,
|
||||
this.color,
|
||||
this.unread
|
||||
)
|
||||
|
||||
fun ItemEntity.toView(): Item =
|
||||
Item(
|
||||
this.id,
|
||||
this.datetime,
|
||||
this.title,
|
||||
this.content,
|
||||
this.unread,
|
||||
this.starred,
|
||||
this.thumbnail,
|
||||
this.icon,
|
||||
this.link,
|
||||
this.sourcetitle,
|
||||
SelfossTagType(this.tags)
|
||||
)
|
||||
|
||||
fun Item.toEntity(): ItemEntity =
|
||||
ItemEntity(
|
||||
this.id,
|
||||
this.datetime,
|
||||
this.title,
|
||||
this.content,
|
||||
this.unread,
|
||||
this.starred,
|
||||
this.thumbnail,
|
||||
this.icon,
|
||||
this.link,
|
||||
this.sourcetitle,
|
||||
this.tags.tags
|
||||
)
|
BIN
app/src/main/res/drawable-hdpi/ic_action_lab.png
Normal file
After Width: | Height: | Size: 683 B |
BIN
app/src/main/res/drawable-hdpi/ic_cloud_download.png
Normal file
After Width: | Height: | Size: 334 B |
BIN
app/src/main/res/drawable-hdpi/ic_fiber_new.png
Normal file
After Width: | Height: | Size: 324 B |
After Width: | Height: | Size: 523 B |
BIN
app/src/main/res/drawable-mdpi/ic_action_lab.png
Normal file
After Width: | Height: | Size: 409 B |
BIN
app/src/main/res/drawable-mdpi/ic_cloud_download.png
Normal file
After Width: | Height: | Size: 228 B |
BIN
app/src/main/res/drawable-mdpi/ic_fiber_new.png
Normal file
After Width: | Height: | Size: 215 B |
After Width: | Height: | Size: 361 B |
BIN
app/src/main/res/drawable-xhdpi/ic_action_lab.png
Normal file
After Width: | Height: | Size: 871 B |
BIN
app/src/main/res/drawable-xhdpi/ic_cloud_download.png
Normal file
After Width: | Height: | Size: 380 B |
BIN
app/src/main/res/drawable-xhdpi/ic_fiber_new.png
Normal file
After Width: | Height: | Size: 327 B |
After Width: | Height: | Size: 660 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_action_lab.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_cloud_download.png
Normal file
After Width: | Height: | Size: 547 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_fiber_new.png
Normal file
After Width: | Height: | Size: 490 B |
After Width: | Height: | Size: 982 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_action_lab.png
Normal file
After Width: | Height: | Size: 3.2 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_cloud_download.png
Normal file
After Width: | Height: | Size: 678 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_fiber_new.png
Normal file
After Width: | Height: | Size: 567 B |
After Width: | Height: | Size: 1.2 KiB |
@ -1,4 +0,0 @@
|
||||
<vector android:height="24dp" android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FFFFFF" android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
|
||||
</vector>
|
@ -1,4 +0,0 @@
|
||||
<vector android:height="24dp" android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FFFFFF" android:pathData="M1,21h4L5,9L1,9v12zM23,10c0,-1.1 -0.9,-2 -2,-2h-6.31l0.95,-4.57 0.03,-0.32c0,-0.41 -0.17,-0.79 -0.44,-1.06L14.17,1 7.59,7.59C7.22,7.95 7,8.45 7,9v10c0,1.1 0.9,2 2,2h9c0.83,0 1.54,-0.5 1.84,-1.22l3.02,-7.05c0.09,-0.23 0.14,-0.47 0.14,-0.73v-1.91l-0.01,-0.01L23,10z"/>
|
||||
</vector>
|
@ -10,22 +10,22 @@
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<android.support.design.widget.AppBarLayout
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:theme="@style/ToolBarStyle"
|
||||
app:popupTheme="?attr/toolbarPopupTheme" />
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
@ -121,7 +121,7 @@
|
||||
android:layout_marginBottom="16dp"
|
||||
app:layout_constraintVertical_bias="0.0"/>
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress"
|
||||
|
@ -30,13 +30,13 @@
|
||||
app:prompt_view_background_color="?attr/colorAccent"
|
||||
app:prompt_view_thanks_display_time_ms="2000"/>
|
||||
|
||||
<android.support.design.widget.CoordinatorLayout
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/coordLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/promptView">
|
||||
|
||||
<android.support.design.widget.CoordinatorLayout
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/intern_coordLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
@ -46,18 +46,18 @@
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<android.support.design.widget.AppBarLayout
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:theme="@style/ToolBarStyle"
|
||||
app:popupTheme="?attr/toolbarPopupTheme" />
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/drawer_layout"
|
||||
@ -66,7 +66,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<android.support.v4.widget.SwipeRefreshLayout
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
@ -86,30 +86,30 @@
|
||||
android:text="@string/nothing_here"
|
||||
android:textAlignment="center"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
|
||||
android:background="@color/transparent"
|
||||
android:background="@android:color/transparent"
|
||||
android:visibility="gone" />
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/transparent"
|
||||
android:background="@android:color/transparent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="60dp"
|
||||
android:scrollbars="vertical"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
</LinearLayout>
|
||||
|
||||
</android.support.v4.widget.SwipeRefreshLayout>
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
<com.ashokvarma.bottomnavigation.BottomNavigationBar
|
||||
android:layout_gravity="bottom"
|
||||
android:id="@+id/bottomBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"/>
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</RelativeLayout>
|
@ -6,18 +6,18 @@
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical"
|
||||
tools:context="apps.amine.bou.readerforselfoss.LoginActivity">
|
||||
<android.support.design.widget.AppBarLayout
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:theme="@style/ToolBarStyle"
|
||||
app:popupTheme="?attr/toolbarPopupTheme" />
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@ -45,7 +45,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<android.support.design.widget.TextInputLayout
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/urlLayout"
|
||||
@ -60,7 +60,7 @@
|
||||
android:inputType="textUri"
|
||||
android:maxLines="1" />
|
||||
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<Switch
|
||||
android:text="@string/withLoginSwitch"
|
||||
@ -69,7 +69,7 @@
|
||||
android:id="@+id/withLogin"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<android.support.design.widget.TextInputLayout
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/loginLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -83,9 +83,9 @@
|
||||
android:inputType="text"
|
||||
android:maxLines="1" />
|
||||
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<android.support.design.widget.TextInputLayout
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/passwordLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -99,7 +99,7 @@
|
||||
android:inputType="textPassword"
|
||||
android:maxLines="1" />
|
||||
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/withHttpLogin"
|
||||
@ -108,7 +108,7 @@
|
||||
android:layout_weight="1"
|
||||
android:text="@string/withHttpLoginSwitch" />
|
||||
|
||||
<android.support.design.widget.TextInputLayout
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/httpLoginInput"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@ -120,9 +120,9 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/prompt_http_login" />
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<android.support.design.widget.TextInputLayout
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/httpPasswordInput"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@ -134,7 +134,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/prompt_http_password"
|
||||
android:inputType="textPassword" />
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/withSelfhostedCert"
|
||||
|
@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="apps.amine.bou.readerforselfoss.MainActivity">
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
@ -1,10 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/reader_activity_view"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<android.support.design.widget.AppBarLayout
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appBarLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -12,16 +13,16 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:popupTheme="?attr/toolbarPopupTheme"
|
||||
app:theme="@style/ToolBarStyle" />
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<android.support.v4.view.ViewPager
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
@ -41,4 +42,4 @@
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/pager" />
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
@ -1,33 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="apps.amine.bou.readerforselfoss.SourcesActivity">
|
||||
<android.support.design.widget.AppBarLayout
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:theme="@style/ToolBarStyle"
|
||||
app:popupTheme="?attr/toolbarPopupTheme" />
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scrollbars="vertical"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
</android.support.v7.widget.RecyclerView>
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@ -40,6 +40,5 @@
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
app:layout_behavior="apps.amine.bou.readerforselfoss.utils.ScrollAwareFABBehavior" />
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
android:layout_marginRight="16dp"/>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.v7.widget.CardView
|
||||
<androidx.cardview.widget.CardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||
@ -18,7 +18,7 @@
|
||||
card_view:cardUseCompatPadding="true"
|
||||
card_view:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
app:srcCompat="@drawable/background_splash"
|
||||
card_view:layout_constraintBottom_toTopOf="@+id/constraintLayout" />
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/constraintLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
@ -143,7 +143,7 @@
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</android.support.v7.widget.CardView>
|
||||
</androidx.cardview.widget.CardView>
|
@ -1,4 +1,4 @@
|
||||
<android.support.design.widget.CoordinatorLayout
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
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"
|
||||
@ -6,12 +6,12 @@
|
||||
android:layout_height="match_parent"
|
||||
android:descendantFocusability="blocksDescendants">
|
||||
|
||||
<android.support.v4.widget.NestedScrollView
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/nestedScrollView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
@ -70,9 +70,9 @@
|
||||
app:layout_constraintTop_toBottomOf="@+id/source"
|
||||
tools:visibility="visible" />
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</android.support.v4.widget.NestedScrollView>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
@ -89,7 +89,7 @@
|
||||
android:layout_gravity="bottom"
|
||||
app:floatingMenu="@menu/reader_toolbar" />
|
||||
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@ -123,4 +123,4 @@
|
||||
android:progressTint="?attr/colorAccent" />
|
||||
</FrameLayout>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
@ -11,7 +11,6 @@
|
||||
android:id="@+id/itemImage"
|
||||
android:layout_width="88dp"
|
||||
android:layout_height="88dp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/actionBar"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
@ -40,79 +39,16 @@
|
||||
android:id="@+id/sourceTitleAndDate"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:gravity="start"
|
||||
android:textAlignment="viewStart"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/actionBar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toEndOf="@+id/itemImage"
|
||||
app:layout_constraintTop_toBottomOf="@+id/title"
|
||||
tools:text="Google Actualité Il y a 5h" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/actionBar"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="#BBBBBB"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
tools:visibility="visible">
|
||||
|
||||
<com.like.LikeButton
|
||||
android:id="@+id/favButton"
|
||||
android:layout_width="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:elevation="5dp"
|
||||
android:padding="4dp"
|
||||
app:icon_size="22dp"
|
||||
app:icon_type="heart" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/shareBtn"
|
||||
android:layout_width="35dp"
|
||||
android:layout_height="35dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_toLeftOf="@+id/favButton"
|
||||
android:layout_toStartOf="@+id/favButton"
|
||||
android:adjustViewBounds="true"
|
||||
android:background="@android:color/transparent"
|
||||
android:backgroundTint="?android:attr/textColorPrimary"
|
||||
android:elevation="5dp"
|
||||
android:padding="4dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_share_black_24dp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/browserBtn"
|
||||
android:layout_width="35dp"
|
||||
android:layout_height="35dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_toLeftOf="@+id/shareBtn"
|
||||
android:layout_toStartOf="@+id/shareBtn"
|
||||
android:adjustViewBounds="true"
|
||||
android:background="@android:color/transparent"
|
||||
android:backgroundTint="?android:attr/textColorPrimary"
|
||||
android:elevation="5dp"
|
||||
android:padding="4dp"
|
||||
android:scaleType="centerCrop"
|
||||
android:src="@drawable/ic_open_in_browser_black_24dp" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,15 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.design.widget.AppBarLayout
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:theme="@style/ToolBarStyle"
|
||||
app:popupTheme="?attr/toolbarPopupTheme" />
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
@ -52,4 +52,4 @@
|
||||
android:layout_width="34dp"
|
||||
android:layout_height="34dp"/>
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -6,13 +6,13 @@
|
||||
android:title="@string/menu_home_search"
|
||||
android:icon="@drawable/ic_action_search"
|
||||
app:showAsAction="ifRoom|collapseActionView"
|
||||
app:actionViewClass="android.support.v7.widget.SearchView" />
|
||||
app:actionViewClass="androidx.appcompat.widget.SearchView" />
|
||||
|
||||
<item android:id="@+id/readAll"
|
||||
android:icon="@drawable/ic_done_all_white_24dp"
|
||||
android:title="@string/readAll"
|
||||
android:orderInCategory="1"
|
||||
app:showAsAction="ifRoom"/>
|
||||
app:showAsAction="always"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/refresh"
|
||||
|
@ -2,6 +2,12 @@
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/unread_action"
|
||||
android:icon="@drawable/ic_fiber_new"
|
||||
android:title="@string/unmark"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/more_action"
|
||||
android:icon="@drawable/ic_chrome_reader_mode"
|
||||
|
@ -19,7 +19,6 @@
|
||||
<string name="action_disconnect">"Disconnect"</string>
|
||||
<string name="title_activity_settings">"Settings"</string>
|
||||
<string name="pref_header_general">"General"</string>
|
||||
<string name="pref_switch_actions_tap_title">"Tap action on the articles"</string>
|
||||
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
|
||||
<string name="add_source_hint_url">"Link"</string>
|
||||
<string name="add_source_hint_name">"Name"</string>
|
||||
@ -64,9 +63,6 @@
|
||||
<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="invitation_title">"Try this app for your Selfoss RSS feeds !"</string>
|
||||
<string name="invitation_message">"I use this app for my Selfoss RSS feeds. You may like it too !"</string>
|
||||
<string name="invitation_cta">"Try the app"</string>
|
||||
<string name="text_wrong_url">"You seem to be trying to use an invalid URL. Make sure it is correct, and if the problem persists, contact me (via the store contact link). Please note that the app needs you to be using Selfoss. You can't access RSS feeds without it."</string>
|
||||
<string name="pref_general_internal_browser_title">"Open links inside the app"</string>
|
||||
<string name="pref_general_internal_browser_on">"Articles will open inside the app"</string>
|
||||
@ -76,26 +72,12 @@
|
||||
<string name="prefer_article_viewer_off">"Will use the internal browser instead of the article viewer"</string>
|
||||
<string name="pref_general_category_links">"Link handling"</string>
|
||||
<string name="pref_general_category_displaying">"Displaying"</string>
|
||||
<string name="pref_general_category_actions">"Actions"</string>
|
||||
<string name="pref_switch_card_view_on">"The articles will be displayed as cards"</string>
|
||||
<string name="pref_switch_card_view_off">"The articles will be displayed as a list"</string>
|
||||
<string name="pref_switch_actions_tap_on">"Displays the action bar under the article"</string>
|
||||
<string name="pref_switch_actions_tap_off">"When selecting an article it will open in your selected browser"</string>
|
||||
<string name="menu_home_refresh">"Update remote"</string>
|
||||
<string name="refresh_success_response">"The remote is updated, you can now reload the articles list"</string>
|
||||
<string name="refresh_failer_message">"The update didn't work, try again later, or check your selfoss logs."</string>
|
||||
<string name="refresh_in_progress">"Refresh in progress"</string>
|
||||
<string name="new_apk_available_title">"A new APK is available."</string>
|
||||
<string name="new_apk_available_message">"A new APK is available to download on the official repository."</string>
|
||||
<string name="new_apk_available_get">"Download now"</string>
|
||||
<string name="new_apk_available_no">"Ignore version"</string>
|
||||
<string name="intro_hello_title">"Hi there !"</string>
|
||||
<string name="intro_hello_message">"Thanks for downloading the app !"</string>
|
||||
<string name="intro_needs_selfoss_title">"Before you start…"</string>
|
||||
<string name="intro_needs_selfoss_message">"You can't use the app without a Selfoss instance."</string>
|
||||
<string name="intro_needs_selfoss_link">"What is Selfoss ?"</string>
|
||||
<string name="intro_all_set_title">"All set !"</string>
|
||||
<string name="intro_all_set_message">"You are ready to use the app. Don't forget to go to the settings page to configure your app, and where you'll find some useful links."</string>
|
||||
<string name="card_height_title">Full height cards</string>
|
||||
<string name="card_height_on">Cards height will adjust to its content</string>
|
||||
<string name="card_height_off">Card height will be fixed</string>
|
||||
@ -108,7 +90,6 @@
|
||||
<string name="drawer_item_tags">Tags</string>
|
||||
<string name="drawer_item_sources">Sources</string>
|
||||
<string name="drawer_action_edit">edit</string>
|
||||
<string name="cache_drawer_error" tools:keep="@string/cache_drawer_error">Couldn\'t cache your drawer data</string>
|
||||
<string name="no_tags_loaded">No tags loaded</string>
|
||||
<string name="no_sources_loaded">No sources loaded</string>
|
||||
<string name="drawer_loading">Loading …</string>
|
||||
@ -127,6 +108,7 @@
|
||||
<string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string>
|
||||
<string name="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="read_debug_title">Read articles appearing as unread ?</string>
|
||||
<string name="read_debug_off">No log when marking an item as read</string>
|
||||
<string name="read_debug_on">Api calls will be logged when marking an article as read</string>
|
||||
@ -158,12 +140,32 @@
|
||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||
<string name="crash_dialog_text">Something went wrong. Please send the report to the developer.</string>
|
||||
<string name="crash_dialog_comment">You can add any helpful details in the comment bellow. Don\'t include any personal data in your comment. You could send me and email with your debug id, and I\'ll keep you posted when the issue is resolved.</string>
|
||||
<string name="pref_acra_user_email">Contact email</string>
|
||||
<string name="pref_acra_user_email_summary">Add an email so I can contact you about the crash reports you send.</string>
|
||||
<string name="pref_acra_alwaysaccept">Automatically send crash reports</string>
|
||||
<string name="pref_acra_alwaysaccept_enabled">Will send crash reports automatically</string>
|
||||
<string name="pref_acra_alwaysaccept_disabled">Will ask everytime when sending crash reports.</string>
|
||||
<string name="pref_debug_crash_reports">Crash reports</string>
|
||||
<string name="pref_debug_debug_logs">Debug logging (these will be sent without a dialog)</string>
|
||||
<string name="acra_login">Enable logging</string>
|
||||
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
||||
<string name="unmark">Mark item as unread</string>
|
||||
<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="no_network_connectivity">Not connected !</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>
|
||||
</resources>
|
||||
|
@ -19,7 +19,6 @@
|
||||
<string name="action_disconnect">"Disconnect"</string>
|
||||
<string name="title_activity_settings">"Settings"</string>
|
||||
<string name="pref_header_general">"General"</string>
|
||||
<string name="pref_switch_actions_tap_title">"Tap action on the articles"</string>
|
||||
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
|
||||
<string name="add_source_hint_url">"Link"</string>
|
||||
<string name="add_source_hint_name">"Name"</string>
|
||||
@ -64,9 +63,6 @@
|
||||
<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="invitation_title">"Try this app for your Selfoss RSS feeds !"</string>
|
||||
<string name="invitation_message">"I use this app for my Selfoss RSS feeds. You may like it too !"</string>
|
||||
<string name="invitation_cta">"Try the app"</string>
|
||||
<string name="text_wrong_url">"You seem to be trying to use an invalid URL. Make sure it is correct, and if the problem persists, contact me (via the store contact link). Please note that the app needs you to be using Selfoss. You can't access RSS feeds without it."</string>
|
||||
<string name="pref_general_internal_browser_title">"Open links inside the app"</string>
|
||||
<string name="pref_general_internal_browser_on">"Articles will open inside the app"</string>
|
||||
@ -76,26 +72,12 @@
|
||||
<string name="prefer_article_viewer_off">"Will use the internal browser instead of the article viewer"</string>
|
||||
<string name="pref_general_category_links">"Link handling"</string>
|
||||
<string name="pref_general_category_displaying">"Displaying"</string>
|
||||
<string name="pref_general_category_actions">"Actions"</string>
|
||||
<string name="pref_switch_card_view_on">"The articles will be displayed as cards"</string>
|
||||
<string name="pref_switch_card_view_off">"The articles will be displayed as a list"</string>
|
||||
<string name="pref_switch_actions_tap_on">"Displays the action bar under the article"</string>
|
||||
<string name="pref_switch_actions_tap_off">"When selecting an article it will open in your selected browser"</string>
|
||||
<string name="menu_home_refresh">"Update remote"</string>
|
||||
<string name="refresh_success_response">"The remote is updated, you can now reload the articles list"</string>
|
||||
<string name="refresh_failer_message">"The update didn't work, try again later, or check your selfoss logs."</string>
|
||||
<string name="refresh_in_progress">"Refresh in progress"</string>
|
||||
<string name="new_apk_available_title">"A new APK is available."</string>
|
||||
<string name="new_apk_available_message">"A new APK is available to download on the official repository."</string>
|
||||
<string name="new_apk_available_get">"Download now"</string>
|
||||
<string name="new_apk_available_no">"Ignore version"</string>
|
||||
<string name="intro_hello_title">"Hi there !"</string>
|
||||
<string name="intro_hello_message">"Thanks for downloading the app !"</string>
|
||||
<string name="intro_needs_selfoss_title">"Before you start…"</string>
|
||||
<string name="intro_needs_selfoss_message">"You can't use the app without a Selfoss instance."</string>
|
||||
<string name="intro_needs_selfoss_link">"What is Selfoss ?"</string>
|
||||
<string name="intro_all_set_title">"All set !"</string>
|
||||
<string name="intro_all_set_message">"You are ready to use the app. Don't forget to go to the settings page to configure your app, and where you'll find some useful links."</string>
|
||||
<string name="card_height_title">Full height cards</string>
|
||||
<string name="card_height_on">Cards height will adjust to its content</string>
|
||||
<string name="card_height_off">Card height will be fixed</string>
|
||||
@ -108,7 +90,6 @@
|
||||
<string name="drawer_item_tags">Tags</string>
|
||||
<string name="drawer_item_sources">Sources</string>
|
||||
<string name="drawer_action_edit">edit</string>
|
||||
<string name="cache_drawer_error" tools:keep="@string/cache_drawer_error">Couldn\'t cache your drawer data</string>
|
||||
<string name="no_tags_loaded">No tags loaded</string>
|
||||
<string name="no_sources_loaded">No sources loaded</string>
|
||||
<string name="drawer_loading">Loading …</string>
|
||||
@ -127,6 +108,7 @@
|
||||
<string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string>
|
||||
<string name="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="read_debug_title">Read articles appearing as unread ?</string>
|
||||
<string name="read_debug_off">No log when marking an item as read</string>
|
||||
<string name="read_debug_on">Api calls will be logged when marking an article as read</string>
|
||||
@ -158,12 +140,32 @@
|
||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||
<string name="crash_dialog_text">Something went wrong. Please send the report to the developer.</string>
|
||||
<string name="crash_dialog_comment">You can add any helpful details in the comment bellow. Don\'t include any personal data in your comment. You could send me and email with your debug id, and I\'ll keep you posted when the issue is resolved.</string>
|
||||
<string name="pref_acra_user_email">Contact email</string>
|
||||
<string name="pref_acra_user_email_summary">Add an email so I can contact you about the crash reports you send.</string>
|
||||
<string name="pref_acra_alwaysaccept">Automatically send crash reports</string>
|
||||
<string name="pref_acra_alwaysaccept_enabled">Will send crash reports automatically</string>
|
||||
<string name="pref_acra_alwaysaccept_disabled">Will ask everytime when sending crash reports.</string>
|
||||
<string name="pref_debug_crash_reports">Crash reports</string>
|
||||
<string name="pref_debug_debug_logs">Debug logging (these will be sent without a dialog)</string>
|
||||
<string name="acra_login">Enable logging</string>
|
||||
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
||||
<string name="unmark">Mark item as unread</string>
|
||||
<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="no_network_connectivity">Not connected !</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>
|
||||
</resources>
|
||||
|
@ -11,7 +11,7 @@
|
||||
<string name="prompt_url">"URL"</string>
|
||||
<string name="withLoginSwitch">"Autenticació (si és necessària)"</string>
|
||||
<string name="withHttpLoginSwitch">"Autenticació HTTP (si és necessària)"</string>
|
||||
<string name="login_url_problem">"Ups. 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_http_login">"Nom d'usuari HTTP"</string>
|
||||
<string name="label_share">"Comparteix"</string>
|
||||
@ -19,17 +19,16 @@
|
||||
<string name="action_disconnect">"Desconnecta't"</string>
|
||||
<string name="title_activity_settings">"Configuració"</string>
|
||||
<string name="pref_header_general">"General"</string>
|
||||
<string name="pref_switch_actions_tap_title">"Toqueu l'acció en els articles"</string>
|
||||
<string name="add_source_hint_tags">"Etiqueta1, Etiqueta2, Etiqueta3"</string>
|
||||
<string name="add_source_hint_url">"Enllaça"</string>
|
||||
<string name="add_source_hint_url">"Enllaç"</string>
|
||||
<string name="add_source_hint_name">"Nom"</string>
|
||||
<string name="add_source">"Afegeix una font"</string>
|
||||
<string name="add_source_save">"Desa"</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_read">"S'han llegit totes les publicacions"</string>
|
||||
<string name="cant_get_favs">"No s'han pogut obtenir preferits"</string>
|
||||
<string name="cant_get_new_elements">"No s'han pogut obtenir articles nous"</string>
|
||||
<string name="cant_get_favs">"No es poden obtenir preferits"</string>
|
||||
<string name="cant_get_new_elements">"No es pot accedir als articles nous"</string>
|
||||
<string name="cant_get_read">"No es poden llegir els articles"</string>
|
||||
<string name="nothing_here">"No hi ha res"</string>
|
||||
<string name="tab_new">"Nou"</string>
|
||||
@ -41,20 +40,20 @@
|
||||
<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_create_source">"No es pot crear la font."</string>
|
||||
<string name="cant_get_spouts">"No s'ha pogut obtenir la llista de canals."</string>
|
||||
<string name="cant_get_spouts">"No es pot obtenir la llista de canals."</string>
|
||||
<string name="form_not_complete">"El formulari no està complet"</string>
|
||||
<string name="pref_header_links">"Enllaços"</string>
|
||||
<string name="issue_tracker_link">"Detector de problemes"</string>
|
||||
<string name="issue_tracker_summary">"Informa d'un error o pregunta sobre funcions noves"</string>
|
||||
<string name="warning_wrong_url">"ADVERTÈNCIA"</string>
|
||||
<string name="pref_switch_card_view_title">"Visualització de targeta"</string>
|
||||
<string name="cant_mark_favortie">"No s'ha pogut marcar l'article com a preferit"</string>
|
||||
<string name="cant_unmark_favortie">"No s'ha pogut eliminar l'element de preferits"</string>
|
||||
<string name="cant_mark_favortie">"No es pot marcar l'article com a preferit"</string>
|
||||
<string name="cant_unmark_favortie">"No es pot treure l'element de preferits"</string>
|
||||
<string name="share">"Comparteix"</string>
|
||||
<string name="rating_prompt_title">"Us agrada l'aplicació?"</string>
|
||||
<string name="rating_prompt_yes">"Sí."</string>
|
||||
<string name="rating_prompt_no">"No gaire…"</string>
|
||||
<string name="rating_prompt_feedback_title">"Ens podeu dir per què"</string>
|
||||
<string name="rating_prompt_feedback_title">"Ens podeu dir per què?"</string>
|
||||
<string name="rating_prompt_feedback_yes">"D'acord."</string>
|
||||
<string name="rating_prompt_feedback_no">"Ara no."</string>
|
||||
<string name="rating_prompt_rating_title">"Perfecte! Ens podeu puntuar a la Botiga?"</string>
|
||||
@ -62,11 +61,8 @@
|
||||
<string name="rating_prompt_rating_no">"Ara no."</string>
|
||||
<string name="rating_prompt_thanks">"Gràcies. La vostra opinió ens ajuda a millorar l'aplicació."</string>
|
||||
<string name="switch_unread_count">"Mostra el recompte d'articles no llegits amb un distintiu a la barra inferior."</string>
|
||||
<string name="switch_unread_count_title">"Mostra el recompte d'articles no llegits"</string>
|
||||
<string name="display_all_counts_title">"Mostra el recompte d'articles llegits"</string>
|
||||
<string name="invitation_title">"Prova aquesta aplicació per als canals RSS de Selfoss."</string>
|
||||
<string name="invitation_message">"Jo utilitzo aquesta aplicació per llegir els canals RSS de Selfoss. Segur que a tu també t'agrada!"</string>
|
||||
<string name="invitation_cta">"Prova l'aplicació"</string>
|
||||
<string name="switch_unread_count_title">"Recompte d'articles no llegits"</string>
|
||||
<string name="display_all_counts_title">"Recompte d'articles llegits i preferits"</string>
|
||||
<string name="text_wrong_url">"Sembla que esteu utilitzant un URL no vàlid. Assegureu-vos que és correcte, i si el problema persisteix, poseu-vos en contacte amb mi (a través de l'enllaç de contacte que hi ha a la Botiga). Tingueu en compte que per utilitzar aquesta aplicació cal que també utilitzeu Selfoss. Si no, no podreu accedir a canals RSS."</string>
|
||||
<string name="pref_general_internal_browser_title">"Obre els enllaços dins de l'aplicació"</string>
|
||||
<string name="pref_general_internal_browser_on">"Els articles s'obriran dins de l'aplicació"</string>
|
||||
@ -75,32 +71,18 @@
|
||||
<string name="prefer_article_viewer_on">"S'obrirà el visualitzador d'articles en lloc del navegador intern"</string>
|
||||
<string name="prefer_article_viewer_off">"S'obrirà el navegador intern en lloc del visualitzador d'articles"</string>
|
||||
<string name="pref_general_category_links">"Gestió d'enllaços"</string>
|
||||
<string name="pref_general_category_displaying">"S'està mostrant"</string>
|
||||
<string name="pref_general_category_actions">"Accions"</string>
|
||||
<string name="pref_general_category_displaying">"Visualització"</string>
|
||||
<string name="pref_switch_card_view_on">"Els articles es mostraran com a targetes"</string>
|
||||
<string name="pref_switch_card_view_off">"Els articles es mostraran en forma de llista"</string>
|
||||
<string name="pref_switch_actions_tap_on">"Mostra la barra d'acció sota l'article"</string>
|
||||
<string name="pref_switch_actions_tap_off">"Quan seleccioneu un article, s'obrirà al navegador seleccionat"</string>
|
||||
<string name="menu_home_refresh">"Actualitza l'accés remot"</string>
|
||||
<string name="refresh_success_response">"Un cop s'hagi actualitzat, podeu tornar a carregar la llista d'articles"</string>
|
||||
<string name="refresh_failer_message">"L'actualització no ha funcionat. Torneu a provar-ho més tard o consulteu els registres de Selfoss"</string>
|
||||
<string name="refresh_success_response">"S'ha actualitzat el remot. Torneu a carregar la llista d'articles"</string>
|
||||
<string name="refresh_failer_message">"L'actualització no ha funcionat. Torneu a provar-ho més tard o consulteu els registres de Selfoss."</string>
|
||||
<string name="refresh_in_progress">"S'està actualitzant"</string>
|
||||
<string name="new_apk_available_title">"Hi ha una nou APK disponible."</string>
|
||||
<string name="new_apk_available_message">"Podeu baixar l'APK nou des del dipòsit oficial."</string>
|
||||
<string name="new_apk_available_get">"Baixa-ho ara"</string>
|
||||
<string name="new_apk_available_no">"Ignora la versió"</string>
|
||||
<string name="intro_hello_title">"Hola"</string>
|
||||
<string name="intro_hello_message">"Gràcies per baixar l'aplicació!"</string>
|
||||
<string name="intro_needs_selfoss_title">"Abans de començar…"</string>
|
||||
<string name="intro_needs_selfoss_message">"No podeu utilitzar l'aplicació sense una instància de Selfoss."</string>
|
||||
<string name="intro_needs_selfoss_link">"Què és Selfoss?"</string>
|
||||
<string name="intro_all_set_title">"Tot a punt!"</string>
|
||||
<string name="intro_all_set_message">"Ja podeu començar a utilitzar l'aplicació. Configureu l'aplicació des de la pàgina Configuració. Allà hi trobareu enllaços que us poden ser útils."</string>
|
||||
<string name="card_height_title">Alçada completa de les targetes</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="source_code">Codi font</string>
|
||||
<string name="cant_mark_read">Marca aquest article com a llegit</string>
|
||||
<string name="cant_mark_read">No es pot marcar l\'article com a llegit</string>
|
||||
<string name="drawer_error_loading_tags">S\'ha produït un error en carregar les etiquetes</string>
|
||||
<string name="drawer_error_loading_sources">S\'ha produït un error en carregar les fonts</string>
|
||||
<string name="drawer_item_filters">Filtres</string>
|
||||
@ -108,7 +90,6 @@
|
||||
<string name="drawer_item_tags">Etiquetes</string>
|
||||
<string name="drawer_item_sources">Fonts</string>
|
||||
<string name="drawer_action_edit">Edita</string>
|
||||
<string name="cache_drawer_error" tools:keep="@string/cache_drawer_error">La informació del calaix no s\'ha pogut emmagatzemar a la memòria cau</string>
|
||||
<string name="no_tags_loaded">No s\'ha carregat cap etiqueta</string>
|
||||
<string name="no_sources_loaded">No s\'ha carregat cap font</string>
|
||||
<string name="drawer_loading">S\'està carregant…</string>
|
||||
@ -118,26 +99,27 @@
|
||||
<string name="pref_header_theme">Temes</string>
|
||||
<string name="default_theme">Predeterminat</string>
|
||||
<string name="default_dark_theme">Predeterminat/Fosc</string>
|
||||
<string name="pref_header_debug">Depura</string>
|
||||
<string name="login_debug_title">Activeu-ho per registrar els errors d\'inici de sessió </string>
|
||||
<string name="pref_header_debug">Depuració</string>
|
||||
<string name="login_debug_title">Registra els errors d\'inici de sessió</string>
|
||||
<string name="login_debug_on">Es registraran tots els errors que es produeixin a la pàgina d\'inici de sessió</string>
|
||||
<string name="login_debug_off">No hi ha cap registre a la pàgina d\'inici de sessió</string>
|
||||
<string name="login_menu_debug">Depura</string>
|
||||
<string name="login_debug_off">No es registrarà cap error que es produeixi a la pàgina d\'inici de sessió</string>
|
||||
<string name="login_menu_debug">Depuració</string>
|
||||
<string name="self_hosted_cert_switch">Utilitzeu un certificat autoallotjat?</string>
|
||||
<string name="self_signed_cert_warning">Per raons de seguretat, els certificats autosignats no seran compatibles per defecte. En activar aquesta opció, sereu responsable de qualsevol problema de seguretat que es pugui produir.</string>
|
||||
<string name="pref_selfoss_category">API de Selfoss</string>
|
||||
<string name="pref_api_items_number_title">Nombre d\'elements carregats</string>
|
||||
<string name="pref_hidden_tags">Hidden Tags</string>
|
||||
<string name="read_debug_title">Voleu llegir els articles que apareixen com a no llegits?</string>
|
||||
<string name="read_debug_off">No creïs un registre quan es marquen elements com a llegits</string>
|
||||
<string name="read_debug_off">No es registraran quan es marquen elements com a llegits</string>
|
||||
<string name="read_debug_on">Les crides de l\'API es registraran en marcar un article com a llegit</string>
|
||||
<string name="summary_debug_identifier">Identificador de depuració</string>
|
||||
<string name="unique_id_to_clipboard">S\'ha copiat l\'identificador al porta-retalls</string>
|
||||
<string name="display_header_drawer_summary">Mostra una capçalera amb la instància URL de Selfoss al panell lateral.</string>
|
||||
<string name="display_header_drawer_title">Capçalera de menú</string>
|
||||
<string name="login_everything_title">S\'estan registrant totes les crides de l\'API</string>
|
||||
<string name="login_everything_title">Registra totes les crides de l\'API</string>
|
||||
<string name="login_everything_on">Aquesta acció registrarà totes les crides de l\'API per als programadors.</string>
|
||||
<string name="login_everything_off">No es registrarà cap crida de l\'API</string>
|
||||
<string name="pref_general_infinite_loading_title">Carrega més articles en desplaçar cap avall</string>
|
||||
<string name="pref_general_infinite_loading_title">Carrega articles en desplaçar</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="drawer_report_bug">Informa d\'un error</string>
|
||||
@ -145,25 +127,45 @@
|
||||
<string name="reader_action_more">Més informació</string>
|
||||
<string name="reader_action_open">Obre al navegador</string>
|
||||
<string name="reader_action_share">Comparteix</string>
|
||||
<string name="pref_switch_actions_pager_scroll_on">Marca l\'article com a llegit en fer 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>
|
||||
<string name="add_to_favs_reader">Afegeix als preferits</string>
|
||||
<string name="remove_to_favs_reader">Suprimeix dels preferits</string>
|
||||
<string name="pref_content_reader_font_size">Mida de la lletra del contingut del lector d\'articles</string>
|
||||
<string name="pref_content_reader_font_size">Mida de la lletra del lector d’articles</string>
|
||||
<string name="pref_header_viewer">Visualitzador d\'articles</string>
|
||||
<string name="refresh_dialog_message">Aquesta acció actualitzarà la vostra instància de Selfoss.</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 fer lliscar el dit d\'un article a l\'altre</string>
|
||||
<string name="pref_switch_actions_pager_scroll_off">No marquis els articles com a llegits en fer lliscar el dit d\'un article a l\'altre.</string>
|
||||
<string name="gdpr_dialog_message">The app does not collect any personal data. Every analytics tools were removed. Crash reports sending is now optional, as is the debug logging. Keep in mind that debugging and crash reports are essential for the app development (You can configure everything in Settings > Debug).</string>
|
||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||
<string name="crash_dialog_text">Something went wrong. Please send the report to the developer.</string>
|
||||
<string name="crash_dialog_comment">You can add any helpful details in the comment bellow. Don\'t include any personal data in your comment. You could send me and email with your debug id, and I\'ll keep you posted when the issue is resolved.</string>
|
||||
<string name="pref_acra_user_email">Contact email</string>
|
||||
<string name="pref_acra_user_email_summary">Add an email so I can contact you about the crash reports you send.</string>
|
||||
<string name="pref_acra_alwaysaccept">Automatically send crash reports</string>
|
||||
<string name="pref_acra_alwaysaccept_enabled">Will send crash reports automatically</string>
|
||||
<string name="pref_acra_alwaysaccept_disabled">Will ask everytime when sending crash reports.</string>
|
||||
<string name="pref_debug_crash_reports">Crash reports</string>
|
||||
<string name="pref_debug_debug_logs">Debug logging (these will be sent without a dialog)</string>
|
||||
<string name="acra_login">Enable logging</string>
|
||||
<string name="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="gdpr_dialog_message">Aquesta aplicació no recull cap dada personal. S\'han suprimit totes les eines d\'anàlisi. A partir d\'ara, l\'enviament d\'informes és opcional, així com el registre de depuració d\'errors. Recordeu que la depuració i els informes d\'error són essencials per al desenvolupament de l\'aplicació (Ho podeu configurar tot a Configuració > Depura).</string>
|
||||
<string name="gdpr_dialog_title">Aquesta aplicació no comparteix cap dada personal vostra.</string>
|
||||
<string name="crash_dialog_text">Alguna cosa ha anat malament. Envieu l\'informe al desenvolupador.</string>
|
||||
<string name="crash_dialog_comment">Podeu afegir informació útil en la secció de comentaris. No incloeu cap dada personal en el vostre comentari. També em podeu enviar un correu electrònic amb l\'identificador de depuració i us ho faré saber quan el problema s\'hagi resolt.</string>
|
||||
<string name="pref_acra_alwaysaccept">Envia informes d\'error automàtics</string>
|
||||
<string name="pref_acra_alwaysaccept_enabled">S\'enviaran informes d\'error automàticament</string>
|
||||
<string name="pref_acra_alwaysaccept_disabled">Us preguntarem abans d\'enviar un informe d\'error.</string>
|
||||
<string name="pref_debug_crash_reports">Informes d\'error</string>
|
||||
<string name="pref_debug_debug_logs">Registre de depuració (s\'enviarà automàticament)</string>
|
||||
<string name="acra_login">Habilita el registre</string>
|
||||
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
||||
<string name="unmark">Mark item as unread</string>
|
||||
<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="no_network_connectivity">Not connected !</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>
|
||||
</resources>
|
||||
|
@ -19,7 +19,6 @@
|
||||
<string name="action_disconnect">"Disconnect"</string>
|
||||
<string name="title_activity_settings">"Settings"</string>
|
||||
<string name="pref_header_general">"General"</string>
|
||||
<string name="pref_switch_actions_tap_title">"Tap action on the articles"</string>
|
||||
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
|
||||
<string name="add_source_hint_url">"Link"</string>
|
||||
<string name="add_source_hint_name">"Name"</string>
|
||||
@ -64,9 +63,6 @@
|
||||
<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="invitation_title">"Try this app for your Selfoss RSS feeds !"</string>
|
||||
<string name="invitation_message">"I use this app for my Selfoss RSS feeds. You may like it too !"</string>
|
||||
<string name="invitation_cta">"Try the app"</string>
|
||||
<string name="text_wrong_url">"You seem to be trying to use an invalid URL. Make sure it is correct, and if the problem persists, contact me (via the store contact link). Please note that the app needs you to be using Selfoss. You can't access RSS feeds without it."</string>
|
||||
<string name="pref_general_internal_browser_title">"Open links inside the app"</string>
|
||||
<string name="pref_general_internal_browser_on">"Articles will open inside the app"</string>
|
||||
@ -76,26 +72,12 @@
|
||||
<string name="prefer_article_viewer_off">"Will use the internal browser instead of the article viewer"</string>
|
||||
<string name="pref_general_category_links">"Link handling"</string>
|
||||
<string name="pref_general_category_displaying">"Displaying"</string>
|
||||
<string name="pref_general_category_actions">"Actions"</string>
|
||||
<string name="pref_switch_card_view_on">"The articles will be displayed as cards"</string>
|
||||
<string name="pref_switch_card_view_off">"The articles will be displayed as a list"</string>
|
||||
<string name="pref_switch_actions_tap_on">"Displays the action bar under the article"</string>
|
||||
<string name="pref_switch_actions_tap_off">"When selecting an article it will open in your selected browser"</string>
|
||||
<string name="menu_home_refresh">"Update remote"</string>
|
||||
<string name="refresh_success_response">"The remote is updated, you can now reload the articles list"</string>
|
||||
<string name="refresh_failer_message">"The update didn't work, try again later, or check your selfoss logs."</string>
|
||||
<string name="refresh_in_progress">"Refresh in progress"</string>
|
||||
<string name="new_apk_available_title">"A new APK is available."</string>
|
||||
<string name="new_apk_available_message">"A new APK is available to download on the official repository."</string>
|
||||
<string name="new_apk_available_get">"Download now"</string>
|
||||
<string name="new_apk_available_no">"Ignore version"</string>
|
||||
<string name="intro_hello_title">"Hi there !"</string>
|
||||
<string name="intro_hello_message">"Thanks for downloading the app !"</string>
|
||||
<string name="intro_needs_selfoss_title">"Before you start…"</string>
|
||||
<string name="intro_needs_selfoss_message">"You can't use the app without a Selfoss instance."</string>
|
||||
<string name="intro_needs_selfoss_link">"What is Selfoss ?"</string>
|
||||
<string name="intro_all_set_title">"All set !"</string>
|
||||
<string name="intro_all_set_message">"You are ready to use the app. Don't forget to go to the settings page to configure your app, and where you'll find some useful links."</string>
|
||||
<string name="card_height_title">Full height cards</string>
|
||||
<string name="card_height_on">Cards height will adjust to its content</string>
|
||||
<string name="card_height_off">Card height will be fixed</string>
|
||||
@ -108,7 +90,6 @@
|
||||
<string name="drawer_item_tags">Tags</string>
|
||||
<string name="drawer_item_sources">Sources</string>
|
||||
<string name="drawer_action_edit">edit</string>
|
||||
<string name="cache_drawer_error" tools:keep="@string/cache_drawer_error">Couldn\'t cache your drawer data</string>
|
||||
<string name="no_tags_loaded">No tags loaded</string>
|
||||
<string name="no_sources_loaded">No sources loaded</string>
|
||||
<string name="drawer_loading">Loading …</string>
|
||||
@ -127,6 +108,7 @@
|
||||
<string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string>
|
||||
<string name="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="read_debug_title">Read articles appearing as unread ?</string>
|
||||
<string name="read_debug_off">No log when marking an item as read</string>
|
||||
<string name="read_debug_on">Api calls will be logged when marking an article as read</string>
|
||||
@ -158,12 +140,32 @@
|
||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||
<string name="crash_dialog_text">Something went wrong. Please send the report to the developer.</string>
|
||||
<string name="crash_dialog_comment">You can add any helpful details in the comment bellow. Don\'t include any personal data in your comment. You could send me and email with your debug id, and I\'ll keep you posted when the issue is resolved.</string>
|
||||
<string name="pref_acra_user_email">Contact email</string>
|
||||
<string name="pref_acra_user_email_summary">Add an email so I can contact you about the crash reports you send.</string>
|
||||
<string name="pref_acra_alwaysaccept">Automatically send crash reports</string>
|
||||
<string name="pref_acra_alwaysaccept_enabled">Will send crash reports automatically</string>
|
||||
<string name="pref_acra_alwaysaccept_disabled">Will ask everytime when sending crash reports.</string>
|
||||
<string name="pref_debug_crash_reports">Crash reports</string>
|
||||
<string name="pref_debug_debug_logs">Debug logging (these will be sent without a dialog)</string>
|
||||
<string name="acra_login">Enable logging</string>
|
||||
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
||||
<string name="unmark">Mark item as unread</string>
|
||||
<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="no_network_connectivity">Not connected !</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>
|
||||
</resources>
|
||||
|
@ -19,7 +19,6 @@
|
||||
<string name="action_disconnect">"Disconnect"</string>
|
||||
<string name="title_activity_settings">"Settings"</string>
|
||||
<string name="pref_header_general">"General"</string>
|
||||
<string name="pref_switch_actions_tap_title">"Tap action on the articles"</string>
|
||||
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
|
||||
<string name="add_source_hint_url">"Link"</string>
|
||||
<string name="add_source_hint_name">"Name"</string>
|
||||
@ -64,9 +63,6 @@
|
||||
<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="invitation_title">"Try this app for your Selfoss RSS feeds !"</string>
|
||||
<string name="invitation_message">"I use this app for my Selfoss RSS feeds. You may like it too !"</string>
|
||||
<string name="invitation_cta">"Try the app"</string>
|
||||
<string name="text_wrong_url">"You seem to be trying to use an invalid URL. Make sure it is correct, and if the problem persists, contact me (via the store contact link). Please note that the app needs you to be using Selfoss. You can't access RSS feeds without it."</string>
|
||||
<string name="pref_general_internal_browser_title">"Open links inside the app"</string>
|
||||
<string name="pref_general_internal_browser_on">"Articles will open inside the app"</string>
|
||||
@ -76,26 +72,12 @@
|
||||
<string name="prefer_article_viewer_off">"Will use the internal browser instead of the article viewer"</string>
|
||||
<string name="pref_general_category_links">"Link handling"</string>
|
||||
<string name="pref_general_category_displaying">"Displaying"</string>
|
||||
<string name="pref_general_category_actions">"Actions"</string>
|
||||
<string name="pref_switch_card_view_on">"The articles will be displayed as cards"</string>
|
||||
<string name="pref_switch_card_view_off">"The articles will be displayed as a list"</string>
|
||||
<string name="pref_switch_actions_tap_on">"Displays the action bar under the article"</string>
|
||||
<string name="pref_switch_actions_tap_off">"When selecting an article it will open in your selected browser"</string>
|
||||
<string name="menu_home_refresh">"Update remote"</string>
|
||||
<string name="refresh_success_response">"The remote is updated, you can now reload the articles list"</string>
|
||||
<string name="refresh_failer_message">"The update didn't work, try again later, or check your selfoss logs."</string>
|
||||
<string name="refresh_in_progress">"Refresh in progress"</string>
|
||||
<string name="new_apk_available_title">"A new APK is available."</string>
|
||||
<string name="new_apk_available_message">"A new APK is available to download on the official repository."</string>
|
||||
<string name="new_apk_available_get">"Download now"</string>
|
||||
<string name="new_apk_available_no">"Ignore version"</string>
|
||||
<string name="intro_hello_title">"Hi there !"</string>
|
||||
<string name="intro_hello_message">"Thanks for downloading the app !"</string>
|
||||
<string name="intro_needs_selfoss_title">"Before you start…"</string>
|
||||
<string name="intro_needs_selfoss_message">"You can't use the app without a Selfoss instance."</string>
|
||||
<string name="intro_needs_selfoss_link">"What is Selfoss ?"</string>
|
||||
<string name="intro_all_set_title">"All set !"</string>
|
||||
<string name="intro_all_set_message">"You are ready to use the app. Don't forget to go to the settings page to configure your app, and where you'll find some useful links."</string>
|
||||
<string name="card_height_title">Full height cards</string>
|
||||
<string name="card_height_on">Cards height will adjust to its content</string>
|
||||
<string name="card_height_off">Card height will be fixed</string>
|
||||
@ -108,7 +90,6 @@
|
||||
<string name="drawer_item_tags">Tags</string>
|
||||
<string name="drawer_item_sources">Sources</string>
|
||||
<string name="drawer_action_edit">edit</string>
|
||||
<string name="cache_drawer_error" tools:keep="@string/cache_drawer_error">Couldn\'t cache your drawer data</string>
|
||||
<string name="no_tags_loaded">No tags loaded</string>
|
||||
<string name="no_sources_loaded">No sources loaded</string>
|
||||
<string name="drawer_loading">Loading …</string>
|
||||
@ -127,6 +108,7 @@
|
||||
<string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string>
|
||||
<string name="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="read_debug_title">Read articles appearing as unread ?</string>
|
||||
<string name="read_debug_off">No log when marking an item as read</string>
|
||||
<string name="read_debug_on">Api calls will be logged when marking an article as read</string>
|
||||
@ -158,12 +140,32 @@
|
||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||
<string name="crash_dialog_text">Something went wrong. Please send the report to the developer.</string>
|
||||
<string name="crash_dialog_comment">You can add any helpful details in the comment bellow. Don\'t include any personal data in your comment. You could send me and email with your debug id, and I\'ll keep you posted when the issue is resolved.</string>
|
||||
<string name="pref_acra_user_email">Contact email</string>
|
||||
<string name="pref_acra_user_email_summary">Add an email so I can contact you about the crash reports you send.</string>
|
||||
<string name="pref_acra_alwaysaccept">Automatically send crash reports</string>
|
||||
<string name="pref_acra_alwaysaccept_enabled">Will send crash reports automatically</string>
|
||||
<string name="pref_acra_alwaysaccept_disabled">Will ask everytime when sending crash reports.</string>
|
||||
<string name="pref_debug_crash_reports">Crash reports</string>
|
||||
<string name="pref_debug_debug_logs">Debug logging (these will be sent without a dialog)</string>
|
||||
<string name="acra_login">Enable logging</string>
|
||||
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
||||
<string name="unmark">Mark item as unread</string>
|
||||
<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="no_network_connectivity">Not connected !</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>
|
||||
</resources>
|
||||
|
@ -19,7 +19,6 @@
|
||||
<string name="action_disconnect">"Verbindung trennen"</string>
|
||||
<string name="title_activity_settings">"Einstellungen"</string>
|
||||
<string name="pref_header_general">"Allgemein"</string>
|
||||
<string name="pref_switch_actions_tap_title">"Tap action on the articles"</string>
|
||||
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
|
||||
<string name="add_source_hint_url">"Link"</string>
|
||||
<string name="add_source_hint_name">"Name"</string>
|
||||
@ -64,9 +63,6 @@
|
||||
<string name="switch_unread_count">"Zeige die Zahl ungelesener Artikel in der unteren Leiste."</string>
|
||||
<string name="switch_unread_count_title">"Zeige Anzahl ungelesener Artikel"</string>
|
||||
<string name="display_all_counts_title">"Zeige Anzahl der Favoriten und gelesenen Artikel"</string>
|
||||
<string name="invitation_title">"Probiere diese App für deine Selfoss RSS-Feeds!"</string>
|
||||
<string name="invitation_message">"Ich benutze diese App für meine Selfoss RSS-Feeds. Vielleicht magst du sie auch!"</string>
|
||||
<string name="invitation_cta">"Probier die App"</string>
|
||||
<string name="text_wrong_url">"Sie scheinen eine ungültige URL verwenden. Stellen Sie sicher, dass die URL richtig ist. Sollte das Problem weiterhin bestehen kontaktieren Sie mich (über den Playstore-Kontakt-Link). Bitte beachten Sie, dass Sie Selfoss benötigen um RSS-Feeds zu lesen."</string>
|
||||
<string name="pref_general_internal_browser_title">"Öffne Links innerhalb der App"</string>
|
||||
<string name="pref_general_internal_browser_on">"Artikel werden innerhalb der App geöffnet"</string>
|
||||
@ -76,26 +72,12 @@
|
||||
<string name="prefer_article_viewer_off">"Der internen Browser wird anstelle des Artikel-Viewer verwendet"</string>
|
||||
<string name="pref_general_category_links">"Umgang mit Links"</string>
|
||||
<string name="pref_general_category_displaying">"Ansicht"</string>
|
||||
<string name="pref_general_category_actions">"Aktionen"</string>
|
||||
<string name="pref_switch_card_view_on">"Artikel werden als Kacheln angezeigt"</string>
|
||||
<string name="pref_switch_card_view_off">"Artikel werden als Liste angezeigt"</string>
|
||||
<string name="pref_switch_actions_tap_on">"Zeigt die Aktionsleiste unter dem Artikel"</string>
|
||||
<string name="pref_switch_actions_tap_off">"Bei der Auswahl eines Artikels wird dieser im ausgewählten Browser geöffnet"</string>
|
||||
<string name="menu_home_refresh">"Remote-Aktualisierung"</string>
|
||||
<string name="refresh_success_response">"Selfoss wird aktualisiert, du kannst jetzt die Artikel laden"</string>
|
||||
<string name="refresh_failer_message">"Das Update hat nicht funktioniert, versuche es erneut oder überprüfe die Protokolle von Selfoss."</string>
|
||||
<string name="refresh_in_progress">"Aktualisierung läuft"</string>
|
||||
<string name="new_apk_available_title">"Eine neue Version ist verfügbar."</string>
|
||||
<string name="new_apk_available_message">"Eine neue APK steht im offiziellen Repository zur Verfügung."</string>
|
||||
<string name="new_apk_available_get">"Jetzt herunterladen"</string>
|
||||
<string name="new_apk_available_no">"Version ignorieren"</string>
|
||||
<string name="intro_hello_title">"Hallo!"</string>
|
||||
<string name="intro_hello_message">"Danke fürs Herunterladen der App!"</string>
|
||||
<string name="intro_needs_selfoss_title">"Bevor du beginnst…"</string>
|
||||
<string name="intro_needs_selfoss_message">"Die App kann nicht ohne Selfoss-Instanz benutzt werden."</string>
|
||||
<string name="intro_needs_selfoss_link">"Was ist Selfoss?"</string>
|
||||
<string name="intro_all_set_title">"Fertig!"</string>
|
||||
<string name="intro_all_set_message">"Sie können die App jetzt verwenden. Vergiss nicht deine App unter \"Einstellungen\" zu konfigurieren. Dort findest du auch einige nützliche Links."</string>
|
||||
<string name="card_height_title">Maximale Kartenhöhe</string>
|
||||
<string name="card_height_on">Kartenhöhe passt sich Inhalt an</string>
|
||||
<string name="card_height_off">Kartenhöhe ist fix</string>
|
||||
@ -108,7 +90,6 @@
|
||||
<string name="drawer_item_tags">Tags</string>
|
||||
<string name="drawer_item_sources">Quellen</string>
|
||||
<string name="drawer_action_edit">bearbeiten</string>
|
||||
<string name="cache_drawer_error" tools:keep="@string/cache_drawer_error">Couldn\'t cache your drawer data</string>
|
||||
<string name="no_tags_loaded">No tags loaded</string>
|
||||
<string name="no_sources_loaded">Keine Quellen geladen</string>
|
||||
<string name="drawer_loading">Lade…</string>
|
||||
@ -127,6 +108,7 @@
|
||||
<string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string>
|
||||
<string name="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="read_debug_title">Read articles appearing as unread ?</string>
|
||||
<string name="read_debug_off">No log when marking an item as read</string>
|
||||
<string name="read_debug_on">Api calls will be logged when marking an article as read</string>
|
||||
@ -158,12 +140,32 @@
|
||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||
<string name="crash_dialog_text">Something went wrong. Please send the report to the developer.</string>
|
||||
<string name="crash_dialog_comment">You can add any helpful details in the comment bellow. Don\'t include any personal data in your comment. You could send me and email with your debug id, and I\'ll keep you posted when the issue is resolved.</string>
|
||||
<string name="pref_acra_user_email">Contact email</string>
|
||||
<string name="pref_acra_user_email_summary">Add an email so I can contact you about the crash reports you send.</string>
|
||||
<string name="pref_acra_alwaysaccept">Automatically send crash reports</string>
|
||||
<string name="pref_acra_alwaysaccept_enabled">Will send crash reports automatically</string>
|
||||
<string name="pref_acra_alwaysaccept_disabled">Will ask everytime when sending crash reports.</string>
|
||||
<string name="pref_debug_crash_reports">Crash reports</string>
|
||||
<string name="pref_debug_debug_logs">Debug logging (these will be sent without a dialog)</string>
|
||||
<string name="acra_login">Enable logging</string>
|
||||
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
||||
<string name="unmark">Mark item as unread</string>
|
||||
<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="no_network_connectivity">Not connected !</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>
|
||||
</resources>
|
||||
|
@ -19,7 +19,6 @@
|
||||
<string name="action_disconnect">"Disconnect"</string>
|
||||
<string name="title_activity_settings">"Settings"</string>
|
||||
<string name="pref_header_general">"General"</string>
|
||||
<string name="pref_switch_actions_tap_title">"Tap action on the articles"</string>
|
||||
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
|
||||
<string name="add_source_hint_url">"Link"</string>
|
||||
<string name="add_source_hint_name">"Name"</string>
|
||||
@ -64,9 +63,6 @@
|
||||
<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="invitation_title">"Try this app for your Selfoss RSS feeds !"</string>
|
||||
<string name="invitation_message">"I use this app for my Selfoss RSS feeds. You may like it too !"</string>
|
||||
<string name="invitation_cta">"Try the app"</string>
|
||||
<string name="text_wrong_url">"You seem to be trying to use an invalid URL. Make sure it is correct, and if the problem persists, contact me (via the store contact link). Please note that the app needs you to be using Selfoss. You can't access RSS feeds without it."</string>
|
||||
<string name="pref_general_internal_browser_title">"Open links inside the app"</string>
|
||||
<string name="pref_general_internal_browser_on">"Articles will open inside the app"</string>
|
||||
@ -76,26 +72,12 @@
|
||||
<string name="prefer_article_viewer_off">"Will use the internal browser instead of the article viewer"</string>
|
||||
<string name="pref_general_category_links">"Link handling"</string>
|
||||
<string name="pref_general_category_displaying">"Displaying"</string>
|
||||
<string name="pref_general_category_actions">"Actions"</string>
|
||||
<string name="pref_switch_card_view_on">"The articles will be displayed as cards"</string>
|
||||
<string name="pref_switch_card_view_off">"The articles will be displayed as a list"</string>
|
||||
<string name="pref_switch_actions_tap_on">"Displays the action bar under the article"</string>
|
||||
<string name="pref_switch_actions_tap_off">"When selecting an article it will open in your selected browser"</string>
|
||||
<string name="menu_home_refresh">"Update remote"</string>
|
||||
<string name="refresh_success_response">"The remote is updated, you can now reload the articles list"</string>
|
||||
<string name="refresh_failer_message">"The update didn't work, try again later, or check your selfoss logs."</string>
|
||||
<string name="refresh_in_progress">"Refresh in progress"</string>
|
||||
<string name="new_apk_available_title">"A new APK is available."</string>
|
||||
<string name="new_apk_available_message">"A new APK is available to download on the official repository."</string>
|
||||
<string name="new_apk_available_get">"Download now"</string>
|
||||
<string name="new_apk_available_no">"Ignore version"</string>
|
||||
<string name="intro_hello_title">"Hi there !"</string>
|
||||
<string name="intro_hello_message">"Thanks for downloading the app !"</string>
|
||||
<string name="intro_needs_selfoss_title">"Before you start…"</string>
|
||||
<string name="intro_needs_selfoss_message">"You can't use the app without a Selfoss instance."</string>
|
||||
<string name="intro_needs_selfoss_link">"What is Selfoss ?"</string>
|
||||
<string name="intro_all_set_title">"All set !"</string>
|
||||
<string name="intro_all_set_message">"You are ready to use the app. Don't forget to go to the settings page to configure your app, and where you'll find some useful links."</string>
|
||||
<string name="card_height_title">Full height cards</string>
|
||||
<string name="card_height_on">Cards height will adjust to its content</string>
|
||||
<string name="card_height_off">Card height will be fixed</string>
|
||||
@ -108,7 +90,6 @@
|
||||
<string name="drawer_item_tags">Tags</string>
|
||||
<string name="drawer_item_sources">Sources</string>
|
||||
<string name="drawer_action_edit">edit</string>
|
||||
<string name="cache_drawer_error" tools:keep="@string/cache_drawer_error">Couldn\'t cache your drawer data</string>
|
||||
<string name="no_tags_loaded">No tags loaded</string>
|
||||
<string name="no_sources_loaded">No sources loaded</string>
|
||||
<string name="drawer_loading">Loading …</string>
|
||||
@ -127,6 +108,7 @@
|
||||
<string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string>
|
||||
<string name="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="read_debug_title">Read articles appearing as unread ?</string>
|
||||
<string name="read_debug_off">No log when marking an item as read</string>
|
||||
<string name="read_debug_on">Api calls will be logged when marking an article as read</string>
|
||||
@ -158,12 +140,32 @@
|
||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||
<string name="crash_dialog_text">Something went wrong. Please send the report to the developer.</string>
|
||||
<string name="crash_dialog_comment">You can add any helpful details in the comment bellow. Don\'t include any personal data in your comment. You could send me and email with your debug id, and I\'ll keep you posted when the issue is resolved.</string>
|
||||
<string name="pref_acra_user_email">Contact email</string>
|
||||
<string name="pref_acra_user_email_summary">Add an email so I can contact you about the crash reports you send.</string>
|
||||
<string name="pref_acra_alwaysaccept">Automatically send crash reports</string>
|
||||
<string name="pref_acra_alwaysaccept_enabled">Will send crash reports automatically</string>
|
||||
<string name="pref_acra_alwaysaccept_disabled">Will ask everytime when sending crash reports.</string>
|
||||
<string name="pref_debug_crash_reports">Crash reports</string>
|
||||
<string name="pref_debug_debug_logs">Debug logging (these will be sent without a dialog)</string>
|
||||
<string name="acra_login">Enable logging</string>
|
||||
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
||||
<string name="unmark">Mark item as unread</string>
|
||||
<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="no_network_connectivity">Not connected !</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>
|
||||
</resources>
|
||||
|