Compare commits
172 Commits
v161809264
...
v171812343
Author | SHA1 | Date | |
---|---|---|---|
9eac51e729 | |||
fa9cce6783 | |||
f0d4b63a97 | |||
83eeb11388 | |||
01f746f33d | |||
200851894b | |||
862e5cf4ab | |||
0b07f2a407 | |||
9ba6feef0b | |||
63a0638522 | |||
f9a4e6e363 | |||
6b40fd4bdc | |||
04c7776466 | |||
92c335b4e1 | |||
17251e576b | |||
62ea782429 | |||
f99474e3c1 | |||
57ac8f428f | |||
9cc1adbf15 | |||
1d9a440ae7 | |||
511553806c | |||
87e7d7c4fe | |||
ec87089310 | |||
d8478ebb01 | |||
600adc81b5 | |||
ddac2870af | |||
8d9c8c1394 | |||
b59c3bcb23 | |||
7f554adba5 | |||
21ce061282 | |||
bdb71e9b14 | |||
df22e7de15 | |||
6b3550396b | |||
c70f1e31a6 | |||
695670e944 | |||
1028826788 | |||
82a8977c96 | |||
07d9ce1054 | |||
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 |
6
.github/CONTRIBUTING.md
vendored
@ -41,6 +41,12 @@ Always check if the web version of your instance is working.
|
|||||||
* Remember that PR review can take time.
|
* Remember that PR review can take time.
|
||||||
|
|
||||||
|
|
||||||
|
# Install Selfoss (if you don't have an instance)
|
||||||
|
|
||||||
|
I won't provide any selfoss instance url. If you want to help, but to not have one, you'll have to install one, and use it.
|
||||||
|
|
||||||
|
All the details to need are [here](https://selfoss.aditu.de/).
|
||||||
|
|
||||||
# Build the project
|
# Build the project
|
||||||
|
|
||||||
You can directly import this project into IntellIJ/Android Studio.
|
You can directly import this project into IntellIJ/Android Studio.
|
||||||
|
2
.gitignore
vendored
@ -217,5 +217,3 @@ gradle-app.setting
|
|||||||
release/
|
release/
|
||||||
|
|
||||||
crowdin.properties
|
crowdin.properties
|
||||||
|
|
||||||
publish-version.sh
|
|
20
CHANGELOG.md
@ -1,3 +1,23 @@
|
|||||||
|
**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**
|
**1.6.x**
|
||||||
|
|
||||||
- Handling hidden tags.
|
- Handling hidden tags.
|
||||||
|
@ -18,7 +18,11 @@ Also, the last APK built from source is available [here](https://jenkins.amine-b
|
|||||||
|
|
||||||
## Want to help ?
|
## Want to help ?
|
||||||
|
|
||||||
Check the [Contribution guide](https://github.com/aminecmi/ReaderforSelfoss/blob/master/.github/CONTRIBUTING.md)
|
1. **You'll have to have a Selfoss instance running.** You'll find everything you need to install it [here](https://selfoss.aditu.de/).
|
||||||
|
|
||||||
|
2. Check the [Contribution guide](https://github.com/aminecmi/ReaderforSelfoss/blob/master/.github/CONTRIBUTING.md).
|
||||||
|
|
||||||
|
3. Build the project by following [these steps](https://github.com/aminecmi/ReaderforSelfoss/blob/master/.github/CONTRIBUTING.md#build-the-project) (you should have read them after the contribution guide)
|
||||||
|
|
||||||
## Useful links
|
## Useful links
|
||||||
|
|
||||||
|
@ -1,17 +1,17 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
}
|
}
|
||||||
|
|
||||||
ext {
|
|
||||||
configuration = [
|
|
||||||
buildDate: new Date()
|
|
||||||
]
|
|
||||||
// This will make me able to build multiple times a day. May break thinks. I may forget it.
|
|
||||||
todaysBuilds = "1"
|
|
||||||
}
|
|
||||||
|
|
||||||
def gitVersion() {
|
def gitVersion() {
|
||||||
def process = "git describe --abbrev=0 --tags".execute()
|
def process
|
||||||
return process.text.substring(1).replaceAll("\\.", "").trim()
|
def maybeTagOfCurrentCommit = 'git describe --contains HEAD'.execute()
|
||||||
|
if (maybeTagOfCurrentCommit.text.isEmpty()) {
|
||||||
|
println "No tag on current commit. Will take the latest one."
|
||||||
|
process = "git for-each-ref refs/tags --sort=-authordate --format='%(refname:short)' --count=1".execute()
|
||||||
|
} else {
|
||||||
|
println "Tag found on current commit"
|
||||||
|
process = 'git describe --contains HEAD'.execute()
|
||||||
|
}
|
||||||
|
return process.text.replaceAll("'", "").substring(1).replaceAll("\\.", "").trim()
|
||||||
}
|
}
|
||||||
|
|
||||||
def versionCodeFromGit() {
|
def versionCodeFromGit() {
|
||||||
@ -24,6 +24,8 @@ def versionNameFromGit() {
|
|||||||
return gitVersion()
|
return gitVersion()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
apply plugin: 'kotlin-kapt'
|
||||||
|
|
||||||
apply plugin: 'com.android.application'
|
apply plugin: 'com.android.application'
|
||||||
|
|
||||||
apply plugin: 'kotlin-android'
|
apply plugin: 'kotlin-android'
|
||||||
@ -36,7 +38,7 @@ android {
|
|||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
compileSdkVersion 28
|
compileSdkVersion 28
|
||||||
buildToolsVersion '28.0.2'
|
buildToolsVersion '28.0.3'
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "apps.amine.bou.readerforselfoss"
|
applicationId "apps.amine.bou.readerforselfoss"
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
@ -53,7 +55,14 @@ android {
|
|||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
|
|
||||||
// tests
|
// tests
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
|
javaCompileOptions {
|
||||||
|
annotationProcessorOptions {
|
||||||
|
arguments = ["room.schemaLocation":
|
||||||
|
"$projectDir/schemas".toString()]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
@ -66,7 +75,6 @@ android {
|
|||||||
buildConfigField "String", "LOGIN_URL", appLoginUrl
|
buildConfigField "String", "LOGIN_URL", appLoginUrl
|
||||||
buildConfigField "String", "LOGIN_USERNAME", appLoginUsername
|
buildConfigField "String", "LOGIN_USERNAME", appLoginUsername
|
||||||
buildConfigField "String", "LOGIN_PASSWORD", appLoginPassword
|
buildConfigField "String", "LOGIN_PASSWORD", appLoginPassword
|
||||||
applicationIdSuffix ".dev"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
flavorDimensions "build"
|
flavorDimensions "build"
|
||||||
@ -85,32 +93,29 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Testing
|
// Testing
|
||||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-beta02'
|
||||||
androidTestImplementation 'com.android.support.test:runner:1.0.1'
|
androidTestImplementation 'androidx.test:runner:1.1.0-beta02'
|
||||||
// Espresso-contrib for DatePicker, RecyclerView, Drawer actions, Accessibility checks, CountingIdlingResource
|
// Espresso-contrib for DatePicker, RecyclerView, Drawer actions, Accessibility checks, CountingIdlingResource
|
||||||
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
|
// Espresso-intents for validation and stubbing of Intents
|
||||||
androidTestImplementation 'com.android.support.test.espresso:espresso-intents:3.0.1'
|
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.0-beta02'
|
||||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
// Android Support
|
// Android Support
|
||||||
implementation "com.android.support:appcompat-v7:$android_version"
|
implementation "androidx.appcompat:appcompat:$android_version"
|
||||||
implementation "com.android.support:design:$android_version"
|
implementation "com.google.android.material:material:$android_version"
|
||||||
implementation "com.android.support:recyclerview-v7:$android_version"
|
implementation "androidx.recyclerview:recyclerview:$android_version"
|
||||||
implementation "com.android.support:support-v4:$android_version"
|
implementation "androidx.legacy:legacy-support-v4:$android_version"
|
||||||
implementation "com.android.support:support-vector-drawable:$android_version"
|
implementation "androidx.vectordrawable:vectordrawable:$android_version"
|
||||||
implementation "com.android.support:customtabs:$android_version"
|
implementation "androidx.browser:browser:$android_version"
|
||||||
implementation "com.android.support:cardview-v7:$android_version"
|
implementation "androidx.cardview:cardview:$android_version"
|
||||||
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
|
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2'
|
||||||
|
|
||||||
//multidex
|
//multidex
|
||||||
implementation 'com.android.support:multidex:1.0.3'
|
implementation 'androidx.multidex:multidex:2.0.0'
|
||||||
|
|
||||||
// Intro
|
|
||||||
implementation 'agency.tango.android:material-intro-screen:0.0.5'
|
|
||||||
|
|
||||||
// About
|
// About
|
||||||
implementation('com.mikepenz:aboutlibraries:6.0.0@aar') {
|
implementation('com.mikepenz:aboutlibraries:6.2.0@aar') {
|
||||||
transitive = true
|
transitive = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,8 +126,8 @@ dependencies {
|
|||||||
implementation 'com.burgstaller:okhttp-digest:1.12'
|
implementation 'com.burgstaller:okhttp-digest:1.12'
|
||||||
|
|
||||||
// Material-ish things
|
// Material-ish things
|
||||||
implementation 'com.ashokvarma.android:bottom-navigation-bar:2.0.3'
|
implementation 'com.ashokvarma.android:bottom-navigation-bar:2.0.5'
|
||||||
implementation 'com.github.jd-alexander:LikeButton:0.2.1'
|
implementation 'com.github.jd-alexander:LikeButton:0.2.3'
|
||||||
implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
|
implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
|
||||||
|
|
||||||
// glide
|
// glide
|
||||||
@ -130,25 +135,32 @@ dependencies {
|
|||||||
implementation 'com.github.bumptech.glide:okhttp3-integration:4.1.1'
|
implementation 'com.github.bumptech.glide:okhttp3-integration:4.1.1'
|
||||||
|
|
||||||
// Asking politely users to rate the app
|
// Asking politely users to rate the app
|
||||||
implementation 'com.github.stkent:amplify:2.1.0'
|
implementation 'com.github.stkent:amplify:2.2.0'
|
||||||
|
|
||||||
// Drawer
|
// Drawer
|
||||||
implementation 'co.zsmb:materialdrawer-kt:1.3.5'
|
implementation 'co.zsmb:materialdrawer-kt:2.0.1'
|
||||||
implementation 'com.anupcowkur:reservoir:3.1.0'
|
|
||||||
|
|
||||||
// Themes
|
// Themes
|
||||||
implementation 'com.52inc:scoops:1.0.0'
|
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'
|
implementation 'com.github.rubensousa:floatingtoolbar:1.5.1'
|
||||||
|
|
||||||
// Pager
|
// 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
|
// Crash
|
||||||
implementation 'ch.acra:acra-http:5.1.3'
|
implementation 'ch.acra:acra-http:5.2.1'
|
||||||
implementation 'ch.acra:acra-dialog:5.1.3'
|
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>;
|
<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 okio.**
|
||||||
-dontwarn retrofit2.Platform$Java8
|
-dontwarn retrofit2.Platform$Java8
|
||||||
-keep class retrofit.** { *; }
|
-keep class retrofit.** { *; }
|
||||||
@ -76,3 +60,6 @@
|
|||||||
-dontwarn javax.annotation.**
|
-dontwarn javax.annotation.**
|
||||||
|
|
||||||
-keep class android.support.v7.widget.SearchView { *; }
|
-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.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.support.test.InstrumentationRegistry
|
import androidx.test.InstrumentationRegistry
|
||||||
import android.support.test.espresso.Espresso.onView
|
import androidx.test.espresso.Espresso.onView
|
||||||
import android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
||||||
import android.support.test.espresso.action.ViewActions.click
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
import android.support.test.espresso.action.ViewActions.closeSoftKeyboard
|
import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
|
||||||
import android.support.test.espresso.action.ViewActions.pressBack
|
import androidx.test.espresso.action.ViewActions.pressBack
|
||||||
import android.support.test.espresso.action.ViewActions.pressKey
|
import androidx.test.espresso.action.ViewActions.pressKey
|
||||||
import android.support.test.espresso.action.ViewActions.typeText
|
import androidx.test.espresso.action.ViewActions.typeText
|
||||||
import android.support.test.espresso.assertion.ViewAssertions.matches
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
import android.support.test.espresso.contrib.DrawerActions
|
import androidx.test.espresso.contrib.DrawerActions
|
||||||
import android.support.test.espresso.intent.Intents
|
import androidx.test.espresso.intent.Intents
|
||||||
import android.support.test.espresso.intent.Intents.intended
|
import androidx.test.espresso.intent.Intents.intended
|
||||||
import android.support.test.espresso.intent.Intents.times
|
import androidx.test.espresso.intent.Intents.times
|
||||||
import android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
||||||
import android.support.test.espresso.matcher.ViewMatchers.isDisplayed
|
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||||
import android.support.test.espresso.matcher.ViewMatchers.isRoot
|
import androidx.test.espresso.matcher.ViewMatchers.isRoot
|
||||||
import android.support.test.espresso.matcher.ViewMatchers.withContentDescription
|
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
|
||||||
import android.support.test.espresso.matcher.ViewMatchers.withId
|
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||||
import android.support.test.espresso.matcher.ViewMatchers.withText
|
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||||
import android.support.test.rule.ActivityTestRule
|
import androidx.test.rule.ActivityTestRule
|
||||||
import android.support.test.runner.AndroidJUnit4
|
import androidx.test.runner.AndroidJUnit4
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import apps.amine.bou.readerforselfoss.utils.Config
|
import apps.amine.bou.readerforselfoss.utils.Config
|
||||||
import org.junit.After
|
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.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.support.test.InstrumentationRegistry
|
import androidx.test.InstrumentationRegistry
|
||||||
import android.support.test.espresso.Espresso.onView
|
import androidx.test.espresso.Espresso.onView
|
||||||
import android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
||||||
import android.support.test.espresso.action.ViewActions.click
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
import android.support.test.espresso.action.ViewActions.closeSoftKeyboard
|
import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
|
||||||
import android.support.test.espresso.action.ViewActions.pressBack
|
import androidx.test.espresso.action.ViewActions.pressBack
|
||||||
import android.support.test.espresso.action.ViewActions.typeText
|
import androidx.test.espresso.action.ViewActions.typeText
|
||||||
import android.support.test.espresso.assertion.ViewAssertions.matches
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
import android.support.test.espresso.intent.Intents
|
import androidx.test.espresso.intent.Intents
|
||||||
import android.support.test.espresso.intent.Intents.intended
|
import androidx.test.espresso.intent.Intents.intended
|
||||||
import android.support.test.espresso.intent.Intents.times
|
import androidx.test.espresso.intent.Intents.times
|
||||||
import android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
||||||
import android.support.test.espresso.matcher.ViewMatchers
|
import androidx.test.espresso.matcher.ViewMatchers
|
||||||
import android.support.test.espresso.matcher.ViewMatchers.isRoot
|
import androidx.test.espresso.matcher.ViewMatchers.isRoot
|
||||||
import android.support.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
|
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
|
||||||
import android.support.test.espresso.matcher.ViewMatchers.withId
|
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||||
import android.support.test.espresso.matcher.ViewMatchers.withText
|
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||||
import android.support.test.rule.ActivityTestRule
|
import androidx.test.rule.ActivityTestRule
|
||||||
import android.support.test.runner.AndroidJUnit4
|
import androidx.test.runner.AndroidJUnit4
|
||||||
import apps.amine.bou.readerforselfoss.utils.Config
|
import apps.amine.bou.readerforselfoss.utils.Config
|
||||||
import com.mikepenz.aboutlibraries.ui.LibsActivity
|
import com.mikepenz.aboutlibraries.ui.LibsActivity
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
|
@ -3,13 +3,13 @@ package apps.amine.bou.readerforselfoss
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
import android.support.test.InstrumentationRegistry.getInstrumentation
|
import androidx.test.InstrumentationRegistry.getInstrumentation
|
||||||
import android.support.test.espresso.intent.Intents
|
import androidx.test.espresso.intent.Intents
|
||||||
import android.support.test.espresso.intent.Intents.intended
|
import androidx.test.espresso.intent.Intents.intended
|
||||||
import android.support.test.espresso.intent.Intents.times
|
import androidx.test.espresso.intent.Intents.times
|
||||||
import android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
||||||
import android.support.test.rule.ActivityTestRule
|
import androidx.test.rule.ActivityTestRule
|
||||||
import android.support.test.runner.AndroidJUnit4
|
import androidx.test.runner.AndroidJUnit4
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
|
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@ -45,7 +45,6 @@ class MainActivityEspressoTest {
|
|||||||
rule.launchActivity(intent)
|
rule.launchActivity(intent)
|
||||||
|
|
||||||
intended(hasComponent(MainActivity::class.java.name))
|
intended(hasComponent(MainActivity::class.java.name))
|
||||||
intended(hasComponent(IntroActivity::class.java.name))
|
|
||||||
intended(hasComponent(LoginActivity::class.java.name), times(0))
|
intended(hasComponent(LoginActivity::class.java.name), times(0))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,7 +57,6 @@ class MainActivityEspressoTest {
|
|||||||
|
|
||||||
intended(hasComponent(MainActivity::class.java.name))
|
intended(hasComponent(MainActivity::class.java.name))
|
||||||
intended(hasComponent(LoginActivity::class.java.name))
|
intended(hasComponent(LoginActivity::class.java.name))
|
||||||
intended(hasComponent(IntroActivity::class.java.name), times(0))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package apps.amine.bou.readerforselfoss
|
package apps.amine.bou.readerforselfoss
|
||||||
|
|
||||||
import android.support.design.widget.TextInputLayout
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import android.support.test.espresso.matcher.ViewMatchers
|
import androidx.test.espresso.matcher.ViewMatchers
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import org.hamcrest.Description
|
import org.hamcrest.Description
|
||||||
import org.hamcrest.Matcher
|
import org.hamcrest.Matcher
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
package="apps.amine.bou.readerforselfoss"
|
package="apps.amine.bou.readerforselfoss"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
@ -11,6 +12,7 @@
|
|||||||
android:icon="@mipmap/ic_launcher"
|
android:icon="@mipmap/ic_launcher"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
android:theme="@style/NoBar">
|
android:theme="@style/NoBar">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
@ -20,10 +22,9 @@
|
|||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
|
||||||
<activity
|
<meta-data android:name="android.app.shortcuts"
|
||||||
android:name=".IntroActivity"
|
android:resource="@xml/shortcuts" />
|
||||||
android:theme="@style/Theme.Intro">
|
|
||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".LoginActivity"
|
android:name=".LoginActivity"
|
||||||
|
@ -4,8 +4,8 @@ import android.content.Intent
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
import android.support.constraint.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import android.support.v7.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.AdapterView
|
import android.widget.AdapterView
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
@ -89,6 +89,7 @@ class AddSourceActivity : AppCompatActivity() {
|
|||||||
this,
|
this,
|
||||||
this@AddSourceActivity,
|
this@AddSourceActivity,
|
||||||
prefs.getBoolean("isSelfSignedCert", false),
|
prefs.getBoolean("isSelfSignedCert", false),
|
||||||
|
prefs.getString("api_timeout", "-1").toLong(),
|
||||||
prefs.getBoolean("should_log_everything", false)
|
prefs.getBoolean("should_log_everything", false)
|
||||||
)
|
)
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
@ -108,7 +109,7 @@ class AddSourceActivity : AppCompatActivity() {
|
|||||||
super.onResume()
|
super.onResume()
|
||||||
val config = Config(this)
|
val config = Config(this)
|
||||||
|
|
||||||
if (config.baseUrl.isEmpty() || !config.baseUrl.isBaseUrlValid()) {
|
if (config.baseUrl.isEmpty() || !config.baseUrl.isBaseUrlValid(false, this@AddSourceActivity)) {
|
||||||
mustLoginToAddSource()
|
mustLoginToAddSource()
|
||||||
} else {
|
} else {
|
||||||
handleSpoutsSpinner(spoutsSpinner, api, progress, formContainer)
|
handleSpoutsSpinner(spoutsSpinner, api, progress, formContainer)
|
||||||
|
@ -9,29 +9,42 @@ import android.net.Uri
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
import android.support.v4.view.MenuItemCompat
|
import androidx.core.view.MenuItemCompat
|
||||||
import android.support.v7.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import android.support.v7.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import android.support.v7.widget.DividerItemDecoration
|
import androidx.recyclerview.widget.DividerItemDecoration
|
||||||
import android.support.v7.widget.GridLayoutManager
|
import androidx.recyclerview.widget.GridLayoutManager
|
||||||
import android.support.v7.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import android.support.v7.widget.SearchView
|
import androidx.appcompat.widget.SearchView
|
||||||
import android.support.v7.widget.StaggeredGridLayoutManager
|
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
||||||
import android.support.v7.widget.helper.ItemTouchHelper
|
import androidx.recyclerview.widget.ItemTouchHelper
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
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.ItemCardAdapter
|
||||||
import apps.amine.bou.readerforselfoss.adapters.ItemListAdapter
|
import apps.amine.bou.readerforselfoss.adapters.ItemListAdapter
|
||||||
import apps.amine.bou.readerforselfoss.adapters.ItemsAdapter
|
import apps.amine.bou.readerforselfoss.adapters.ItemsAdapter
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.Sources
|
import apps.amine.bou.readerforselfoss.api.selfoss.Source
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.Stats
|
import apps.amine.bou.readerforselfoss.api.selfoss.Stats
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.Tag
|
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.settings.SettingsActivity
|
||||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||||
import apps.amine.bou.readerforselfoss.themes.Toppings
|
import apps.amine.bou.readerforselfoss.themes.Toppings
|
||||||
@ -42,14 +55,15 @@ import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
|||||||
import apps.amine.bou.readerforselfoss.utils.drawer.CustomUrlPrimaryDrawerItem
|
import apps.amine.bou.readerforselfoss.utils.drawer.CustomUrlPrimaryDrawerItem
|
||||||
import apps.amine.bou.readerforselfoss.utils.flattenTags
|
import apps.amine.bou.readerforselfoss.utils.flattenTags
|
||||||
import apps.amine.bou.readerforselfoss.utils.longHash
|
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.accountHeader
|
||||||
import co.zsmb.materialdrawerkt.builders.drawer
|
import co.zsmb.materialdrawerkt.builders.drawer
|
||||||
import co.zsmb.materialdrawerkt.builders.footer
|
import co.zsmb.materialdrawerkt.builders.footer
|
||||||
import co.zsmb.materialdrawerkt.draweritems.badgeable.primaryItem
|
import co.zsmb.materialdrawerkt.draweritems.badgeable.primaryItem
|
||||||
import co.zsmb.materialdrawerkt.draweritems.profile.profile
|
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.BottomNavigationBar
|
||||||
import com.ashokvarma.bottomnavigation.BottomNavigationItem
|
import com.ashokvarma.bottomnavigation.BottomNavigationItem
|
||||||
import com.ashokvarma.bottomnavigation.TextBadgeItem
|
import com.ashokvarma.bottomnavigation.TextBadgeItem
|
||||||
@ -65,9 +79,14 @@ import com.mikepenz.materialdrawer.model.DividerDrawerItem
|
|||||||
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
|
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
|
||||||
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem
|
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem
|
||||||
import kotlinx.android.synthetic.main.activity_home.*
|
import kotlinx.android.synthetic.main.activity_home.*
|
||||||
|
import kotlinx.android.synthetic.main.fragment_article.*
|
||||||
|
import org.acra.ACRA
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.Callback
|
import retrofit2.Callback
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||||
|
|
||||||
private val MENU_PREFERENCES = 12302
|
private val MENU_PREFERENCES = 12302
|
||||||
@ -82,7 +101,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
private var items: ArrayList<Item> = ArrayList()
|
private var items: ArrayList<Item> = ArrayList()
|
||||||
private var allItems: ArrayList<Item> = ArrayList()
|
private var allItems: ArrayList<Item> = ArrayList()
|
||||||
|
|
||||||
private var clickBehavior = false
|
|
||||||
private var debugReadingItems = false
|
private var debugReadingItems = false
|
||||||
private var shouldLogEverything = false
|
private var shouldLogEverything = false
|
||||||
private var internalBrowser = false
|
private var internalBrowser = false
|
||||||
@ -94,14 +112,18 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
private var itemsNumber: Int = 200
|
private var itemsNumber: Int = 200
|
||||||
private var elementsShown: Int = 0
|
private var elementsShown: Int = 0
|
||||||
private var maybeTagFilter: Tag? = null
|
private var maybeTagFilter: Tag? = null
|
||||||
private var maybeSourceFilter: Sources? = null
|
private var maybeSourceFilter: Source? = null
|
||||||
private var maybeSearchFilter: String? = null
|
private var maybeSearchFilter: String? = null
|
||||||
private var userIdentifier: String = ""
|
private var userIdentifier: String = ""
|
||||||
private var displayAccountHeader: Boolean = false
|
private var displayAccountHeader: Boolean = false
|
||||||
private var infiniteScroll: Boolean = false
|
private var infiniteScroll: Boolean = false
|
||||||
private var lastFetchDone: Boolean = false
|
private var lastFetchDone: Boolean = false
|
||||||
|
private var itemsCaching: Boolean = false
|
||||||
private var hiddenTags: List<String> = emptyList()
|
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 tabNewBadge: TextBadgeItem
|
||||||
private lateinit var tabArchiveBadge: TextBadgeItem
|
private lateinit var tabArchiveBadge: TextBadgeItem
|
||||||
@ -114,7 +136,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
private lateinit var appColors: AppColors
|
private lateinit var appColors: AppColors
|
||||||
private var offset: Int = 0
|
private var offset: Int = 0
|
||||||
private var firstVisible: 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 lateinit var settings: SharedPreferences
|
||||||
|
|
||||||
private var recyclerAdapter: RecyclerView.Adapter<*>? = null
|
private var recyclerAdapter: RecyclerView.Adapter<*>? = null
|
||||||
@ -123,10 +145,14 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
private var badgeAll: Int = -1
|
private var badgeAll: Int = -1
|
||||||
private var badgeFavs: Int = -1
|
private var badgeFavs: Int = -1
|
||||||
|
|
||||||
|
private var fromTabShortcut: Boolean = false
|
||||||
|
private var offlineShortcut: Boolean = false
|
||||||
|
|
||||||
private lateinit var tagsBadge: Map<Long, Int>
|
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() {
|
override fun onStart() {
|
||||||
super.onStart()
|
super.onStart()
|
||||||
@ -138,6 +164,13 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
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)
|
setContentView(R.layout.activity_home)
|
||||||
|
|
||||||
handleThemeBinding()
|
handleThemeBinding()
|
||||||
@ -147,6 +180,12 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
Amplify.getSharedInstance().promptIfReady(promptView)
|
Amplify.getSharedInstance().promptIfReady(promptView)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
db = Room.databaseBuilder(
|
||||||
|
applicationContext,
|
||||||
|
AppDatabase::class.java, "selfoss-database"
|
||||||
|
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).build()
|
||||||
|
|
||||||
|
|
||||||
customTabActivityHelper = CustomTabActivityHelper()
|
customTabActivityHelper = CustomTabActivityHelper()
|
||||||
|
|
||||||
sharedPref = PreferenceManager.getDefaultSharedPreferences(this)
|
sharedPref = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
@ -156,6 +195,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
this,
|
this,
|
||||||
this@HomeActivity,
|
this@HomeActivity,
|
||||||
settings.getBoolean("isSelfSignedCert", false),
|
settings.getBoolean("isSelfSignedCert", false),
|
||||||
|
sharedPref.getString("api_timeout", "-1").toLong(),
|
||||||
shouldLogEverything
|
shouldLogEverything
|
||||||
)
|
)
|
||||||
items = ArrayList()
|
items = ArrayList()
|
||||||
@ -167,29 +207,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
handleSwipeRefreshLayout()
|
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() {
|
private fun handleSwipeRefreshLayout() {
|
||||||
swipeRefreshLayout.setColorSchemeResources(
|
swipeRefreshLayout.setColorSchemeResources(
|
||||||
R.color.refresh_progress_1,
|
R.color.refresh_progress_1,
|
||||||
@ -197,6 +214,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
R.color.refresh_progress_3
|
R.color.refresh_progress_3
|
||||||
)
|
)
|
||||||
swipeRefreshLayout.setOnRefreshListener {
|
swipeRefreshLayout.setOnRefreshListener {
|
||||||
|
offlineShortcut = false
|
||||||
allItems = ArrayList()
|
allItems = ArrayList()
|
||||||
lastFetchDone = false
|
lastFetchDone = false
|
||||||
handleDrawerItems()
|
handleDrawerItems()
|
||||||
@ -209,8 +227,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
|
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
|
||||||
) {
|
) {
|
||||||
override fun getSwipeDirs(
|
override fun getSwipeDirs(
|
||||||
recyclerView: RecyclerView?,
|
recyclerView: RecyclerView,
|
||||||
viewHolder: RecyclerView.ViewHolder?
|
viewHolder: RecyclerView.ViewHolder
|
||||||
): Int =
|
): Int =
|
||||||
if (elementsShown != UNREAD_SHOWN) {
|
if (elementsShown != UNREAD_SHOWN) {
|
||||||
0
|
0
|
||||||
@ -242,7 +260,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
badgeNew--
|
badgeNew--
|
||||||
reloadBadgeContent()
|
reloadBadgeContent()
|
||||||
|
|
||||||
val tagHashes = i.tags.split(",").map { it.longHash() }
|
val tagHashes = i.tags.tags.split(",").map { it.longHash() }
|
||||||
tagsBadge = tagsBadge.map {
|
tagsBadge = tagsBadge.map {
|
||||||
if (tagHashes.contains(it.key)) {
|
if (tagHashes.contains(it.key)) {
|
||||||
(it.key to (it.value - 1))
|
(it.key to (it.value - 1))
|
||||||
@ -312,6 +330,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
|
|
||||||
bottomBar.setMode(BottomNavigationBar.MODE_SHIFTING)
|
bottomBar.setMode(BottomNavigationBar.MODE_SHIFTING)
|
||||||
bottomBar.setBackgroundStyle(BottomNavigationBar.BACKGROUND_STYLE_STATIC)
|
bottomBar.setBackgroundStyle(BottomNavigationBar.BACKGROUND_STYLE_STATIC)
|
||||||
|
|
||||||
|
if (fromTabShortcut) {
|
||||||
|
bottomBar.selectTab(elementsShown - 1)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
@ -343,6 +365,33 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
getElementsAccordingToTab()
|
getElementsAccordingToTab()
|
||||||
|
|
||||||
handleGDPRDialog(sharedPref.getBoolean("GDPR_shown", false))
|
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() {
|
override fun onStop() {
|
||||||
@ -353,7 +402,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
private fun handleSharedPrefs() {
|
private fun handleSharedPrefs() {
|
||||||
debugReadingItems = sharedPref.getBoolean("read_debug", false)
|
debugReadingItems = sharedPref.getBoolean("read_debug", false)
|
||||||
shouldLogEverything = sharedPref.getBoolean("should_log_everything", false)
|
shouldLogEverything = sharedPref.getBoolean("should_log_everything", false)
|
||||||
clickBehavior = sharedPref.getBoolean("tab_on_tap", false)
|
|
||||||
internalBrowser = sharedPref.getBoolean("prefer_internal_browser", true)
|
internalBrowser = sharedPref.getBoolean("prefer_internal_browser", true)
|
||||||
articleViewer = sharedPref.getBoolean("prefer_article_viewer", true)
|
articleViewer = sharedPref.getBoolean("prefer_article_viewer", true)
|
||||||
shouldBeCardView = sharedPref.getBoolean("card_view_active", false)
|
shouldBeCardView = sharedPref.getBoolean("card_view_active", false)
|
||||||
@ -364,11 +412,19 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
userIdentifier = sharedPref.getString("unique_id", "")
|
userIdentifier = sharedPref.getString("unique_id", "")
|
||||||
displayAccountHeader = sharedPref.getBoolean("account_header_displaying", false)
|
displayAccountHeader = sharedPref.getBoolean("account_header_displaying", false)
|
||||||
infiniteScroll = sharedPref.getBoolean("infinite_loading", false)
|
infiniteScroll = sharedPref.getBoolean("infinite_loading", false)
|
||||||
|
itemsCaching = sharedPref.getBoolean("items_caching", false)
|
||||||
hiddenTags = if (sharedPref.getString("hidden_tags", "").isNotEmpty()) {
|
hiddenTags = if (sharedPref.getString("hidden_tags", "").isNotEmpty()) {
|
||||||
sharedPref.getString("hidden_tags", "").replace("\\s".toRegex(), "").split(",")
|
sharedPref.getString("hidden_tags", "").replace("\\s".toRegex(), "").split(",")
|
||||||
} else {
|
} else {
|
||||||
emptyList()
|
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() {
|
private fun handleThemeBinding() {
|
||||||
@ -508,7 +564,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val filteredHiddenTags: List<Tag> = maybeTags.filter { hiddenTags.contains(it.tag) }
|
val filteredHiddenTags: List<Tag> =
|
||||||
|
maybeTags.filter { hiddenTags.contains(it.tag) }
|
||||||
tagsBadge = filteredHiddenTags.map {
|
tagsBadge = filteredHiddenTags.map {
|
||||||
val gd = GradientDrawable()
|
val gd = GradientDrawable()
|
||||||
val color = try {
|
val color = try {
|
||||||
@ -544,7 +601,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleSources(maybeSources: List<Sources>?) {
|
fun handleSources(maybeSources: List<Source>?) {
|
||||||
if (maybeSources == null) {
|
if (maybeSources == null) {
|
||||||
if (loadedFromCache) {
|
if (loadedFromCache) {
|
||||||
drawer.addItem(
|
drawer.addItem(
|
||||||
@ -587,14 +644,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
false
|
false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
drawer.addItem(DividerDrawerItem())
|
|
||||||
drawer.addItem(
|
|
||||||
SecondaryDrawerItem()
|
|
||||||
.withName(getString(R.string.drawer_item_tags))
|
|
||||||
.withIdentifier(DRAWER_ID_TAGS)
|
|
||||||
.withSelectable(false)
|
|
||||||
)
|
|
||||||
handleTags(maybeDrawerData.tags)
|
|
||||||
if (hiddenTags.isNotEmpty()) {
|
if (hiddenTags.isNotEmpty()) {
|
||||||
drawer.addItem(DividerDrawerItem())
|
drawer.addItem(DividerDrawerItem())
|
||||||
drawer.addItem(
|
drawer.addItem(
|
||||||
@ -606,6 +655,14 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
handleHiddenTags(maybeDrawerData.tags)
|
handleHiddenTags(maybeDrawerData.tags)
|
||||||
}
|
}
|
||||||
drawer.addItem(DividerDrawerItem())
|
drawer.addItem(DividerDrawerItem())
|
||||||
|
drawer.addItem(
|
||||||
|
SecondaryDrawerItem()
|
||||||
|
.withName(getString(R.string.drawer_item_tags))
|
||||||
|
.withIdentifier(DRAWER_ID_TAGS)
|
||||||
|
.withSelectable(false)
|
||||||
|
)
|
||||||
|
handleTags(maybeDrawerData.tags)
|
||||||
|
drawer.addItem(DividerDrawerItem())
|
||||||
drawer.addItem(
|
drawer.addItem(
|
||||||
SecondaryDrawerItem()
|
SecondaryDrawerItem()
|
||||||
.withName(getString(R.string.drawer_item_sources))
|
.withName(getString(R.string.drawer_item_sources))
|
||||||
@ -643,14 +700,19 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
|
|
||||||
|
|
||||||
if (!loadedFromCache) {
|
if (!loadedFromCache) {
|
||||||
Reservoir.putAsync(
|
if (maybeDrawerData.tags != null) {
|
||||||
"drawerData", maybeDrawerData, object : ReservoirPutCallback {
|
thread {
|
||||||
override fun onSuccess() {
|
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 {
|
} else {
|
||||||
if (!loadedFromCache) {
|
if (!loadedFromCache) {
|
||||||
@ -672,13 +734,14 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
|
|
||||||
fun drawerApiCalls(maybeDrawerData: DrawerData?) {
|
fun drawerApiCalls(maybeDrawerData: DrawerData?) {
|
||||||
var tags: List<Tag>? = null
|
var tags: List<Tag>? = null
|
||||||
var sources: List<Sources>?
|
var sources: List<Source>?
|
||||||
|
|
||||||
fun sourcesApiCall() {
|
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(
|
override fun onResponse(
|
||||||
call: Call<List<Sources>>?,
|
call: Call<List<Source>>?,
|
||||||
response: Response<List<Sources>>
|
response: Response<List<Source>>
|
||||||
) {
|
) {
|
||||||
sources = response.body()
|
sources = response.body()
|
||||||
val apiDrawerData = DrawerData(tags, sources)
|
val apiDrawerData = DrawerData(tags, sources)
|
||||||
@ -687,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>> {
|
api.tags.enqueue(object : Callback<List<Tag>> {
|
||||||
override fun onResponse(
|
override fun onResponse(
|
||||||
call: Call<List<Tag>>,
|
call: Call<List<Tag>>,
|
||||||
@ -706,6 +775,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
drawer.addItem(
|
drawer.addItem(
|
||||||
PrimaryDrawerItem().withName(getString(R.string.drawer_loading)).withSelectable(
|
PrimaryDrawerItem().withName(getString(R.string.drawer_loading)).withSelectable(
|
||||||
@ -713,18 +783,14 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
val resultType = object : TypeToken<DrawerData>() {}.type
|
thread {
|
||||||
Reservoir.getAsync(
|
var drawerData = DrawerData(db.drawerDataDao().tags().map { it.toView() },
|
||||||
"drawerData", resultType, object : ReservoirGetCallback<DrawerData> {
|
db.drawerDataDao().sources().map { it.toView() })
|
||||||
override fun onSuccess(maybeDrawerData: DrawerData?) {
|
runOnUiThread {
|
||||||
handleDrawerData(maybeDrawerData, loadedFromCache = true)
|
handleDrawerData(drawerData, loadedFromCache = true)
|
||||||
drawerApiCalls(maybeDrawerData)
|
drawerApiCalls(drawerData)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(p0: Exception?) {
|
|
||||||
drawerApiCalls(null)
|
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reloadLayoutManager() {
|
private fun reloadLayoutManager() {
|
||||||
@ -735,7 +801,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
when (currentManager) {
|
when (currentManager) {
|
||||||
is StaggeredGridLayoutManager ->
|
is StaggeredGridLayoutManager ->
|
||||||
if (!shouldBeCardView) {
|
if (!shouldBeCardView) {
|
||||||
layoutManager = GridLayoutManager(this, calculateNoOfColumns())
|
layoutManager = GridLayoutManager(
|
||||||
|
this,
|
||||||
|
calculateNoOfColumns()
|
||||||
|
)
|
||||||
recyclerView.layoutManager = layoutManager
|
recyclerView.layoutManager = layoutManager
|
||||||
}
|
}
|
||||||
is GridLayoutManager ->
|
is GridLayoutManager ->
|
||||||
@ -751,7 +820,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
else ->
|
else ->
|
||||||
if (currentManager == null) {
|
if (currentManager == null) {
|
||||||
if (!shouldBeCardView) {
|
if (!shouldBeCardView) {
|
||||||
layoutManager = GridLayoutManager(this, calculateNoOfColumns())
|
layoutManager = GridLayoutManager(
|
||||||
|
this,
|
||||||
|
calculateNoOfColumns()
|
||||||
|
)
|
||||||
recyclerView.layoutManager = layoutManager
|
recyclerView.layoutManager = layoutManager
|
||||||
} else {
|
} else {
|
||||||
layoutManager = StaggeredGridLayoutManager(
|
layoutManager = StaggeredGridLayoutManager(
|
||||||
@ -794,6 +866,45 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
override fun onTabSelected(position: Int) {
|
override fun onTabSelected(position: Int) {
|
||||||
offset = 0
|
offset = 0
|
||||||
lastFetchDone = false
|
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) {
|
when (position) {
|
||||||
0 -> getUnRead()
|
0 -> getUnRead()
|
||||||
1 -> getRead()
|
1 -> getRead()
|
||||||
@ -801,14 +912,14 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
else -> Unit
|
else -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleInfiniteScroll() {
|
private fun handleInfiniteScroll() {
|
||||||
if (recyclerViewScrollListener == null) {
|
|
||||||
recyclerViewScrollListener = object : RecyclerView.OnScrollListener() {
|
recyclerViewScrollListener = object : RecyclerView.OnScrollListener() {
|
||||||
override fun onScrolled(localRecycler: RecyclerView?, dx: Int, dy: Int) {
|
override fun onScrolled(localRecycler: RecyclerView, dx: Int, dy: Int) {
|
||||||
if (localRecycler != null && dy > 0) {
|
if (dy > 0) {
|
||||||
val manager = recyclerView.layoutManager
|
val manager = recyclerView.layoutManager
|
||||||
val lastVisibleItem: Int = when (manager) {
|
val lastVisibleItem: Int = when (manager) {
|
||||||
is StaggeredGridLayoutManager -> manager.findLastCompletelyVisibleItemPositions(
|
is StaggeredGridLayoutManager -> manager.findLastCompletelyVisibleItemPositions(
|
||||||
@ -824,7 +935,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
recyclerView.clearOnScrollListeners()
|
recyclerView.clearOnScrollListeners()
|
||||||
recyclerView.addOnScrollListener(recyclerViewScrollListener)
|
recyclerView.addOnScrollListener(recyclerViewScrollListener)
|
||||||
@ -843,6 +953,15 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
appendResults: Boolean = false,
|
appendResults: Boolean = false,
|
||||||
offsetOverride: Int? = null
|
offsetOverride: Int? = null
|
||||||
) {
|
) {
|
||||||
|
fun doGetAccordingToTab() {
|
||||||
|
when (elementsShown) {
|
||||||
|
UNREAD_SHOWN -> getUnRead(appendResults)
|
||||||
|
READ_SHOWN -> getRead(appendResults)
|
||||||
|
FAV_SHOWN -> getStarred(appendResults)
|
||||||
|
else -> getUnRead(appendResults)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
offset = if (appendResults && offsetOverride === null) {
|
offset = if (appendResults && offsetOverride === null) {
|
||||||
(offset + itemsNumber)
|
(offset + itemsNumber)
|
||||||
} else {
|
} else {
|
||||||
@ -850,12 +969,37 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
}
|
}
|
||||||
firstVisible = if (appendResults) firstVisible else 0
|
firstVisible = if (appendResults) firstVisible else 0
|
||||||
|
|
||||||
when (elementsShown) {
|
if (itemsCaching) {
|
||||||
UNREAD_SHOWN -> getUnRead(appendResults)
|
|
||||||
READ_SHOWN -> getRead(appendResults)
|
if (!swipeRefreshLayout.isRefreshing) {
|
||||||
FAV_SHOWN -> getStarred(appendResults)
|
swipeRefreshLayout.post { swipeRefreshLayout.isRefreshing = true }
|
||||||
else -> getUnRead(appendResults)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 {
|
private fun filter(tags: String): Boolean {
|
||||||
@ -869,12 +1013,13 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
call: (String?, Long?, String?) -> Call<List<Item>>
|
call: (String?, Long?, String?) -> Call<List<Item>>
|
||||||
) {
|
) {
|
||||||
fun handleItemsResponse(response: Response<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 (response.body() != null) {
|
||||||
if (shouldUpdate) {
|
if (shouldUpdate) {
|
||||||
|
getAndStoreAllItems()
|
||||||
items = response.body() as ArrayList<Item>
|
items = response.body() as ArrayList<Item>
|
||||||
items = items.filter {
|
items = items.filter {
|
||||||
maybeTagFilter != null || filter(it.tags)
|
maybeTagFilter != null || filter(it.tags.tags)
|
||||||
} as ArrayList<Item>
|
} as ArrayList<Item>
|
||||||
|
|
||||||
if (allItems.isEmpty()) {
|
if (allItems.isEmpty()) {
|
||||||
@ -891,9 +1036,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
allItems = ArrayList()
|
allItems = ArrayList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (shouldUpdate) {
|
|
||||||
handleListResult(appendResults)
|
handleListResult(appendResults)
|
||||||
}
|
|
||||||
|
|
||||||
if (!appendResults) mayBeEmpty()
|
if (!appendResults) mayBeEmpty()
|
||||||
swipeRefreshLayout.isRefreshing = false
|
swipeRefreshLayout.isRefreshing = false
|
||||||
@ -903,6 +1047,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
swipeRefreshLayout.post { swipeRefreshLayout.isRefreshing = true }
|
swipeRefreshLayout.post { swipeRefreshLayout.isRefreshing = true }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this@HomeActivity.isNetworkAccessible(this@HomeActivity.findViewById(R.id.coordLayout), offlineShortcut)) {
|
||||||
call(maybeTagFilter?.tag, maybeSourceFilter?.id?.toLong(), maybeSearchFilter)
|
call(maybeTagFilter?.tag, maybeSourceFilter?.id?.toLong(), maybeSearchFilter)
|
||||||
.enqueue(object : Callback<List<Item>> {
|
.enqueue(object : Callback<List<Item>> {
|
||||||
override fun onResponse(
|
override fun onResponse(
|
||||||
@ -921,6 +1066,9 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
swipeRefreshLayout.post { swipeRefreshLayout.isRefreshing = false }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getUnRead(appendResults: Boolean = false) {
|
private fun getUnRead(appendResults: Boolean = false) {
|
||||||
@ -981,6 +1129,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
this,
|
this,
|
||||||
items,
|
items,
|
||||||
api,
|
api,
|
||||||
|
db,
|
||||||
customTabActivityHelper,
|
customTabActivityHelper,
|
||||||
internalBrowser,
|
internalBrowser,
|
||||||
articleViewer,
|
articleViewer,
|
||||||
@ -997,8 +1146,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
this,
|
this,
|
||||||
items,
|
items,
|
||||||
api,
|
api,
|
||||||
|
db,
|
||||||
customTabActivityHelper,
|
customTabActivityHelper,
|
||||||
clickBehavior,
|
|
||||||
internalBrowser,
|
internalBrowser,
|
||||||
articleViewer,
|
articleViewer,
|
||||||
debugReadingItems,
|
debugReadingItems,
|
||||||
@ -1028,7 +1177,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun reloadBadges() {
|
private fun reloadBadges() {
|
||||||
if (displayUnreadCount || displayAllCount) {
|
if (this@HomeActivity.isNetworkAccessible(null, offlineShortcut) && (displayUnreadCount || displayAllCount)) {
|
||||||
api.stats.enqueue(object : Callback<Stats> {
|
api.stats.enqueue(object : Callback<Stats> {
|
||||||
override fun onResponse(call: Call<Stats>, response: Response<Stats>) {
|
override fun onResponse(call: Call<Stats>, response: Response<Stats>) {
|
||||||
if (response.body() != null) {
|
if (response.body() != null) {
|
||||||
@ -1134,6 +1283,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.refresh -> {
|
R.id.refresh -> {
|
||||||
|
if (this@HomeActivity.isNetworkAccessible(null, offlineShortcut)) {
|
||||||
needsConfirmation(R.string.menu_home_refresh, R.string.refresh_dialog_message) {
|
needsConfirmation(R.string.menu_home_refresh, R.string.refresh_dialog_message) {
|
||||||
api.update().enqueue(object : Callback<String> {
|
api.update().enqueue(object : Callback<String> {
|
||||||
override fun onResponse(
|
override fun onResponse(
|
||||||
@ -1158,6 +1308,9 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
Toast.makeText(this, R.string.refresh_in_progress, Toast.LENGTH_SHORT).show()
|
Toast.makeText(this, R.string.refresh_in_progress, Toast.LENGTH_SHORT).show()
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
R.id.readAll -> {
|
R.id.readAll -> {
|
||||||
if (elementsShown == UNREAD_SHOWN) {
|
if (elementsShown == UNREAD_SHOWN) {
|
||||||
@ -1166,15 +1319,15 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
val ids = allItems.map { it.id }
|
val ids = allItems.map { it.id }
|
||||||
val itemsByTag: Map<Long, Int> =
|
val itemsByTag: Map<Long, Int> =
|
||||||
allItems.flattenTags()
|
allItems.flattenTags()
|
||||||
.groupBy { it.tags.longHash() }
|
.groupBy { it.tags.tags.longHash() }
|
||||||
.map { it.key to it.value.size }
|
.map { it.key to it.value.size }
|
||||||
.toMap()
|
.toMap()
|
||||||
|
|
||||||
fun readAllDebug(e: Throwable) {
|
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> {
|
api.readAll(ids).enqueue(object : Callback<SuccessResponse> {
|
||||||
override fun onResponse(
|
override fun onResponse(
|
||||||
call: Call<SuccessResponse>,
|
call: Call<SuccessResponse>,
|
||||||
@ -1259,8 +1412,79 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
else -> badgeNew // if !elementsShown then unread are fetched.
|
else -> badgeNew // if !elementsShown then unread are fetched.
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateItems(adapterItems: ArrayList<Item>) {
|
private fun updateItems(adapterItems: ArrayList<Item>) {
|
||||||
items = adapterItems
|
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.Intent
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.support.v7.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import android.support.v7.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import android.text.TextUtils
|
import android.text.TextUtils
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
@ -21,6 +21,7 @@ import apps.amine.bou.readerforselfoss.themes.AppColors
|
|||||||
import apps.amine.bou.readerforselfoss.utils.Config
|
import apps.amine.bou.readerforselfoss.utils.Config
|
||||||
import apps.amine.bou.readerforselfoss.utils.isBaseUrlValid
|
import apps.amine.bou.readerforselfoss.utils.isBaseUrlValid
|
||||||
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
|
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
|
||||||
|
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
||||||
import com.mikepenz.aboutlibraries.Libs
|
import com.mikepenz.aboutlibraries.Libs
|
||||||
import com.mikepenz.aboutlibraries.LibsBuilder
|
import com.mikepenz.aboutlibraries.LibsBuilder
|
||||||
import kotlinx.android.synthetic.main.activity_login.*
|
import kotlinx.android.synthetic.main.activity_login.*
|
||||||
@ -53,7 +54,6 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
handleBaseUrlFail()
|
handleBaseUrlFail()
|
||||||
|
|
||||||
|
|
||||||
settings = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
settings = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||||
userIdentifier = settings.getString("unique_id", "")
|
userIdentifier = settings.getString("unique_id", "")
|
||||||
logErrors = settings.getBoolean("login_debug", false)
|
logErrors = settings.getBoolean("login_debug", false)
|
||||||
@ -144,7 +144,7 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
var cancel = false
|
var cancel = false
|
||||||
var focusView: View? = null
|
var focusView: View? = null
|
||||||
|
|
||||||
if (!url.isBaseUrlValid()) {
|
if (!url.isBaseUrlValid(logErrors, this@LoginActivity)) {
|
||||||
urlView.error = getString(R.string.login_url_problem)
|
urlView.error = getString(R.string.login_url_problem)
|
||||||
focusView = urlView
|
focusView = urlView
|
||||||
cancel = true
|
cancel = true
|
||||||
@ -163,7 +163,7 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isWithLogin || isWithHTTPLogin) {
|
if (isWithLogin) {
|
||||||
if (TextUtils.isEmpty(password)) {
|
if (TextUtils.isEmpty(password)) {
|
||||||
passwordView.error = getString(R.string.error_invalid_password)
|
passwordView.error = getString(R.string.error_invalid_password)
|
||||||
focusView = passwordView
|
focusView = passwordView
|
||||||
@ -177,6 +177,20 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isWithHTTPLogin) {
|
||||||
|
if (TextUtils.isEmpty(httpPassword)) {
|
||||||
|
httpPasswordView.error = getString(R.string.error_invalid_password)
|
||||||
|
focusView = httpPasswordView
|
||||||
|
cancel = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TextUtils.isEmpty(httpLogin)) {
|
||||||
|
httpLoginView.error = getString(R.string.error_field_required)
|
||||||
|
focusView = httpLoginView
|
||||||
|
cancel = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (cancel) {
|
if (cancel) {
|
||||||
focusView?.requestFocus()
|
focusView?.requestFocus()
|
||||||
} else {
|
} else {
|
||||||
@ -194,8 +208,11 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
this,
|
this,
|
||||||
this@LoginActivity,
|
this@LoginActivity,
|
||||||
isWithSelfSignedCert,
|
isWithSelfSignedCert,
|
||||||
|
-1L,
|
||||||
isWithSelfSignedCert
|
isWithSelfSignedCert
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (this@LoginActivity.isNetworkAccessible(this@LoginActivity.findViewById(R.id.loginForm))) {
|
||||||
api.login().enqueue(object : Callback<SuccessResponse> {
|
api.login().enqueue(object : Callback<SuccessResponse> {
|
||||||
private fun preferenceError(t: Throwable) {
|
private fun preferenceError(t: Throwable) {
|
||||||
editor.remove("url")
|
editor.remove("url")
|
||||||
@ -235,6 +252,9 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
preferenceError(t)
|
preferenceError(t)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
showProgress(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@ package apps.amine.bou.readerforselfoss
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
import android.support.v7.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
@ -11,17 +11,9 @@ class MainActivity : AppCompatActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_main)
|
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)
|
val intent = Intent(this, LoginActivity::class.java)
|
||||||
startActivity(intent)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
startActivity(intent)
|
||||||
finish()
|
finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,14 +1,15 @@
|
|||||||
package apps.amine.bou.readerforselfoss
|
package apps.amine.bou.readerforselfoss
|
||||||
|
|
||||||
|
import android.app.NotificationChannel
|
||||||
|
import android.app.NotificationManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
|
||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
import android.support.multidex.MultiDexApplication
|
import androidx.multidex.MultiDexApplication
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import apps.amine.bou.readerforselfoss.utils.Config
|
import apps.amine.bou.readerforselfoss.utils.Config
|
||||||
import com.anupcowkur.reservoir.Reservoir
|
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.request.RequestOptions
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import com.ftinc.scoop.Scoop
|
import com.ftinc.scoop.Scoop
|
||||||
@ -27,10 +28,8 @@ import java.io.IOException
|
|||||||
import java.util.UUID.randomUUID
|
import java.util.UUID.randomUUID
|
||||||
|
|
||||||
|
|
||||||
@AcraHttpSender(uri = "http://amine-bou.fr:5984/acra-selfoss/_design/acra-storage/_update/report",
|
@AcraHttpSender(uri = "http://37.187.110.167/amine/acra/simplest-acra.php",
|
||||||
basicAuthLogin = "selfoss",
|
httpMethod = HttpSender.Method.POST)
|
||||||
basicAuthPassword = "selfoss",
|
|
||||||
httpMethod = HttpSender.Method.PUT)
|
|
||||||
@AcraDialog(resText = R.string.crash_dialog_text,
|
@AcraDialog(resText = R.string.crash_dialog_text,
|
||||||
resCommentPrompt = R.string.crash_dialog_comment,
|
resCommentPrompt = R.string.crash_dialog_comment,
|
||||||
resTheme = android.R.style.Theme_DeviceDefault_Dialog)
|
resTheme = android.R.style.Theme_DeviceDefault_Dialog)
|
||||||
@ -49,8 +48,6 @@ class MyApp : MultiDexApplication() {
|
|||||||
|
|
||||||
initAmplify()
|
initAmplify()
|
||||||
|
|
||||||
initCache()
|
|
||||||
|
|
||||||
val prefs = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
val prefs = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||||
if (prefs.getString("unique_id", "").isEmpty()) {
|
if (prefs.getString("unique_id", "").isEmpty()) {
|
||||||
val editor = prefs.edit()
|
val editor = prefs.edit()
|
||||||
@ -63,6 +60,25 @@ class MyApp : MultiDexApplication() {
|
|||||||
initTheme()
|
initTheme()
|
||||||
|
|
||||||
tryToHandleBug()
|
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?) {
|
override fun attachBaseContext(base: Context?) {
|
||||||
@ -80,14 +96,6 @@ class MyApp : MultiDexApplication() {
|
|||||||
.applyAllDefaultRules()
|
.applyAllDefaultRules()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun initCache() {
|
|
||||||
try {
|
|
||||||
Reservoir.init(this, 8192) //in bytes
|
|
||||||
} catch (e: IOException) {
|
|
||||||
//failure
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun initDrawerImageLoader() {
|
private fun initDrawerImageLoader() {
|
||||||
DrawerImageLoader.init(object : AbstractDrawerImageLoader() {
|
DrawerImageLoader.init(object : AbstractDrawerImageLoader() {
|
||||||
override fun set(
|
override fun set(
|
||||||
|
@ -1,26 +1,35 @@
|
|||||||
package apps.amine.bou.readerforselfoss
|
package apps.amine.bou.readerforselfoss
|
||||||
|
|
||||||
|
import android.content.SharedPreferences
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
import android.support.v4.app.FragmentManager
|
import androidx.fragment.app.FragmentManager
|
||||||
import android.support.v4.app.FragmentStatePagerAdapter
|
import androidx.fragment.app.FragmentStatePagerAdapter
|
||||||
import android.support.v4.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import android.support.v4.view.ViewPager
|
import androidx.viewpager.widget.ViewPager
|
||||||
import android.support.v7.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.room.Room
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||||
import apps.amine.bou.readerforselfoss.fragments.ArticleFragment
|
import apps.amine.bou.readerforselfoss.fragments.ArticleFragment
|
||||||
|
import apps.amine.bou.readerforselfoss.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.AppColors
|
||||||
import apps.amine.bou.readerforselfoss.themes.Toppings
|
import apps.amine.bou.readerforselfoss.themes.Toppings
|
||||||
import apps.amine.bou.readerforselfoss.transformers.DepthPageTransformer
|
import apps.amine.bou.readerforselfoss.transformers.DepthPageTransformer
|
||||||
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
|
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.succeeded
|
||||||
import apps.amine.bou.readerforselfoss.utils.toggleStar
|
import apps.amine.bou.readerforselfoss.utils.toggleStar
|
||||||
import com.ftinc.scoop.Scoop
|
import com.ftinc.scoop.Scoop
|
||||||
@ -30,6 +39,7 @@ import org.acra.ACRA
|
|||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.Callback
|
import retrofit2.Callback
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
class ReaderActivity : AppCompatActivity() {
|
class ReaderActivity : AppCompatActivity() {
|
||||||
|
|
||||||
@ -42,6 +52,13 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
private lateinit var toolbarMenu: Menu
|
private lateinit var toolbarMenu: Menu
|
||||||
|
|
||||||
|
private lateinit var db: AppDatabase
|
||||||
|
private lateinit var prefs: SharedPreferences
|
||||||
|
|
||||||
|
private var activeAlignment: Int = 1
|
||||||
|
val JUSTIFY = 1
|
||||||
|
val ALIGN_LEFT = 2
|
||||||
|
|
||||||
private fun showMenuItem(willAddToFavorite: Boolean) {
|
private fun showMenuItem(willAddToFavorite: Boolean) {
|
||||||
toolbarMenu.findItem(R.id.save).isVisible = willAddToFavorite
|
toolbarMenu.findItem(R.id.save).isVisible = willAddToFavorite
|
||||||
toolbarMenu.findItem(R.id.unsave).isVisible = !willAddToFavorite
|
toolbarMenu.findItem(R.id.unsave).isVisible = !willAddToFavorite
|
||||||
@ -55,11 +72,18 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
showMenuItem(false)
|
showMenuItem(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private lateinit var editor: SharedPreferences.Editor
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
setContentView(R.layout.activity_reader)
|
setContentView(R.layout.activity_reader)
|
||||||
|
|
||||||
|
db = Room.databaseBuilder(
|
||||||
|
applicationContext,
|
||||||
|
AppDatabase::class.java, "selfoss-database"
|
||||||
|
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).build()
|
||||||
|
|
||||||
val scoop = Scoop.getInstance()
|
val scoop = Scoop.getInstance()
|
||||||
scoop.bind(this, Toppings.PRIMARY.value, toolBar)
|
scoop.bind(this, Toppings.PRIMARY.value, toolBar)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
@ -70,16 +94,19 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||||
|
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
editor = prefs.edit()
|
||||||
|
|
||||||
debugReadingItems = prefs.getBoolean("read_debug", false)
|
debugReadingItems = prefs.getBoolean("read_debug", false)
|
||||||
userIdentifier = prefs.getString("unique_id", "")
|
userIdentifier = prefs.getString("unique_id", "")
|
||||||
markOnScroll = prefs.getBoolean("mark_on_scroll", false)
|
markOnScroll = prefs.getBoolean("mark_on_scroll", false)
|
||||||
|
activeAlignment = prefs.getInt("text_align", JUSTIFY)
|
||||||
|
|
||||||
api = SelfossApi(
|
api = SelfossApi(
|
||||||
this,
|
this,
|
||||||
this@ReaderActivity,
|
this@ReaderActivity,
|
||||||
prefs.getBoolean("isSelfSignedCert", false),
|
prefs.getBoolean("isSelfSignedCert", false),
|
||||||
|
prefs.getString("api_timeout", "-1").toLong(),
|
||||||
prefs.getBoolean("should_log_everything", false)
|
prefs.getBoolean("should_log_everything", false)
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -89,9 +116,10 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
currentItem = intent.getIntExtra("currentItem", 0)
|
currentItem = intent.getIntExtra("currentItem", 0)
|
||||||
|
|
||||||
readItem(allItems[currentItem].id)
|
readItem(allItems[currentItem])
|
||||||
|
|
||||||
pager.adapter = ScreenSlidePagerAdapter(supportFragmentManager, AppColors(this@ReaderActivity))
|
pager.adapter =
|
||||||
|
ScreenSlidePagerAdapter(supportFragmentManager, AppColors(this@ReaderActivity))
|
||||||
pager.currentItem = currentItem
|
pager.currentItem = currentItem
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,15 +141,19 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
} else {
|
} else {
|
||||||
canFavorite()
|
canFavorite()
|
||||||
}
|
}
|
||||||
readItem(allItems[pager.currentItem].id)
|
readItem(allItems[pager.currentItem])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun readItem(id: String) {
|
fun readItem(item: Item) {
|
||||||
if (markOnScroll) {
|
if (markOnScroll) {
|
||||||
api.markItem(id).enqueue(
|
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> {
|
object : Callback<SuccessResponse> {
|
||||||
override fun onResponse(
|
override fun onResponse(
|
||||||
call: Call<SuccessResponse>,
|
call: Call<SuccessResponse>,
|
||||||
@ -145,12 +177,21 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
call: Call<SuccessResponse>,
|
call: Call<SuccessResponse>,
|
||||||
t: Throwable
|
t: Throwable
|
||||||
) {
|
) {
|
||||||
|
thread {
|
||||||
|
db.itemsDao().insertAllItems(item.toEntity())
|
||||||
|
}
|
||||||
if (debugReadingItems) {
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -173,7 +214,6 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
private inner class ScreenSlidePagerAdapter(fm: FragmentManager, val appColors: AppColors) :
|
private inner class ScreenSlidePagerAdapter(fm: FragmentManager, val appColors: AppColors) :
|
||||||
FragmentStatePagerAdapter(fm) {
|
FragmentStatePagerAdapter(fm) {
|
||||||
|
|
||||||
|
|
||||||
override fun getCount(): Int {
|
override fun getCount(): Int {
|
||||||
return allItems.size
|
return allItems.size
|
||||||
}
|
}
|
||||||
@ -185,10 +225,20 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
override fun startUpdate(container: ViewGroup) {
|
override fun startUpdate(container: ViewGroup) {
|
||||||
super.startUpdate(container)
|
super.startUpdate(container)
|
||||||
|
|
||||||
container.background = ColorDrawable(ContextCompat.getColor(this@ReaderActivity, appColors.colorBackground))
|
container.background = ColorDrawable(
|
||||||
|
ContextCompat.getColor(
|
||||||
|
this@ReaderActivity,
|
||||||
|
appColors.colorBackground
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun alignmentMenu(showJustify: Boolean) {
|
||||||
|
toolbarMenu.findItem(R.id.align_left).isVisible = !showJustify
|
||||||
|
toolbarMenu.findItem(R.id.align_justify).isVisible = showJustify
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
val inflater = menuInflater
|
val inflater = menuInflater
|
||||||
inflater.inflate(R.menu.reader_menu, menu)
|
inflater.inflate(R.menu.reader_menu, menu)
|
||||||
@ -199,26 +249,43 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
} else {
|
} else {
|
||||||
canFavorite()
|
canFavorite()
|
||||||
}
|
}
|
||||||
|
if (activeAlignment == JUSTIFY) {
|
||||||
|
alignmentMenu(false)
|
||||||
|
} else {
|
||||||
|
alignmentMenu(true)
|
||||||
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
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) {
|
when (item.itemId) {
|
||||||
android.R.id.home -> {
|
android.R.id.home -> {
|
||||||
onBackPressed()
|
onBackPressed()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.save -> {
|
R.id.save -> {
|
||||||
|
if (this@ReaderActivity.isNetworkAccessible(null)) {
|
||||||
api.starrItem(allItems[pager.currentItem].id)
|
api.starrItem(allItems[pager.currentItem].id)
|
||||||
.enqueue(object : Callback<SuccessResponse> {
|
.enqueue(object : Callback<SuccessResponse> {
|
||||||
override fun onResponse(
|
override fun onResponse(
|
||||||
call: Call<SuccessResponse>,
|
call: Call<SuccessResponse>,
|
||||||
response: Response<SuccessResponse>
|
response: Response<SuccessResponse>
|
||||||
) {
|
) {
|
||||||
allItems[pager.currentItem] = allItems[pager.currentItem].toggleStar()
|
afterSave()
|
||||||
notifyAdapter()
|
|
||||||
canRemoveFromFavorite()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(
|
override fun onFailure(
|
||||||
@ -232,17 +299,22 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
thread {
|
||||||
|
db.actionsDao().insertAllActions(ActionEntity(allItems[pager.currentItem].id, false, false, true, false))
|
||||||
|
afterSave()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
R.id.unsave -> {
|
R.id.unsave -> {
|
||||||
|
if (this@ReaderActivity.isNetworkAccessible(null)) {
|
||||||
api.unstarrItem(allItems[pager.currentItem].id)
|
api.unstarrItem(allItems[pager.currentItem].id)
|
||||||
.enqueue(object : Callback<SuccessResponse> {
|
.enqueue(object : Callback<SuccessResponse> {
|
||||||
override fun onResponse(
|
override fun onResponse(
|
||||||
call: Call<SuccessResponse>,
|
call: Call<SuccessResponse>,
|
||||||
response: Response<SuccessResponse>
|
response: Response<SuccessResponse>
|
||||||
) {
|
) {
|
||||||
allItems[pager.currentItem] = allItems[pager.currentItem].toggleStar()
|
afterUnsave()
|
||||||
notifyAdapter()
|
|
||||||
canFavorite()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(
|
override fun onFailure(
|
||||||
@ -256,11 +328,36 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
thread {
|
||||||
|
db.actionsDao().insertAllActions(ActionEntity(allItems[pager.currentItem].id, false, false, false, true))
|
||||||
|
afterUnsave()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
R.id.align_left -> {
|
||||||
|
editor.putInt("text_align", ALIGN_LEFT)
|
||||||
|
editor.apply()
|
||||||
|
alignmentMenu(true)
|
||||||
|
refreshFragment()
|
||||||
|
}
|
||||||
|
R.id.align_justify -> {
|
||||||
|
editor.putInt("text_align", JUSTIFY)
|
||||||
|
editor.apply()
|
||||||
|
alignmentMenu(false)
|
||||||
|
refreshFragment()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.onOptionsItemSelected(item)
|
return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun refreshFragment() {
|
||||||
|
finish()
|
||||||
|
overridePendingTransition(0, 0)
|
||||||
|
startActivity(intent)
|
||||||
|
overridePendingTransition(0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
var allItems: ArrayList<Item> = ArrayList()
|
var allItems: ArrayList<Item> = ArrayList()
|
||||||
}
|
}
|
||||||
|
@ -5,14 +5,15 @@ import android.content.res.ColorStateList
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
import android.support.v7.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import android.support.v7.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import apps.amine.bou.readerforselfoss.adapters.SourcesListAdapter
|
import apps.amine.bou.readerforselfoss.adapters.SourcesListAdapter
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.Sources
|
import apps.amine.bou.readerforselfoss.api.selfoss.Source
|
||||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||||
import apps.amine.bou.readerforselfoss.themes.Toppings
|
import apps.amine.bou.readerforselfoss.themes.Toppings
|
||||||
|
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
||||||
import com.ftinc.scoop.Scoop
|
import com.ftinc.scoop.Scoop
|
||||||
import kotlinx.android.synthetic.main.activity_sources.*
|
import kotlinx.android.synthetic.main.activity_sources.*
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
@ -59,20 +60,22 @@ class SourcesActivity : AppCompatActivity() {
|
|||||||
this,
|
this,
|
||||||
this@SourcesActivity,
|
this@SourcesActivity,
|
||||||
prefs.getBoolean("isSelfSignedCert", false),
|
prefs.getBoolean("isSelfSignedCert", false),
|
||||||
|
prefs.getString("api_timeout", "-1").toLong(),
|
||||||
prefs.getBoolean("should_log_everything", false)
|
prefs.getBoolean("should_log_everything", false)
|
||||||
)
|
)
|
||||||
var items: ArrayList<Sources> = ArrayList()
|
var items: ArrayList<Source> = ArrayList()
|
||||||
|
|
||||||
recyclerView.setHasFixedSize(true)
|
recyclerView.setHasFixedSize(true)
|
||||||
recyclerView.layoutManager = mLayoutManager
|
recyclerView.layoutManager = mLayoutManager
|
||||||
|
|
||||||
api.sources.enqueue(object : Callback<List<Sources>> {
|
if (this@SourcesActivity.isNetworkAccessible(this@SourcesActivity.findViewById(R.id.recyclerView))) {
|
||||||
|
api.sources.enqueue(object : Callback<List<Source>> {
|
||||||
override fun onResponse(
|
override fun onResponse(
|
||||||
call: Call<List<Sources>>,
|
call: Call<List<Source>>,
|
||||||
response: Response<List<Sources>>
|
response: Response<List<Source>>
|
||||||
) {
|
) {
|
||||||
if (response.body() != null && response.body()!!.isNotEmpty()) {
|
if (response.body() != null && response.body()!!.isNotEmpty()) {
|
||||||
items = response.body() as ArrayList<Sources>
|
items = response.body() as ArrayList<Source>
|
||||||
}
|
}
|
||||||
val mAdapter = SourcesListAdapter(this@SourcesActivity, items, api)
|
val mAdapter = SourcesListAdapter(this@SourcesActivity, items, api)
|
||||||
recyclerView.adapter = mAdapter
|
recyclerView.adapter = mAdapter
|
||||||
@ -86,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(
|
Toast.makeText(
|
||||||
this@SourcesActivity,
|
this@SourcesActivity,
|
||||||
R.string.cant_get_sources,
|
R.string.cant_get_sources,
|
||||||
@ -94,6 +97,7 @@ class SourcesActivity : AppCompatActivity() {
|
|||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fab.setOnClickListener {
|
fab.setOnClickListener {
|
||||||
startActivity(Intent(this@SourcesActivity, AddSourceActivity::class.java))
|
startActivity(Intent(this@SourcesActivity, AddSourceActivity::class.java))
|
||||||
|
@ -2,8 +2,8 @@ package apps.amine.bou.readerforselfoss.adapters
|
|||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.support.v7.widget.CardView
|
import androidx.cardview.widget.CardView
|
||||||
import android.support.v7.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import android.text.Html
|
import android.text.Html
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@ -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.Item
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||||
|
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
||||||
|
import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity
|
||||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
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.buildCustomTabsIntent
|
||||||
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
||||||
import apps.amine.bou.readerforselfoss.utils.glide.bitmapCenterCrop
|
import apps.amine.bou.readerforselfoss.utils.glide.bitmapCenterCrop
|
||||||
import apps.amine.bou.readerforselfoss.utils.glide.circularBitmapDrawable
|
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.openInBrowserAsNewTask
|
||||||
import apps.amine.bou.readerforselfoss.utils.openItemUrl
|
import apps.amine.bou.readerforselfoss.utils.openItemUrl
|
||||||
import apps.amine.bou.readerforselfoss.utils.shareLink
|
import apps.amine.bou.readerforselfoss.utils.shareLink
|
||||||
@ -33,11 +37,13 @@ import kotlinx.android.synthetic.main.card_item.view.*
|
|||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.Callback
|
import retrofit2.Callback
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
class ItemCardAdapter(
|
class ItemCardAdapter(
|
||||||
override val app: Activity,
|
override val app: Activity,
|
||||||
override var items: ArrayList<Item>,
|
override var items: ArrayList<Item>,
|
||||||
override val api: SelfossApi,
|
override val api: SelfossApi,
|
||||||
|
override val db: AppDatabase,
|
||||||
private val helper: CustomTabActivityHelper,
|
private val helper: CustomTabActivityHelper,
|
||||||
private val internalBrowser: Boolean,
|
private val internalBrowser: Boolean,
|
||||||
private val articleViewer: Boolean,
|
private val articleViewer: Boolean,
|
||||||
@ -63,6 +69,7 @@ class ItemCardAdapter(
|
|||||||
|
|
||||||
holder.mView.favButton.isLiked = itm.starred
|
holder.mView.favButton.isLiked = itm.starred
|
||||||
holder.mView.title.text = Html.fromHtml(itm.title)
|
holder.mView.title.text = Html.fromHtml(itm.title)
|
||||||
|
holder.mView.title.setOnTouchListener(LinkOnTouchListener())
|
||||||
|
|
||||||
holder.mView.title.setLinkTextColor(appColors.colorAccent)
|
holder.mView.title.setLinkTextColor(appColors.colorAccent)
|
||||||
|
|
||||||
@ -114,6 +121,7 @@ class ItemCardAdapter(
|
|||||||
mView.favButton.setOnLikeListener(object : OnLikeListener {
|
mView.favButton.setOnLikeListener(object : OnLikeListener {
|
||||||
override fun liked(likeButton: LikeButton) {
|
override fun liked(likeButton: LikeButton) {
|
||||||
val (id) = items[adapterPosition]
|
val (id) = items[adapterPosition]
|
||||||
|
if (c.isNetworkAccessible(null)) {
|
||||||
api.starrItem(id).enqueue(object : Callback<SuccessResponse> {
|
api.starrItem(id).enqueue(object : Callback<SuccessResponse> {
|
||||||
override fun onResponse(
|
override fun onResponse(
|
||||||
call: Call<SuccessResponse>,
|
call: Call<SuccessResponse>,
|
||||||
@ -133,10 +141,16 @@ class ItemCardAdapter(
|
|||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
thread {
|
||||||
|
db.actionsDao().insertAllActions(ActionEntity(id, false, false, true, false))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun unLiked(likeButton: LikeButton) {
|
override fun unLiked(likeButton: LikeButton) {
|
||||||
val (id) = items[adapterPosition]
|
val (id) = items[adapterPosition]
|
||||||
|
if (c.isNetworkAccessible(null)) {
|
||||||
api.unstarrItem(id).enqueue(object : Callback<SuccessResponse> {
|
api.unstarrItem(id).enqueue(object : Callback<SuccessResponse> {
|
||||||
override fun onResponse(
|
override fun onResponse(
|
||||||
call: Call<SuccessResponse>,
|
call: Call<SuccessResponse>,
|
||||||
@ -156,11 +170,17 @@ class ItemCardAdapter(
|
|||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
thread {
|
||||||
|
db.actionsDao().insertAllActions(ActionEntity(id, false, false, false, true))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
mView.shareBtn.setOnClickListener {
|
mView.shareBtn.setOnClickListener {
|
||||||
c.shareLink(items[adapterPosition].getLinkDecoded())
|
val item = items[adapterPosition]
|
||||||
|
c.shareLink(item.getLinkDecoded(), item.title)
|
||||||
}
|
}
|
||||||
|
|
||||||
mView.browserBtn.setOnClickListener {
|
mView.browserBtn.setOnClickListener {
|
||||||
|
@ -2,19 +2,25 @@ package apps.amine.bou.readerforselfoss.adapters
|
|||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.support.constraint.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import android.support.v7.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import android.text.Html
|
import android.text.Html
|
||||||
|
import android.text.Spannable
|
||||||
|
import android.text.style.ClickableSpan
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import apps.amine.bou.readerforselfoss.R
|
import apps.amine.bou.readerforselfoss.R
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||||
|
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
||||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
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.buildCustomTabsIntent
|
||||||
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
||||||
import apps.amine.bou.readerforselfoss.utils.glide.bitmapCenterCrop
|
import apps.amine.bou.readerforselfoss.utils.glide.bitmapCenterCrop
|
||||||
@ -39,8 +45,8 @@ class ItemListAdapter(
|
|||||||
override val app: Activity,
|
override val app: Activity,
|
||||||
override var items: ArrayList<Item>,
|
override var items: ArrayList<Item>,
|
||||||
override val api: SelfossApi,
|
override val api: SelfossApi,
|
||||||
|
override val db: AppDatabase,
|
||||||
private val helper: CustomTabActivityHelper,
|
private val helper: CustomTabActivityHelper,
|
||||||
private val clickBehavior: Boolean,
|
|
||||||
private val internalBrowser: Boolean,
|
private val internalBrowser: Boolean,
|
||||||
private val articleViewer: Boolean,
|
private val articleViewer: Boolean,
|
||||||
override val debugReadingItems: Boolean,
|
override val debugReadingItems: Boolean,
|
||||||
@ -50,7 +56,6 @@ class ItemListAdapter(
|
|||||||
) : ItemsAdapter<ItemListAdapter.ViewHolder>() {
|
) : ItemsAdapter<ItemListAdapter.ViewHolder>() {
|
||||||
private val generator: ColorGenerator = ColorGenerator.MATERIAL
|
private val generator: ColorGenerator = ColorGenerator.MATERIAL
|
||||||
private val c: Context = app.baseContext
|
private val c: Context = app.baseContext
|
||||||
private val bars: ArrayList<Boolean> = ArrayList(Collections.nCopies(items.size + 1, false))
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
val v = LayoutInflater.from(c).inflate(
|
val v = LayoutInflater.from(c).inflate(
|
||||||
@ -67,6 +72,8 @@ class ItemListAdapter(
|
|||||||
|
|
||||||
holder.mView.title.text = Html.fromHtml(itm.title)
|
holder.mView.title.text = Html.fromHtml(itm.title)
|
||||||
|
|
||||||
|
holder.mView.title.setOnTouchListener(LinkOnTouchListener())
|
||||||
|
|
||||||
holder.mView.title.setLinkTextColor(appColors.colorAccent)
|
holder.mView.title.setLinkTextColor(appColors.colorAccent)
|
||||||
|
|
||||||
holder.mView.sourceTitleAndDate.text = itm.sourceAndDateText()
|
holder.mView.sourceTitleAndDate.text = itm.sourceAndDateText()
|
||||||
@ -106,19 +113,6 @@ class ItemListAdapter(
|
|||||||
} else {
|
} else {
|
||||||
c.bitmapCenterCrop(itm.getThumbnail(c), holder.mView.itemImage)
|
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
|
override fun getItemCount(): Int = items.size
|
||||||
@ -126,76 +120,13 @@ class ItemListAdapter(
|
|||||||
inner class ViewHolder(val mView: ConstraintLayout) : RecyclerView.ViewHolder(mView) {
|
inner class ViewHolder(val mView: ConstraintLayout) : RecyclerView.ViewHolder(mView) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
handleClickListeners()
|
|
||||||
handleCustomTabActions()
|
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() {
|
private fun handleCustomTabActions() {
|
||||||
val customTabsIntent = c.buildCustomTabsIntent()
|
val customTabsIntent = c.buildCustomTabsIntent()
|
||||||
helper.bindCustomTabsService(app)
|
helper.bindCustomTabsService(app)
|
||||||
|
|
||||||
|
|
||||||
if (!clickBehavior) {
|
|
||||||
mView.setOnClickListener {
|
mView.setOnClickListener {
|
||||||
c.openItemUrl(
|
c.openItemUrl(
|
||||||
items,
|
items,
|
||||||
@ -207,34 +138,6 @@ class ItemListAdapter(
|
|||||||
app
|
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.app.Activity
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.support.design.widget.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import android.support.v7.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import apps.amine.bou.readerforselfoss.R
|
import apps.amine.bou.readerforselfoss.R
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||||
|
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
||||||
|
import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity
|
||||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||||
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
|
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.succeeded
|
||||||
import org.acra.ACRA
|
import org.acra.ACRA
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.Callback
|
import retrofit2.Callback
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapter<VH>() {
|
abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapter<VH>() {
|
||||||
abstract var items: ArrayList<Item>
|
abstract var items: ArrayList<Item>
|
||||||
abstract val api: SelfossApi
|
abstract val api: SelfossApi
|
||||||
|
abstract val db: AppDatabase
|
||||||
abstract val debugReadingItems: Boolean
|
abstract val debugReadingItems: Boolean
|
||||||
abstract val userIdentifier: String
|
abstract val userIdentifier: String
|
||||||
abstract val app: Activity
|
abstract val app: Activity
|
||||||
@ -42,9 +48,13 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
|
|||||||
)
|
)
|
||||||
.setAction(R.string.undo_string) {
|
.setAction(R.string.undo_string) {
|
||||||
items.add(position, i)
|
items.add(position, i)
|
||||||
|
thread {
|
||||||
|
db.itemsDao().insertAllItems(i.toEntity())
|
||||||
|
}
|
||||||
notifyItemInserted(position)
|
notifyItemInserted(position)
|
||||||
updateItems(items)
|
updateItems(items)
|
||||||
|
|
||||||
|
if (app.isNetworkAccessible(null)) {
|
||||||
api.unmarkItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
api.unmarkItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
||||||
override fun onResponse(
|
override fun onResponse(
|
||||||
call: Call<SuccessResponse>,
|
call: Call<SuccessResponse>,
|
||||||
@ -54,28 +64,38 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
|
|||||||
|
|
||||||
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||||
items.remove(i)
|
items.remove(i)
|
||||||
|
thread {
|
||||||
|
db.itemsDao().delete(i.toEntity())
|
||||||
|
}
|
||||||
notifyItemRemoved(position)
|
notifyItemRemoved(position)
|
||||||
updateItems(items)
|
updateItems(items)
|
||||||
doUnmark(i, position)
|
doUnmark(i, position)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
thread {
|
||||||
|
db.actionsDao().deleteReadActionForArticle(i.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val view = s.view
|
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)
|
tv.setTextColor(Color.WHITE)
|
||||||
s.show()
|
s.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeItemAtIndex(position: Int) {
|
fun removeItemAtIndex(position: Int) {
|
||||||
|
|
||||||
val i = items[position]
|
val i = items[position]
|
||||||
|
|
||||||
items.remove(i)
|
items.remove(i)
|
||||||
notifyItemRemoved(position)
|
notifyItemRemoved(position)
|
||||||
updateItems(items)
|
updateItems(items)
|
||||||
|
|
||||||
|
thread {
|
||||||
|
db.itemsDao().delete(i.toEntity())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app.isNetworkAccessible(null)) {
|
||||||
api.markItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
api.markItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
||||||
override fun onResponse(
|
override fun onResponse(
|
||||||
call: Call<SuccessResponse>,
|
call: Call<SuccessResponse>,
|
||||||
@ -93,6 +113,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
|
|||||||
ACRA.getErrorReporter().maybeHandleSilentException(Exception(message), app)
|
ACRA.getErrorReporter().maybeHandleSilentException(Exception(message), app)
|
||||||
Toast.makeText(app.baseContext, message, Toast.LENGTH_LONG).show()
|
Toast.makeText(app.baseContext, message, Toast.LENGTH_LONG).show()
|
||||||
}
|
}
|
||||||
|
|
||||||
doUnmark(i, position)
|
doUnmark(i, position)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -110,8 +131,17 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
|
|||||||
notifyItemInserted(position)
|
notifyItemInserted(position)
|
||||||
updateItems(items)
|
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) {
|
fun addItemAtIndex(item: Item, position: Int) {
|
||||||
|
@ -2,17 +2,18 @@ package apps.amine.bou.readerforselfoss.adapters
|
|||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.support.constraint.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import android.support.v7.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import apps.amine.bou.readerforselfoss.R
|
import apps.amine.bou.readerforselfoss.R
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.Sources
|
import apps.amine.bou.readerforselfoss.api.selfoss.Source
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||||
import apps.amine.bou.readerforselfoss.utils.glide.circularBitmapDrawable
|
import apps.amine.bou.readerforselfoss.utils.glide.circularBitmapDrawable
|
||||||
|
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
||||||
import apps.amine.bou.readerforselfoss.utils.toTextDrawableString
|
import apps.amine.bou.readerforselfoss.utils.toTextDrawableString
|
||||||
import com.amulyakhare.textdrawable.TextDrawable
|
import com.amulyakhare.textdrawable.TextDrawable
|
||||||
import com.amulyakhare.textdrawable.util.ColorGenerator
|
import com.amulyakhare.textdrawable.util.ColorGenerator
|
||||||
@ -23,7 +24,7 @@ import retrofit2.Response
|
|||||||
|
|
||||||
class SourcesListAdapter(
|
class SourcesListAdapter(
|
||||||
private val app: Activity,
|
private val app: Activity,
|
||||||
private val items: ArrayList<Sources>,
|
private val items: ArrayList<Source>,
|
||||||
private val api: SelfossApi
|
private val api: SelfossApi
|
||||||
) : RecyclerView.Adapter<SourcesListAdapter.ViewHolder>() {
|
) : RecyclerView.Adapter<SourcesListAdapter.ViewHolder>() {
|
||||||
private val c: Context = app.baseContext
|
private val c: Context = app.baseContext
|
||||||
@ -70,6 +71,7 @@ class SourcesListAdapter(
|
|||||||
val deleteBtn: Button = mView.findViewById(R.id.deleteBtn)
|
val deleteBtn: Button = mView.findViewById(R.id.deleteBtn)
|
||||||
|
|
||||||
deleteBtn.setOnClickListener {
|
deleteBtn.setOnClickListener {
|
||||||
|
if (c.isNetworkAccessible(null)) {
|
||||||
val (id) = items[adapterPosition]
|
val (id) = items[adapterPosition]
|
||||||
api.deleteSource(id).enqueue(object : Callback<SuccessResponse> {
|
api.deleteSource(id).enqueue(object : Callback<SuccessResponse> {
|
||||||
override fun onResponse(
|
override fun onResponse(
|
||||||
@ -100,4 +102,5 @@ class SourcesListAdapter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,11 +18,13 @@ import retrofit2.Call
|
|||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
import retrofit2.converter.gson.GsonConverterFactory
|
import retrofit2.converter.gson.GsonConverterFactory
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
import java.util.concurrent.ConcurrentHashMap
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class SelfossApi(
|
class SelfossApi(
|
||||||
c: Context,
|
c: Context,
|
||||||
callingActivity: Activity,
|
callingActivity: Activity?,
|
||||||
isWithSelfSignedCert: Boolean,
|
isWithSelfSignedCert: Boolean,
|
||||||
|
timeout: Long,
|
||||||
shouldLog: Boolean
|
shouldLog: Boolean
|
||||||
) {
|
) {
|
||||||
|
|
||||||
@ -38,16 +40,25 @@ class SelfossApi(
|
|||||||
this
|
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 =
|
fun Credentials.createAuthenticator(): DispatchingAuthenticator =
|
||||||
DispatchingAuthenticator.Builder()
|
DispatchingAuthenticator.Builder()
|
||||||
.with("digest", DigestAuthenticator(this))
|
.with("digest", DigestAuthenticator(this))
|
||||||
.with("basic", BasicAuthenticator(this))
|
.with("basic", BasicAuthenticator(this))
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
fun DispatchingAuthenticator.getHttpClien(isWithSelfSignedCert: Boolean): OkHttpClient.Builder {
|
fun DispatchingAuthenticator.getHttpClien(isWithSelfSignedCert: Boolean, timeout: Long): OkHttpClient.Builder {
|
||||||
val authCache = ConcurrentHashMap<String, CachingAuthenticator>()
|
val authCache = ConcurrentHashMap<String, CachingAuthenticator>()
|
||||||
return OkHttpClient
|
return OkHttpClient
|
||||||
.Builder()
|
.Builder()
|
||||||
|
.maybeWithSettingsTimeout(timeout)
|
||||||
.maybeWithSelfSigned(isWithSelfSignedCert)
|
.maybeWithSelfSigned(isWithSelfSignedCert)
|
||||||
.authenticator(CachingAuthenticatorDecorator(this, authCache))
|
.authenticator(CachingAuthenticatorDecorator(this, authCache))
|
||||||
.addInterceptor(AuthenticationCacheInterceptor(authCache))
|
.addInterceptor(AuthenticationCacheInterceptor(authCache))
|
||||||
@ -66,6 +77,7 @@ class SelfossApi(
|
|||||||
val gson =
|
val gson =
|
||||||
GsonBuilder()
|
GsonBuilder()
|
||||||
.registerTypeAdapter(Boolean::class.javaPrimitiveType, BooleanTypeAdapter())
|
.registerTypeAdapter(Boolean::class.javaPrimitiveType, BooleanTypeAdapter())
|
||||||
|
.registerTypeAdapter(SelfossTagType::class.java, SelfossTagTypeTypeAdapter())
|
||||||
.setLenient()
|
.setLenient()
|
||||||
.create()
|
.create()
|
||||||
|
|
||||||
@ -77,7 +89,7 @@ class SelfossApi(
|
|||||||
HttpLoggingInterceptor.Level.NONE
|
HttpLoggingInterceptor.Level.NONE
|
||||||
}
|
}
|
||||||
|
|
||||||
val httpClient = authenticator.getHttpClien(isWithSelfSignedCert)
|
val httpClient = authenticator.getHttpClien(isWithSelfSignedCert, timeout)
|
||||||
|
|
||||||
httpClient.addInterceptor(logging)
|
httpClient.addInterceptor(logging)
|
||||||
|
|
||||||
@ -91,9 +103,11 @@ class SelfossApi(
|
|||||||
.build()
|
.build()
|
||||||
service = retrofit.create(SelfossService::class.java)
|
service = retrofit.create(SelfossService::class.java)
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
|
if (callingActivity != null) {
|
||||||
Config.logoutAndRedirect(c, callingActivity, config.settings.edit(), baseUrlFail = true)
|
Config.logoutAndRedirect(c, callingActivity, config.settings.edit(), baseUrlFail = true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun login(): Call<SuccessResponse> =
|
fun login(): Call<SuccessResponse> =
|
||||||
service.loginToSelfoss(config.userLogin, config.userPassword)
|
service.loginToSelfoss(config.userLogin, config.userPassword)
|
||||||
@ -125,6 +139,9 @@ class SelfossApi(
|
|||||||
): Call<List<Item>> =
|
): Call<List<Item>> =
|
||||||
getItems("starred", tag, sourceId, search, itemsNumber, offset)
|
getItems("starred", tag, sourceId, search, itemsNumber, offset)
|
||||||
|
|
||||||
|
fun allItems(): Call<List<Item>> =
|
||||||
|
service.allItems(userName, password)
|
||||||
|
|
||||||
private fun getItems(
|
private fun getItems(
|
||||||
type: String,
|
type: String,
|
||||||
tag: String?,
|
tag: String?,
|
||||||
@ -159,7 +176,7 @@ class SelfossApi(
|
|||||||
fun update(): Call<String> =
|
fun update(): Call<String> =
|
||||||
service.update(userName, password)
|
service.update(userName, password)
|
||||||
|
|
||||||
val sources: Call<List<Sources>>
|
val sources: Call<List<Source>>
|
||||||
get() = service.sources(userName, password)
|
get() = service.sources(userName, password)
|
||||||
|
|
||||||
fun deleteSource(id: String): Call<SuccessResponse> =
|
fun deleteSource(id: String): Call<SuccessResponse> =
|
||||||
|
@ -9,13 +9,13 @@ import apps.amine.bou.readerforselfoss.utils.Config
|
|||||||
import apps.amine.bou.readerforselfoss.utils.isEmptyOrNullOrNullString
|
import apps.amine.bou.readerforselfoss.utils.isEmptyOrNullOrNullString
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
private fun constructUrl(config: Config?, path: String, file: String): String {
|
private fun constructUrl(config: Config?, path: String, file: String?): String {
|
||||||
val baseUriBuilder = Uri.parse(config!!.baseUrl).buildUpon()
|
|
||||||
baseUriBuilder.appendPath(path).appendPath(file)
|
|
||||||
|
|
||||||
return if (file.isEmptyOrNullOrNullString()) {
|
return if (file.isEmptyOrNullOrNullString()) {
|
||||||
""
|
""
|
||||||
} else {
|
} else {
|
||||||
|
val baseUriBuilder = Uri.parse(config!!.baseUrl).buildUpon()
|
||||||
|
baseUriBuilder.appendPath(path).appendPath(file)
|
||||||
|
|
||||||
baseUriBuilder.toString()
|
baseUriBuilder.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -42,10 +42,10 @@ data class Spout(
|
|||||||
@SerializedName("description") val description: String
|
@SerializedName("description") val description: String
|
||||||
)
|
)
|
||||||
|
|
||||||
data class Sources(
|
data class Source(
|
||||||
@SerializedName("id") val id: String,
|
@SerializedName("id") val id: String,
|
||||||
@SerializedName("title") val title: String,
|
@SerializedName("title") val title: String,
|
||||||
@SerializedName("tags") val tags: String,
|
@SerializedName("tags") val tags: SelfossTagType,
|
||||||
@SerializedName("spout") val spout: String,
|
@SerializedName("spout") val spout: String,
|
||||||
@SerializedName("error") val error: String,
|
@SerializedName("error") val error: String,
|
||||||
@SerializedName("icon") val icon: String
|
@SerializedName("icon") val icon: String
|
||||||
@ -71,7 +71,7 @@ data class Item(
|
|||||||
@SerializedName("icon") val icon: String,
|
@SerializedName("icon") val icon: String,
|
||||||
@SerializedName("link") val link: String,
|
@SerializedName("link") val link: String,
|
||||||
@SerializedName("sourcetitle") val sourcetitle: String,
|
@SerializedName("sourcetitle") val sourcetitle: String,
|
||||||
@SerializedName("tags") val tags: String
|
@SerializedName("tags") val tags: SelfossTagType
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|
||||||
var config: Config? = null
|
var config: Config? = null
|
||||||
@ -94,7 +94,7 @@ data class Item(
|
|||||||
icon = source.readString(),
|
icon = source.readString(),
|
||||||
link = source.readString(),
|
link = source.readString(),
|
||||||
sourcetitle = source.readString(),
|
sourcetitle = source.readString(),
|
||||||
tags = source.readString()
|
tags = source.readParcelable(ClassLoader.getSystemClassLoader())
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun describeContents() = 0
|
override fun describeContents() = 0
|
||||||
@ -110,7 +110,7 @@ data class Item(
|
|||||||
dest.writeString(icon)
|
dest.writeString(icon)
|
||||||
dest.writeString(link)
|
dest.writeString(link)
|
||||||
dest.writeString(sourcetitle)
|
dest.writeString(sourcetitle)
|
||||||
dest.writeString(tags)
|
dest.writeParcelable(tags, flags)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getIcon(app: Context): String {
|
fun getIcon(app: Context): String {
|
||||||
@ -154,3 +154,26 @@ data class Item(
|
|||||||
return stringUrl
|
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
|
@Query("offset") offset: Int
|
||||||
): Call<List<Item>>
|
): 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")
|
@Headers("Content-Type: application/x-www-form-urlencoded")
|
||||||
@POST("mark/{id}")
|
@POST("mark/{id}")
|
||||||
fun markAsRead(
|
fun markAsRead(
|
||||||
@ -95,7 +101,7 @@ internal interface SelfossService {
|
|||||||
fun sources(
|
fun sources(
|
||||||
@Query("username") username: String,
|
@Query("username") username: String,
|
||||||
@Query("password") password: String
|
@Query("password") password: String
|
||||||
): Call<List<Sources>>
|
): Call<List<Source>>
|
||||||
|
|
||||||
@DELETE("source/{id}")
|
@DELETE("source/{id}")
|
||||||
fun deleteSource(
|
fun deleteSource(
|
||||||
|
@ -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,42 +1,46 @@
|
|||||||
package apps.amine.bou.readerforselfoss.fragments
|
package apps.amine.bou.readerforselfoss.fragments
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
import android.support.customtabs.CustomTabsIntent
|
import android.view.InflateException
|
||||||
import android.support.design.widget.FloatingActionButton
|
import androidx.browser.customtabs.CustomTabsIntent
|
||||||
import android.support.v4.app.Fragment
|
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
import android.support.v4.content.ContextCompat
|
import androidx.fragment.app.Fragment
|
||||||
import android.support.v4.widget.NestedScrollView
|
import androidx.core.content.ContextCompat
|
||||||
import android.support.v7.app.AlertDialog
|
import androidx.core.widget.NestedScrollView
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.webkit.WebSettings
|
import android.webkit.WebSettings
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.room.Room
|
||||||
import apps.amine.bou.readerforselfoss.R
|
import apps.amine.bou.readerforselfoss.R
|
||||||
import apps.amine.bou.readerforselfoss.api.mercury.MercuryApi
|
import apps.amine.bou.readerforselfoss.api.mercury.MercuryApi
|
||||||
import apps.amine.bou.readerforselfoss.api.mercury.ParsedContent
|
import apps.amine.bou.readerforselfoss.api.mercury.ParsedContent
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
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.themes.AppColors
|
||||||
import apps.amine.bou.readerforselfoss.utils.Config
|
import apps.amine.bou.readerforselfoss.utils.Config
|
||||||
import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent
|
import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent
|
||||||
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
||||||
import apps.amine.bou.readerforselfoss.utils.isEmptyOrNullOrNullString
|
import apps.amine.bou.readerforselfoss.utils.isEmptyOrNullOrNullString
|
||||||
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
|
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
|
||||||
|
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
||||||
import apps.amine.bou.readerforselfoss.utils.openItemUrl
|
import apps.amine.bou.readerforselfoss.utils.openItemUrl
|
||||||
import apps.amine.bou.readerforselfoss.utils.shareLink
|
import apps.amine.bou.readerforselfoss.utils.shareLink
|
||||||
import apps.amine.bou.readerforselfoss.utils.sourceAndDateText
|
import apps.amine.bou.readerforselfoss.utils.sourceAndDateText
|
||||||
import apps.amine.bou.readerforselfoss.utils.succeeded
|
import apps.amine.bou.readerforselfoss.utils.succeeded
|
||||||
import apps.amine.bou.readerforselfoss.utils.toPx
|
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.request.RequestOptions
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import com.github.rubensousa.floatingtoolbar.FloatingToolbar
|
import com.github.rubensousa.floatingtoolbar.FloatingToolbar
|
||||||
@ -47,25 +51,29 @@ import retrofit2.Callback
|
|||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import java.net.MalformedURLException
|
import java.net.MalformedURLException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
class ArticleFragment : Fragment() {
|
class ArticleFragment : Fragment() {
|
||||||
private lateinit var pageNumber: Number
|
private lateinit var pageNumber: Number
|
||||||
private var fontSize: Int = 14
|
private var fontSize: Int = 16
|
||||||
private lateinit var allItems: ArrayList<Item>
|
private lateinit var allItems: ArrayList<Item>
|
||||||
private lateinit var mCustomTabActivityHelper: CustomTabActivityHelper
|
private var mCustomTabActivityHelper: CustomTabActivityHelper? = null;
|
||||||
private lateinit var url: String
|
private lateinit var url: String
|
||||||
private lateinit var contentText: String
|
private lateinit var contentText: String
|
||||||
private lateinit var contentSource: String
|
private lateinit var contentSource: String
|
||||||
private lateinit var contentImage: String
|
private lateinit var contentImage: String
|
||||||
private lateinit var contentTitle: String
|
private lateinit var contentTitle: String
|
||||||
private var showMalformedUrl: Boolean = false
|
|
||||||
private lateinit var editor: SharedPreferences.Editor
|
private lateinit var editor: SharedPreferences.Editor
|
||||||
private lateinit var fab: FloatingActionButton
|
private lateinit var fab: FloatingActionButton
|
||||||
private lateinit var appColors: AppColors
|
private lateinit var appColors: AppColors
|
||||||
|
private lateinit var db: AppDatabase
|
||||||
|
private lateinit var textAlignment: String
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
super.onStop()
|
super.onStop()
|
||||||
mCustomTabActivityHelper.unbindCustomTabsService(activity)
|
if (mCustomTabActivityHelper != null) {
|
||||||
|
mCustomTabActivityHelper!!.unbindCustomTabsService(activity)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
@ -75,16 +83,23 @@ class ArticleFragment : Fragment() {
|
|||||||
|
|
||||||
pageNumber = arguments!!.getInt(ARG_POSITION)
|
pageNumber = arguments!!.getInt(ARG_POSITION)
|
||||||
allItems = arguments!!.getParcelableArrayList(ARG_ITEMS)
|
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
|
private var rootView: ViewGroup? = null
|
||||||
|
|
||||||
|
private lateinit var prefs: SharedPreferences
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View? {
|
): View? {
|
||||||
|
try {
|
||||||
rootView = inflater
|
rootView = inflater
|
||||||
.inflate(R.layout.fragment_article, container, false) as ViewGroup
|
.inflate(R.layout.fragment_article, container, false) as ViewGroup
|
||||||
|
|
||||||
@ -94,10 +109,10 @@ class ArticleFragment : Fragment() {
|
|||||||
contentImage = allItems[pageNumber.toInt()].getThumbnail(activity!!)
|
contentImage = allItems[pageNumber.toInt()].getThumbnail(activity!!)
|
||||||
contentSource = allItems[pageNumber.toInt()].sourceAndDateText()
|
contentSource = allItems[pageNumber.toInt()].sourceAndDateText()
|
||||||
|
|
||||||
val prefs = PreferenceManager.getDefaultSharedPreferences(activity)
|
prefs = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
editor = prefs.edit()
|
editor = prefs.edit()
|
||||||
fontSize = prefs.getString("reader_font_size", "14").toInt()
|
fontSize = prefs.getString("reader_font_size", "16").toInt()
|
||||||
showMalformedUrl = prefs.getBoolean("show_error_malformed_url", true)
|
refreshAlignment()
|
||||||
|
|
||||||
val settings = activity!!.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
val settings = activity!!.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||||
val debugReadingItems = prefs.getBoolean("read_debug", false)
|
val debugReadingItems = prefs.getBoolean("read_debug", false)
|
||||||
@ -106,23 +121,24 @@ class ArticleFragment : Fragment() {
|
|||||||
context!!,
|
context!!,
|
||||||
activity!!,
|
activity!!,
|
||||||
settings.getBoolean("isSelfSignedCert", false),
|
settings.getBoolean("isSelfSignedCert", false),
|
||||||
|
prefs.getString("api_timeout", "-1").toLong(),
|
||||||
prefs.getBoolean("should_log_everything", false)
|
prefs.getBoolean("should_log_everything", false)
|
||||||
)
|
)
|
||||||
|
|
||||||
fab = rootView.fab
|
fab = rootView!!.fab
|
||||||
|
|
||||||
fab.backgroundTintList = ColorStateList.valueOf(appColors.colorAccent)
|
fab.backgroundTintList = ColorStateList.valueOf(appColors.colorAccent)
|
||||||
|
|
||||||
fab.rippleColor = appColors.colorAccentDark
|
fab.rippleColor = appColors.colorAccentDark
|
||||||
|
|
||||||
val floatingToolbar: FloatingToolbar = rootView.floatingToolbar
|
val floatingToolbar: FloatingToolbar = rootView!!.floatingToolbar
|
||||||
floatingToolbar.attachFab(fab)
|
floatingToolbar.attachFab(fab)
|
||||||
|
|
||||||
floatingToolbar.background = ColorDrawable(appColors.colorAccent)
|
floatingToolbar.background = ColorDrawable(appColors.colorAccent)
|
||||||
|
|
||||||
val customTabsIntent = activity!!.buildCustomTabsIntent()
|
val customTabsIntent = activity!!.buildCustomTabsIntent()
|
||||||
mCustomTabActivityHelper = CustomTabActivityHelper()
|
mCustomTabActivityHelper = CustomTabActivityHelper()
|
||||||
mCustomTabActivityHelper.bindCustomTabsService(activity)
|
mCustomTabActivityHelper!!.bindCustomTabsService(activity)
|
||||||
|
|
||||||
|
|
||||||
floatingToolbar.setClickListener(
|
floatingToolbar.setClickListener(
|
||||||
@ -130,7 +146,7 @@ class ArticleFragment : Fragment() {
|
|||||||
override fun onItemClick(item: MenuItem) {
|
override fun onItemClick(item: MenuItem) {
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.more_action -> getContentFromMercury(customTabsIntent, prefs)
|
R.id.more_action -> getContentFromMercury(customTabsIntent, prefs)
|
||||||
R.id.share_action -> activity!!.shareLink(url)
|
R.id.share_action -> activity!!.shareLink(url, contentTitle)
|
||||||
R.id.open_action -> activity!!.openItemUrl(
|
R.id.open_action -> activity!!.openItemUrl(
|
||||||
allItems,
|
allItems,
|
||||||
pageNumber.toInt(),
|
pageNumber.toInt(),
|
||||||
@ -140,7 +156,8 @@ class ArticleFragment : Fragment() {
|
|||||||
false,
|
false,
|
||||||
activity!!
|
activity!!
|
||||||
)
|
)
|
||||||
R.id.unread_action -> api.unmarkItem(allItems[pageNumber.toInt()].id).enqueue(
|
R.id.unread_action -> if ((context != null && context!!.isNetworkAccessible(null)) || context == null) {
|
||||||
|
api.unmarkItem(allItems[pageNumber.toInt()].id).enqueue(
|
||||||
object : Callback<SuccessResponse> {
|
object : Callback<SuccessResponse> {
|
||||||
override fun onResponse(
|
override fun onResponse(
|
||||||
call: Call<SuccessResponse>,
|
call: Call<SuccessResponse>,
|
||||||
@ -169,6 +186,11 @@ class ArticleFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
thread {
|
||||||
|
db.actionsDao().insertAllActions(ActionEntity(allItems[pageNumber.toInt()].id, false, true, false, false))
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> Unit
|
else -> Unit
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,29 +200,29 @@ class ArticleFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
rootView.source.text = contentSource
|
rootView!!.source.text = contentSource
|
||||||
|
|
||||||
if (contentText.isEmptyOrNullOrNullString()) {
|
if (contentText.isEmptyOrNullOrNullString()) {
|
||||||
getContentFromMercury(customTabsIntent, prefs)
|
getContentFromMercury(customTabsIntent, prefs)
|
||||||
} else {
|
} else {
|
||||||
rootView.titleView.text = contentTitle
|
rootView!!.titleView.text = contentTitle
|
||||||
|
|
||||||
htmlToWebview(contentText, prefs)
|
htmlToWebview()
|
||||||
|
|
||||||
if (!contentImage.isEmptyOrNullOrNullString() && context != null) {
|
if (!contentImage.isEmptyOrNullOrNullString() && context != null) {
|
||||||
rootView.imageView.visibility = View.VISIBLE
|
rootView!!.imageView.visibility = View.VISIBLE
|
||||||
Glide
|
Glide
|
||||||
.with(context!!)
|
.with(context!!)
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
.load(contentImage)
|
.load(contentImage)
|
||||||
.apply(RequestOptions.fitCenterTransform())
|
.apply(RequestOptions.fitCenterTransform())
|
||||||
.into(rootView.imageView)
|
.into(rootView!!.imageView)
|
||||||
} else {
|
} else {
|
||||||
rootView.imageView.visibility = View.GONE
|
rootView!!.imageView.visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rootView.nestedScrollView.setOnScrollChangeListener(
|
rootView!!.nestedScrollView.setOnScrollChangeListener(
|
||||||
NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
|
NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
|
||||||
if (scrollY > oldScrollY) {
|
if (scrollY > oldScrollY) {
|
||||||
fab.hide()
|
fab.hide()
|
||||||
@ -210,14 +232,39 @@ class ArticleFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
} catch (e: InflateException) {
|
||||||
|
AlertDialog.Builder(context!!)
|
||||||
|
.setMessage(context!!.getString(R.string.webview_dialog_issue_message))
|
||||||
|
.setTitle(context!!.getString(R.string.webview_dialog_issue_title))
|
||||||
|
.setPositiveButton(android.R.string.ok
|
||||||
|
) { dialog, which ->
|
||||||
|
val sharedPref = PreferenceManager.getDefaultSharedPreferences(context!!)
|
||||||
|
val editor = sharedPref.edit()
|
||||||
|
editor.putBoolean("prefer_article_viewer", false)
|
||||||
|
editor.commit()
|
||||||
|
activity!!.finish()
|
||||||
|
}
|
||||||
|
.create()
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
return rootView
|
return rootView
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun refreshAlignment() {
|
||||||
|
textAlignment = when (prefs.getInt("text_align", 1)) {
|
||||||
|
1 -> "justify"
|
||||||
|
2 -> "left"
|
||||||
|
else -> "justify"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun getContentFromMercury(
|
private fun getContentFromMercury(
|
||||||
customTabsIntent: CustomTabsIntent,
|
customTabsIntent: CustomTabsIntent,
|
||||||
prefs: SharedPreferences
|
prefs: SharedPreferences
|
||||||
) {
|
) {
|
||||||
rootView.progressBar.visibility = View.VISIBLE
|
if ((context != null && context!!.isNetworkAccessible(null)) || context == null) {
|
||||||
|
rootView!!.progressBar.visibility = View.VISIBLE
|
||||||
val parser = MercuryApi(
|
val parser = MercuryApi(
|
||||||
prefs.getBoolean("should_log_everything", false)
|
prefs.getBoolean("should_log_everything", false)
|
||||||
)
|
)
|
||||||
@ -232,8 +279,14 @@ class ArticleFragment : Fragment() {
|
|||||||
try {
|
try {
|
||||||
if (response.body() != null && response.body()!!.content != null && !response.body()!!.content.isNullOrEmpty()) {
|
if (response.body() != null && response.body()!!.content != null && !response.body()!!.content.isNullOrEmpty()) {
|
||||||
try {
|
try {
|
||||||
rootView.titleView.text = response.body()!!.title
|
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
|
url = response.body()!!.url
|
||||||
|
} catch (e: MalformedURLException) {
|
||||||
|
// Mercury returned a relative url. We do nothing.
|
||||||
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
|
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
|
||||||
@ -241,7 +294,8 @@ class ArticleFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
htmlToWebview(response.body()!!.content.orEmpty(), prefs)
|
contentText = response.body()!!.content.orEmpty()
|
||||||
|
htmlToWebview()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
|
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
|
||||||
@ -250,19 +304,19 @@ class ArticleFragment : Fragment() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
if (response.body()!!.lead_image_url != null && !response.body()!!.lead_image_url.isNullOrEmpty() && context != null) {
|
if (response.body()!!.lead_image_url != null && !response.body()!!.lead_image_url.isNullOrEmpty() && context != null) {
|
||||||
rootView.imageView.visibility = View.VISIBLE
|
rootView!!.imageView.visibility = View.VISIBLE
|
||||||
try {
|
try {
|
||||||
Glide
|
Glide
|
||||||
.with(context!!)
|
.with(context!!)
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
.load(response.body()!!.lead_image_url)
|
.load(response.body()!!.lead_image_url)
|
||||||
.apply(RequestOptions.fitCenterTransform())
|
.apply(RequestOptions.fitCenterTransform())
|
||||||
.into(rootView.imageView)
|
.into(rootView!!.imageView)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
|
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
rootView.imageView.visibility = View.GONE
|
rootView!!.imageView.visibility = View.GONE
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
@ -271,9 +325,9 @@ class ArticleFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
rootView.nestedScrollView.scrollTo(0, 0)
|
rootView!!.nestedScrollView.scrollTo(0, 0)
|
||||||
|
|
||||||
rootView.progressBar.visibility = View.GONE
|
rootView!!.progressBar.visibility = View.GONE
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
|
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
|
||||||
@ -302,14 +356,15 @@ class ArticleFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun htmlToWebview(c: String, prefs: SharedPreferences) {
|
private fun htmlToWebview() {
|
||||||
val stringColor = String.format("#%06X", 0xFFFFFF and appColors.colorAccent)
|
val stringColor = String.format("#%06X", 0xFFFFFF and appColors.colorAccent)
|
||||||
|
|
||||||
rootView.webcontent.visibility = View.VISIBLE
|
rootView!!.webcontent.visibility = View.VISIBLE
|
||||||
val (textColor, backgroundColor) = if (appColors.isDarkTheme) {
|
val (textColor, backgroundColor) = if (appColors.isDarkTheme) {
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
rootView.webcontent.setBackgroundColor(
|
rootView!!.webcontent.setBackgroundColor(
|
||||||
ContextCompat.getColor(
|
ContextCompat.getColor(
|
||||||
context!!,
|
context!!,
|
||||||
R.color.dark_webview
|
R.color.dark_webview
|
||||||
@ -321,7 +376,7 @@ class ArticleFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
rootView.webcontent.setBackgroundColor(
|
rootView!!.webcontent.setBackgroundColor(
|
||||||
ContextCompat.getColor(
|
ContextCompat.getColor(
|
||||||
context!!,
|
context!!,
|
||||||
R.color.light_webview
|
R.color.light_webview
|
||||||
@ -345,15 +400,15 @@ class ArticleFragment : Fragment() {
|
|||||||
"#FFFFFF"
|
"#FFFFFF"
|
||||||
}
|
}
|
||||||
|
|
||||||
rootView.webcontent.settings.useWideViewPort = true
|
rootView!!.webcontent.settings.useWideViewPort = true
|
||||||
rootView.webcontent.settings.loadWithOverviewMode = true
|
rootView!!.webcontent.settings.loadWithOverviewMode = true
|
||||||
rootView.webcontent.settings.javaScriptEnabled = false
|
rootView!!.webcontent.settings.javaScriptEnabled = false
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
rootView.webcontent.settings.layoutAlgorithm =
|
rootView!!.webcontent.settings.layoutAlgorithm =
|
||||||
WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING
|
WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING
|
||||||
} else {
|
} else {
|
||||||
rootView.webcontent.settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.SINGLE_COLUMN
|
rootView!!.webcontent.settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.SINGLE_COLUMN
|
||||||
}
|
}
|
||||||
|
|
||||||
var baseUrl: String? = null
|
var baseUrl: String? = null
|
||||||
@ -362,80 +417,54 @@ class ArticleFragment : Fragment() {
|
|||||||
val itemUrl = URL(url)
|
val itemUrl = URL(url)
|
||||||
baseUrl = itemUrl.protocol + "://" + itemUrl.host
|
baseUrl = itemUrl.protocol + "://" + itemUrl.host
|
||||||
} catch (e: MalformedURLException) {
|
} catch (e: MalformedURLException) {
|
||||||
if (showMalformedUrl && context != null) {
|
ACRA.getErrorReporter().maybeHandleSilentException(e, activity!!)
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rootView.webcontent.loadDataWithBaseURL(
|
rootView!!.webcontent.loadDataWithBaseURL(
|
||||||
baseUrl,
|
baseUrl,
|
||||||
"""<style>
|
"""<html>
|
||||||
|img {
|
|<head>
|
||||||
|
| <meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
| <style>
|
||||||
|
| img {
|
||||||
| display: inline-block;
|
| display: inline-block;
|
||||||
| height: auto;
|
| height: auto;
|
||||||
| width: 100%;
|
| width: 100%;
|
||||||
| max-width: 100%;
|
| max-width: 100%;
|
||||||
|}
|
| }
|
||||||
|a {
|
| a {
|
||||||
| color: $stringColor !important;
|
| color: $stringColor !important;
|
||||||
|}
|
| }
|
||||||
|*:not(a) {
|
| *:not(a) {
|
||||||
| color: $stringTextColor;
|
| color: $stringTextColor;
|
||||||
|}
|
| }
|
||||||
|* {
|
| * {
|
||||||
| font-size: ${fontSize.toPx}px;
|
| font-size: ${fontSize}px;
|
||||||
| text-align: justify;
|
| text-align: $textAlignment;
|
||||||
| word-break: break-word;
|
| word-break: break-word;
|
||||||
| overflow:hidden;
|
| overflow:hidden;
|
||||||
|}
|
| }
|
||||||
|a, pre, code {
|
| a, pre, code {
|
||||||
| text-align: left;
|
| text-align: $textAlignment;
|
||||||
|}
|
| }
|
||||||
|pre, code {
|
| pre, code {
|
||||||
| white-space: pre-wrap;
|
| white-space: pre-wrap;
|
||||||
| width:100%;
|
| width:100%;
|
||||||
| background-color: $stringBackgroundColor;
|
| background-color: $stringBackgroundColor;
|
||||||
|}</style>$c""".trimMargin(),
|
| }
|
||||||
"text/html; charset=utf-8",
|
| </style>
|
||||||
|
|</head>
|
||||||
|
|<body>
|
||||||
|
| $contentText
|
||||||
|
|</body>""".trimMargin(),
|
||||||
|
"text/html",
|
||||||
"utf-8",
|
"utf-8",
|
||||||
null
|
null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun openInBrowserAfterFailing(customTabsIntent: CustomTabsIntent) {
|
private fun openInBrowserAfterFailing(customTabsIntent: CustomTabsIntent) {
|
||||||
rootView.progressBar.visibility = View.GONE
|
rootView!!.progressBar.visibility = View.GONE
|
||||||
activity!!.openItemUrl(
|
activity!!.openItemUrl(
|
||||||
allItems,
|
allItems,
|
||||||
pageNumber.toInt(),
|
pageNumber.toInt(),
|
||||||
|
@ -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`))")
|
||||||
|
}
|
||||||
|
}
|
@ -4,13 +4,13 @@ import android.content.res.Configuration;
|
|||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.preference.PreferenceActivity;
|
import android.preference.PreferenceActivity;
|
||||||
import android.support.annotation.LayoutRes;
|
import androidx.annotation.LayoutRes;
|
||||||
import android.support.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import android.support.design.widget.AppBarLayout;
|
import com.google.android.material.appbar.AppBarLayout;
|
||||||
import android.support.v7.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import android.support.v7.app.AppCompatDelegate;
|
import androidx.appcompat.app.AppCompatDelegate;
|
||||||
import android.support.v7.widget.Toolbar;
|
import androidx.appcompat.widget.Toolbar;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -19,7 +19,7 @@ import android.preference.PreferenceActivity;
|
|||||||
import android.preference.PreferenceFragment;
|
import android.preference.PreferenceFragment;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.preference.SwitchPreference;
|
import android.preference.SwitchPreference;
|
||||||
import android.support.v7.app.ActionBar;
|
import androidx.appcompat.app.ActionBar;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.InputFilter;
|
import android.text.InputFilter;
|
||||||
import android.text.Spanned;
|
import android.text.Spanned;
|
||||||
@ -31,7 +31,6 @@ import android.widget.Toast;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import apps.amine.bou.readerforselfoss.BuildConfig;
|
|
||||||
import apps.amine.bou.readerforselfoss.R;
|
import apps.amine.bou.readerforselfoss.R;
|
||||||
import apps.amine.bou.readerforselfoss.themes.AppColors;
|
import apps.amine.bou.readerforselfoss.themes.AppColors;
|
||||||
import apps.amine.bou.readerforselfoss.utils.Config;
|
import apps.amine.bou.readerforselfoss.utils.Config;
|
||||||
@ -136,6 +135,8 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
|||||||
return PreferenceFragment.class.getName().equals(fragmentName)
|
return PreferenceFragment.class.getName().equals(fragmentName)
|
||||||
|| GeneralPreferenceFragment.class.getName().equals(fragmentName)
|
|| GeneralPreferenceFragment.class.getName().equals(fragmentName)
|
||||||
|| ArticleViewerPreferenceFragment.class.getName().equals(fragmentName)
|
|| ArticleViewerPreferenceFragment.class.getName().equals(fragmentName)
|
||||||
|
|| OfflinePreferenceFragment.class.getName().equals(fragmentName)
|
||||||
|
|| ExperimentalPreferenceFragment.class.getName().equals(fragmentName)
|
||||||
|| DebugPreferenceFragment.class.getName().equals(fragmentName)
|
|| DebugPreferenceFragment.class.getName().equals(fragmentName)
|
||||||
|| LinksPreferenceFragment.class.getName().equals(fragmentName)
|
|| LinksPreferenceFragment.class.getName().equals(fragmentName)
|
||||||
|| ThemePreferenceFragment.class.getName().equals(fragmentName);
|
|| ThemePreferenceFragment.class.getName().equals(fragmentName);
|
||||||
@ -153,17 +154,6 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
|||||||
addPreferencesFromResource(R.xml.pref_general);
|
addPreferencesFromResource(R.xml.pref_general);
|
||||||
setHasOptionsMenu(true);
|
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");
|
EditTextPreference itemsNumber = (EditTextPreference) findPreference("prefer_api_items_number");
|
||||||
itemsNumber.getEditText().setFilters(new InputFilter[]{
|
itemsNumber.getEditText().setFilters(new InputFilter[]{
|
||||||
new InputFilter() {
|
new InputFilter() {
|
||||||
@ -375,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
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
int id = item.getItemId();
|
int id = item.getItemId();
|
||||||
|
@ -3,8 +3,8 @@ package apps.amine.bou.readerforselfoss.themes
|
|||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
import android.support.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import android.support.v7.view.ContextThemeWrapper
|
import androidx.appcompat.view.ContextThemeWrapper
|
||||||
import android.util.TypedValue
|
import android.util.TypedValue
|
||||||
import apps.amine.bou.readerforselfoss.R
|
import apps.amine.bou.readerforselfoss.R
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package apps.amine.bou.readerforselfoss.transformers
|
package apps.amine.bou.readerforselfoss.transformers
|
||||||
|
|
||||||
import android.support.v4.view.ViewPager
|
import androidx.viewpager.widget.ViewPager
|
||||||
import android.view.View
|
import android.view.View
|
||||||
|
|
||||||
class DepthPageTransformer : ViewPager.PageTransformer {
|
class DepthPageTransformer : ViewPager.PageTransformer {
|
||||||
|
@ -2,11 +2,21 @@ package apps.amine.bou.readerforselfoss.utils
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
|
import android.provider.Settings
|
||||||
import org.acra.ErrorReporter
|
import org.acra.ErrorReporter
|
||||||
|
|
||||||
fun ErrorReporter.maybeHandleSilentException(throwable: Throwable, ctx: Context) {
|
fun ErrorReporter.maybeHandleSilentException(throwable: Throwable, ctx: Context) {
|
||||||
val sharedPref = PreferenceManager.getDefaultSharedPreferences(ctx)
|
val sharedPref = PreferenceManager.getDefaultSharedPreferences(ctx)
|
||||||
if (sharedPref.getBoolean("acra_should_log", false)) {
|
val isTestLab = Settings.System.getString(ctx.contentResolver, "firebase.test.lab") == "true"
|
||||||
|
|
||||||
|
if (sharedPref.getBoolean("acra_should_log", false) && !isTestLab) {
|
||||||
|
this.handleSilentException(throwable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ErrorReporter.doHandleSilentException(throwable: Throwable, ctx: Context) {
|
||||||
|
val isTestLab = Settings.System.getString(ctx.contentResolver, "firebase.test.lab") == "true"
|
||||||
|
if (!isTestLab) {
|
||||||
this.handleSilentException(throwable)
|
this.handleSilentException(throwable)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -25,11 +25,12 @@ fun String.toStringUriWithHttp(): String =
|
|||||||
this
|
this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Context.shareLink(itemUrl: String) {
|
fun Context.shareLink(itemUrl: String, itemTitle: String) {
|
||||||
val sendIntent = Intent()
|
val sendIntent = Intent()
|
||||||
sendIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
sendIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
sendIntent.action = Intent.ACTION_SEND
|
sendIntent.action = Intent.ACTION_SEND
|
||||||
sendIntent.putExtra(Intent.EXTRA_TEXT, itemUrl.toStringUriWithHttp())
|
sendIntent.putExtra(Intent.EXTRA_TEXT, itemUrl.toStringUriWithHttp())
|
||||||
|
sendIntent.putExtra(Intent.EXTRA_SUBJECT, itemTitle)
|
||||||
sendIntent.type = "text/plain"
|
sendIntent.type = "text/plain"
|
||||||
startActivity(
|
startActivity(
|
||||||
Intent.createChooser(
|
Intent.createChooser(
|
||||||
|
@ -36,6 +36,10 @@ class Config(c: Context) {
|
|||||||
|
|
||||||
const val trackerUrl = "https://github.com/aminecmi/ReaderforSelfoss/issues"
|
const val trackerUrl = "https://github.com/aminecmi/ReaderforSelfoss/issues"
|
||||||
|
|
||||||
|
const val syncChannelId = "sync-channel-id"
|
||||||
|
|
||||||
|
const val newItemsChannelId = "new-items-channel-id"
|
||||||
|
|
||||||
fun logoutAndRedirect(
|
fun logoutAndRedirect(
|
||||||
c: Context,
|
c: Context,
|
||||||
callingActivity: Activity,
|
callingActivity: Activity,
|
||||||
|
@ -3,6 +3,7 @@ package apps.amine.bou.readerforselfoss.utils
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||||
|
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossTagType
|
||||||
import org.acra.ACRA
|
import org.acra.ACRA
|
||||||
import java.text.ParseException
|
import java.text.ParseException
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
@ -44,8 +45,8 @@ fun Item.toggleStar(): Item {
|
|||||||
fun List<Item>.flattenTags(): List<Item> =
|
fun List<Item>.flattenTags(): List<Item> =
|
||||||
this.flatMap {
|
this.flatMap {
|
||||||
val item = it
|
val item = it
|
||||||
val tags: List<String> = it.tags.split(",")
|
val tags: List<String> = it.tags.tags.split(",")
|
||||||
tags.map {
|
tags.map { t ->
|
||||||
item.copy(tags = it.trim())
|
item.copy(tags = SelfossTagType(t.trim()))
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,18 +2,25 @@ package apps.amine.bou.readerforselfoss.utils
|
|||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.BitmapFactory
|
import android.graphics.BitmapFactory
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.support.customtabs.CustomTabsIntent
|
import android.text.Spannable
|
||||||
|
import android.text.style.ClickableSpan
|
||||||
|
import androidx.browser.customtabs.CustomTabsIntent
|
||||||
import android.util.Patterns
|
import android.util.Patterns
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import android.widget.TextView
|
||||||
import android.widget.Toast
|
import android.widget.Toast
|
||||||
import apps.amine.bou.readerforselfoss.R
|
import apps.amine.bou.readerforselfoss.R
|
||||||
import apps.amine.bou.readerforselfoss.ReaderActivity
|
import apps.amine.bou.readerforselfoss.ReaderActivity
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||||
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
||||||
import okhttp3.HttpUrl
|
import okhttp3.HttpUrl
|
||||||
|
import org.acra.ACRA
|
||||||
|
|
||||||
fun Context.buildCustomTabsIntent(): CustomTabsIntent {
|
fun Context.buildCustomTabsIntent(): CustomTabsIntent {
|
||||||
|
|
||||||
@ -123,13 +130,17 @@ fun Context.openItemUrl(
|
|||||||
private fun openInBrowser(linkDecoded: String, app: Activity) {
|
private fun openInBrowser(linkDecoded: String, app: Activity) {
|
||||||
val intent = Intent(Intent.ACTION_VIEW)
|
val intent = Intent(Intent.ACTION_VIEW)
|
||||||
intent.data = Uri.parse(linkDecoded)
|
intent.data = Uri.parse(linkDecoded)
|
||||||
|
try {
|
||||||
app.startActivity(intent)
|
app.startActivity(intent)
|
||||||
|
} catch (e: ActivityNotFoundException) {
|
||||||
|
Toast.makeText(app.baseContext, e.message, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun String.isUrlValid(): Boolean =
|
fun String.isUrlValid(): Boolean =
|
||||||
HttpUrl.parse(this) != null && Patterns.WEB_URL.matcher(this).matches()
|
HttpUrl.parse(this) != null && Patterns.WEB_URL.matcher(this).matches()
|
||||||
|
|
||||||
fun String.isBaseUrlValid(): Boolean {
|
fun String.isBaseUrlValid(logErrors: Boolean, ctx: Context): Boolean {
|
||||||
val baseUrl = HttpUrl.parse(this)
|
val baseUrl = HttpUrl.parse(this)
|
||||||
var existsAndEndsWithSlash = false
|
var existsAndEndsWithSlash = false
|
||||||
if (baseUrl != null) {
|
if (baseUrl != null) {
|
||||||
@ -146,3 +157,40 @@ fun Context.openInBrowserAsNewTask(i: Item) {
|
|||||||
intent.data = Uri.parse(i.getLinkDecoded().toStringUriWithHttp())
|
intent.data = Uri.parse(i.getLinkDecoded().toStringUriWithHttp())
|
||||||
startActivity(intent)
|
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.app.Activity;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.customtabs.CustomTabsClient;
|
import androidx.browser.customtabs.CustomTabsClient;
|
||||||
import android.support.customtabs.CustomTabsIntent;
|
import androidx.browser.customtabs.CustomTabsIntent;
|
||||||
import android.support.customtabs.CustomTabsServiceConnection;
|
import androidx.browser.customtabs.CustomTabsServiceConnection;
|
||||||
import android.support.customtabs.CustomTabsSession;
|
import androidx.browser.customtabs.CustomTabsSession;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@ import android.content.IntentFilter;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.support.customtabs.CustomTabsService;
|
import androidx.browser.customtabs.CustomTabsService;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
@ -2,8 +2,8 @@ package apps.amine.bou.readerforselfoss.utils.customtabs;
|
|||||||
|
|
||||||
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.support.customtabs.CustomTabsClient;
|
import androidx.browser.customtabs.CustomTabsClient;
|
||||||
import android.support.customtabs.CustomTabsServiceConnection;
|
import androidx.browser.customtabs.CustomTabsServiceConnection;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package apps.amine.bou.readerforselfoss.utils.customtabs;
|
package apps.amine.bou.readerforselfoss.utils.customtabs;
|
||||||
|
|
||||||
|
|
||||||
import android.support.customtabs.CustomTabsClient;
|
import androidx.browser.customtabs.CustomTabsClient;
|
||||||
|
|
||||||
|
|
||||||
public interface ServiceConnectionCallback {
|
public interface ServiceConnectionCallback {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
/* From https://github.com/mikepenz/MaterialDrawer/blob/develop/app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/CustomBaseViewHolder.java */
|
/* From https://github.com/mikepenz/MaterialDrawer/blob/develop/app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/CustomBaseViewHolder.java */
|
||||||
package apps.amine.bou.readerforselfoss.utils.drawer
|
package apps.amine.bou.readerforselfoss.utils.drawer
|
||||||
|
|
||||||
import android.support.v7.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
package apps.amine.bou.readerforselfoss.utils.drawer
|
package apps.amine.bou.readerforselfoss.utils.drawer
|
||||||
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.support.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import android.support.annotation.ColorRes
|
import androidx.annotation.ColorRes
|
||||||
import android.support.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import android.support.v7.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
|
||||||
import com.mikepenz.materialdrawer.holder.ColorHolder
|
import com.mikepenz.materialdrawer.holder.ColorHolder
|
||||||
import com.mikepenz.materialdrawer.holder.ImageHolder
|
import com.mikepenz.materialdrawer.holder.ImageHolder
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
/* From https://github.com/mikepenz/MaterialDrawer/blob/develop/app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/CustomUrlPrimaryDrawerItem.java */
|
/* From https://github.com/mikepenz/MaterialDrawer/blob/develop/app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/CustomUrlPrimaryDrawerItem.java */
|
||||||
package apps.amine.bou.readerforselfoss.utils.drawer
|
package apps.amine.bou.readerforselfoss.utils.drawer
|
||||||
|
|
||||||
import android.support.annotation.LayoutRes
|
import androidx.annotation.LayoutRes
|
||||||
import android.support.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import apps.amine.bou.readerforselfoss.R
|
import apps.amine.bou.readerforselfoss.R
|
||||||
|
@ -2,7 +2,7 @@ package apps.amine.bou.readerforselfoss.utils.glide
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory
|
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.request.RequestOptions
|
import com.bumptech.glide.request.RequestOptions
|
||||||
|
@ -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
|
||||||
|
)
|
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M3,21h18v-2L3,19v2zM3,17h18v-2L3,15v2zM3,13h18v-2L3,11v2zM3,9h18L21,7L3,7v2zM3,3v2h18L21,3L3,3z"/>
|
||||||
|
</vector>
|
@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:viewportHeight="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M15,15L3,15v2h12v-2zM15,7L3,7v2h12L15,7zM3,13h18v-2L3,11v2zM3,21h18v-2L3,19v2zM3,3v2h18L21,3L3,3z"/>
|
||||||
|
</vector>
|
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 |
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 |
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 |
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 |
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 |
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:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<android.support.design.widget.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<android.support.v7.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
app:theme="@style/ToolBarStyle"
|
app:theme="@style/ToolBarStyle"
|
||||||
app:popupTheme="?attr/toolbarPopupTheme" />
|
app:popupTheme="?attr/toolbarPopupTheme" />
|
||||||
|
|
||||||
</android.support.design.widget.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<android.support.constraint.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||||
@ -121,7 +121,7 @@
|
|||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
app:layout_constraintVertical_bias="0.0"/>
|
app:layout_constraintVertical_bias="0.0"/>
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/progress"
|
android:id="@+id/progress"
|
||||||
|
@ -30,13 +30,13 @@
|
|||||||
app:prompt_view_background_color="?attr/colorAccent"
|
app:prompt_view_background_color="?attr/colorAccent"
|
||||||
app:prompt_view_thanks_display_time_ms="2000"/>
|
app:prompt_view_thanks_display_time_ms="2000"/>
|
||||||
|
|
||||||
<android.support.design.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:id="@+id/coordLayout"
|
android:id="@+id/coordLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_below="@id/promptView">
|
android:layout_below="@id/promptView">
|
||||||
|
|
||||||
<android.support.design.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
android:id="@+id/intern_coordLayout"
|
android:id="@+id/intern_coordLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
@ -46,18 +46,18 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<android.support.design.widget.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<android.support.v7.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/toolBar"
|
android:id="@+id/toolBar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
app:theme="@style/ToolBarStyle"
|
app:theme="@style/ToolBarStyle"
|
||||||
app:popupTheme="?attr/toolbarPopupTheme" />
|
app:popupTheme="?attr/toolbarPopupTheme" />
|
||||||
|
|
||||||
</android.support.design.widget.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/drawer_layout"
|
android:id="@+id/drawer_layout"
|
||||||
@ -66,7 +66,7 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<android.support.v4.widget.SwipeRefreshLayout
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
android:id="@+id/swipeRefreshLayout"
|
android:id="@+id/swipeRefreshLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
@ -86,30 +86,30 @@
|
|||||||
android:text="@string/nothing_here"
|
android:text="@string/nothing_here"
|
||||||
android:textAlignment="center"
|
android:textAlignment="center"
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
|
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
|
||||||
android:background="@color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:visibility="gone" />
|
android:visibility="gone" />
|
||||||
|
|
||||||
<android.support.v7.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/recyclerView"
|
android:id="@+id/recyclerView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@color/transparent"
|
android:background="@android:color/transparent"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:paddingBottom="60dp"
|
android:paddingBottom="60dp"
|
||||||
android:scrollbars="vertical"
|
android:scrollbars="vertical"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</android.support.v4.widget.SwipeRefreshLayout>
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</android.support.design.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
<com.ashokvarma.bottomnavigation.BottomNavigationBar
|
<com.ashokvarma.bottomnavigation.BottomNavigationBar
|
||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
android:id="@+id/bottomBar"
|
android:id="@+id/bottomBar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="60dp"/>
|
android:layout_height="60dp"/>
|
||||||
</android.support.design.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
@ -6,18 +6,18 @@
|
|||||||
android:gravity="center_horizontal"
|
android:gravity="center_horizontal"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
tools:context="apps.amine.bou.readerforselfoss.LoginActivity">
|
tools:context="apps.amine.bou.readerforselfoss.LoginActivity">
|
||||||
<android.support.design.widget.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<android.support.v7.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
app:theme="@style/ToolBarStyle"
|
app:theme="@style/ToolBarStyle"
|
||||||
app:popupTheme="?attr/toolbarPopupTheme" />
|
app:popupTheme="?attr/toolbarPopupTheme" />
|
||||||
|
|
||||||
</android.support.design.widget.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
@ -45,7 +45,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<android.support.design.widget.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:id="@+id/urlLayout"
|
android:id="@+id/urlLayout"
|
||||||
@ -60,7 +60,7 @@
|
|||||||
android:inputType="textUri"
|
android:inputType="textUri"
|
||||||
android:maxLines="1" />
|
android:maxLines="1" />
|
||||||
|
|
||||||
</android.support.design.widget.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<Switch
|
<Switch
|
||||||
android:text="@string/withLoginSwitch"
|
android:text="@string/withLoginSwitch"
|
||||||
@ -69,7 +69,7 @@
|
|||||||
android:id="@+id/withLogin"
|
android:id="@+id/withLogin"
|
||||||
android:layout_weight="1"/>
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
<android.support.design.widget.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/loginLayout"
|
android:id="@+id/loginLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@ -83,9 +83,9 @@
|
|||||||
android:inputType="text"
|
android:inputType="text"
|
||||||
android:maxLines="1" />
|
android:maxLines="1" />
|
||||||
|
|
||||||
</android.support.design.widget.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<android.support.design.widget.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/passwordLayout"
|
android:id="@+id/passwordLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@ -99,7 +99,7 @@
|
|||||||
android:inputType="textPassword"
|
android:inputType="textPassword"
|
||||||
android:maxLines="1" />
|
android:maxLines="1" />
|
||||||
|
|
||||||
</android.support.design.widget.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<Switch
|
<Switch
|
||||||
android:id="@+id/withHttpLogin"
|
android:id="@+id/withHttpLogin"
|
||||||
@ -108,7 +108,7 @@
|
|||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:text="@string/withHttpLoginSwitch" />
|
android:text="@string/withHttpLoginSwitch" />
|
||||||
|
|
||||||
<android.support.design.widget.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/httpLoginInput"
|
android:id="@+id/httpLoginInput"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
@ -120,9 +120,9 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:hint="@string/prompt_http_login" />
|
android:hint="@string/prompt_http_login" />
|
||||||
</android.support.design.widget.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<android.support.design.widget.TextInputLayout
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/httpPasswordInput"
|
android:id="@+id/httpPasswordInput"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
@ -134,7 +134,7 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:hint="@string/prompt_http_password"
|
android:hint="@string/prompt_http_password"
|
||||||
android:inputType="textPassword" />
|
android:inputType="textPassword" />
|
||||||
</android.support.design.widget.TextInputLayout>
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
<Switch
|
<Switch
|
||||||
android:id="@+id/withSelfhostedCert"
|
android:id="@+id/withSelfhostedCert"
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<android.support.constraint.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context="apps.amine.bou.readerforselfoss.MainActivity">
|
tools:context="apps.amine.bou.readerforselfoss.MainActivity">
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
@ -1,10 +1,11 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
android:id="@+id/reader_activity_view"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
<android.support.design.widget.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:id="@+id/appBarLayout"
|
android:id="@+id/appBarLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@ -12,16 +13,16 @@
|
|||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent">
|
app:layout_constraintTop_toTopOf="parent">
|
||||||
|
|
||||||
<android.support.v7.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/toolBar"
|
android:id="@+id/toolBar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
app:popupTheme="?attr/toolbarPopupTheme"
|
app:popupTheme="?attr/toolbarPopupTheme"
|
||||||
app:theme="@style/ToolBarStyle" />
|
app:theme="@style/ToolBarStyle" />
|
||||||
|
|
||||||
</android.support.design.widget.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<android.support.v4.view.ViewPager
|
<androidx.viewpager.widget.ViewPager
|
||||||
android:id="@+id/pager"
|
android:id="@+id/pager"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
@ -41,4 +42,4 @@
|
|||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="@+id/pager" />
|
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"?>
|
<?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:tools="http://schemas.android.com/tools"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context="apps.amine.bou.readerforselfoss.SourcesActivity">
|
tools:context="apps.amine.bou.readerforselfoss.SourcesActivity">
|
||||||
<android.support.design.widget.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
<android.support.v7.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
app:theme="@style/ToolBarStyle"
|
app:theme="@style/ToolBarStyle"
|
||||||
app:popupTheme="?attr/toolbarPopupTheme" />
|
app:popupTheme="?attr/toolbarPopupTheme" />
|
||||||
|
|
||||||
</android.support.design.widget.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<android.support.v7.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/recyclerView"
|
android:id="@+id/recyclerView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:scrollbars="vertical"
|
android:scrollbars="vertical"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
</android.support.v7.widget.RecyclerView>
|
</androidx.recyclerview.widget.RecyclerView>
|
||||||
|
|
||||||
<android.support.design.widget.FloatingActionButton
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
android:id="@+id/fab"
|
android:id="@+id/fab"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@ -40,6 +40,5 @@
|
|||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginEnd="16dp"
|
||||||
android:layout_marginRight="16dp"
|
android:layout_marginRight="16dp"/>
|
||||||
app:layout_behavior="apps.amine.bou.readerforselfoss.utils.ScrollAwareFABBehavior" />
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
</android.support.design.widget.CoordinatorLayout>
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<android.support.v7.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||||
@ -18,7 +18,7 @@
|
|||||||
card_view:cardUseCompatPadding="true"
|
card_view:cardUseCompatPadding="true"
|
||||||
card_view:layout_constraintBottom_toBottomOf="parent">
|
card_view:layout_constraintBottom_toBottomOf="parent">
|
||||||
|
|
||||||
<android.support.constraint.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content">
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
@ -34,7 +34,7 @@
|
|||||||
app:srcCompat="@drawable/background_splash"
|
app:srcCompat="@drawable/background_splash"
|
||||||
card_view:layout_constraintBottom_toTopOf="@+id/constraintLayout" />
|
card_view:layout_constraintBottom_toTopOf="@+id/constraintLayout" />
|
||||||
|
|
||||||
<android.support.constraint.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:id="@+id/constraintLayout"
|
android:id="@+id/constraintLayout"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@ -143,7 +143,7 @@
|
|||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</android.support.constraint.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
</android.support.v7.widget.CardView>
|
</androidx.cardview.widget.CardView>
|
@ -1,4 +1,4 @@
|
|||||||
<android.support.design.widget.CoordinatorLayout
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
@ -6,12 +6,13 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:descendantFocusability="blocksDescendants">
|
android:descendantFocusability="blocksDescendants">
|
||||||
|
|
||||||
<android.support.v4.widget.NestedScrollView
|
<androidx.core.widget.NestedScrollView
|
||||||
android:id="@+id/nestedScrollView"
|
android:id="@+id/nestedScrollView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
android:scrollbars="vertical">
|
||||||
|
|
||||||
<android.support.constraint.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
@ -70,9 +71,9 @@
|
|||||||
app:layout_constraintTop_toBottomOf="@+id/source"
|
app:layout_constraintTop_toBottomOf="@+id/source"
|
||||||
tools:visibility="visible" />
|
tools:visibility="visible" />
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
</android.support.v4.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -89,7 +90,7 @@
|
|||||||
android:layout_gravity="bottom"
|
android:layout_gravity="bottom"
|
||||||
app:floatingMenu="@menu/reader_toolbar" />
|
app:floatingMenu="@menu/reader_toolbar" />
|
||||||
|
|
||||||
<android.support.design.widget.FloatingActionButton
|
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
android:id="@+id/fab"
|
android:id="@+id/fab"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@ -123,4 +124,4 @@
|
|||||||
android:progressTint="?attr/colorAccent" />
|
android:progressTint="?attr/colorAccent" />
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
</android.support.design.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -11,7 +11,6 @@
|
|||||||
android:id="@+id/itemImage"
|
android:id="@+id/itemImage"
|
||||||
android:layout_width="88dp"
|
android:layout_width="88dp"
|
||||||
android:layout_height="88dp"
|
android:layout_height="88dp"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/actionBar"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
@ -40,79 +39,16 @@
|
|||||||
android:id="@+id/sourceTitleAndDate"
|
android:id="@+id/sourceTitleAndDate"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
android:layout_marginEnd="16dp"
|
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
android:gravity="start"
|
android:gravity="start"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/actionBar"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
app:layout_constraintStart_toEndOf="@+id/itemImage"
|
app:layout_constraintStart_toEndOf="@+id/itemImage"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/title"
|
app:layout_constraintTop_toBottomOf="@+id/title"
|
||||||
tools:text="Google Actualité Il y a 5h" />
|
tools:text="Google Actualité Il y a 5h" />
|
||||||
|
|
||||||
<RelativeLayout
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
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>
|
|
@ -1,15 +1,15 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<android.support.design.widget.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<android.support.v7.widget.Toolbar
|
<androidx.appcompat.widget.Toolbar
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize"
|
android:layout_height="?attr/actionBarSize"
|
||||||
app:theme="@style/ToolBarStyle"
|
app:theme="@style/ToolBarStyle"
|
||||||
app:popupTheme="?attr/toolbarPopupTheme" />
|
app:popupTheme="?attr/toolbarPopupTheme" />
|
||||||
|
|
||||||
</android.support.design.widget.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<android.support.constraint.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
@ -52,4 +52,4 @@
|
|||||||
android:layout_width="34dp"
|
android:layout_width="34dp"
|
||||||
android:layout_height="34dp"/>
|
android:layout_height="34dp"/>
|
||||||
|
|
||||||
</android.support.constraint.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -6,7 +6,7 @@
|
|||||||
android:title="@string/menu_home_search"
|
android:title="@string/menu_home_search"
|
||||||
android:icon="@drawable/ic_action_search"
|
android:icon="@drawable/ic_action_search"
|
||||||
app:showAsAction="ifRoom|collapseActionView"
|
app:showAsAction="ifRoom|collapseActionView"
|
||||||
app:actionViewClass="android.support.v7.widget.SearchView" />
|
app:actionViewClass="androidx.appcompat.widget.SearchView" />
|
||||||
|
|
||||||
<item android:id="@+id/readAll"
|
<item android:id="@+id/readAll"
|
||||||
android:icon="@drawable/ic_done_all_white_24dp"
|
android:icon="@drawable/ic_done_all_white_24dp"
|
||||||
|
@ -2,6 +2,18 @@
|
|||||||
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
|
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<item android:id="@+id/align_left"
|
||||||
|
android:icon="@drawable/ic_format_align_left"
|
||||||
|
android:visible="true"
|
||||||
|
app:showAsAction="ifRoom"
|
||||||
|
android:title="@string/reader_text_align_left" />
|
||||||
|
|
||||||
|
<item android:id="@+id/align_justify"
|
||||||
|
android:icon="@drawable/ic_format_align_justify"
|
||||||
|
android:visible="true"
|
||||||
|
app:showAsAction="ifRoom"
|
||||||
|
android:title="@string/reader_text_align_justify" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/unsave"
|
android:id="@+id/unsave"
|
||||||
android:icon="@drawable/heart_on"
|
android:icon="@drawable/heart_on"
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
<string name="action_disconnect">"Disconnect"</string>
|
<string name="action_disconnect">"Disconnect"</string>
|
||||||
<string name="title_activity_settings">"Settings"</string>
|
<string name="title_activity_settings">"Settings"</string>
|
||||||
<string name="pref_header_general">"General"</string>
|
<string name="pref_header_general">"General"</string>
|
||||||
<string name="pref_switch_actions_tap_title">"Tap action on the articles"</string>
|
|
||||||
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
|
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
|
||||||
<string name="add_source_hint_url">"Link"</string>
|
<string name="add_source_hint_url">"Link"</string>
|
||||||
<string name="add_source_hint_name">"Name"</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">"Display the unread count as a badge for the bottom bar."</string>
|
||||||
<string name="switch_unread_count_title">"Display unread count"</string>
|
<string name="switch_unread_count_title">"Display unread count"</string>
|
||||||
<string name="display_all_counts_title">"Display count for favorite and read"</string>
|
<string name="display_all_counts_title">"Display count for favorite and read"</string>
|
||||||
<string name="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="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_title">"Open links inside the app"</string>
|
||||||
<string name="pref_general_internal_browser_on">"Articles will open inside the app"</string>
|
<string name="pref_general_internal_browser_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="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_links">"Link handling"</string>
|
||||||
<string name="pref_general_category_displaying">"Displaying"</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_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_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="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_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_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="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_title">Full height cards</string>
|
||||||
<string name="card_height_on">Cards height will adjust to its content</string>
|
<string name="card_height_on">Cards height will adjust to its content</string>
|
||||||
<string name="card_height_off">Card height will be fixed</string>
|
<string name="card_height_off">Card height will be fixed</string>
|
||||||
@ -108,7 +90,6 @@
|
|||||||
<string name="drawer_item_tags">Tags</string>
|
<string name="drawer_item_tags">Tags</string>
|
||||||
<string name="drawer_item_sources">Sources</string>
|
<string name="drawer_item_sources">Sources</string>
|
||||||
<string name="drawer_action_edit">edit</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_tags_loaded">No tags loaded</string>
|
||||||
<string name="no_sources_loaded">No sources loaded</string>
|
<string name="no_sources_loaded">No sources loaded</string>
|
||||||
<string name="drawer_loading">Loading …</string>
|
<string name="drawer_loading">Loading …</string>
|
||||||
@ -159,8 +140,6 @@
|
|||||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||||
<string name="crash_dialog_text">Something went wrong. Please send the report to the developer.</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="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">Automatically send crash reports</string>
|
||||||
<string name="pref_acra_alwaysaccept_enabled">Will send crash reports automatically</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_acra_alwaysaccept_disabled">Will ask everytime when sending crash reports.</string>
|
||||||
@ -169,4 +148,26 @@
|
|||||||
<string name="acra_login">Enable logging</string>
|
<string name="acra_login">Enable logging</string>
|
||||||
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
||||||
<string name="unmark">Mark item as unread</string>
|
<string name="unmark">Mark item as unread</string>
|
||||||
|
<string name="pref_header_offline">Offline and cache</string>
|
||||||
|
<string name="pref_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>
|
||||||
|
<string name="webview_dialog_issue_message">Webview not available. Disabling the article viewer to avoid any future crashes. Will load articles inside of your browser from now on.</string>
|
||||||
|
<string name="webview_dialog_issue_title">Webview issue</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
<string name="action_disconnect">"Disconnect"</string>
|
<string name="action_disconnect">"Disconnect"</string>
|
||||||
<string name="title_activity_settings">"Settings"</string>
|
<string name="title_activity_settings">"Settings"</string>
|
||||||
<string name="pref_header_general">"General"</string>
|
<string name="pref_header_general">"General"</string>
|
||||||
<string name="pref_switch_actions_tap_title">"Tap action on the articles"</string>
|
|
||||||
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
|
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
|
||||||
<string name="add_source_hint_url">"Link"</string>
|
<string name="add_source_hint_url">"Link"</string>
|
||||||
<string name="add_source_hint_name">"Name"</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">"Display the unread count as a badge for the bottom bar."</string>
|
||||||
<string name="switch_unread_count_title">"Display unread count"</string>
|
<string name="switch_unread_count_title">"Display unread count"</string>
|
||||||
<string name="display_all_counts_title">"Display count for favorite and read"</string>
|
<string name="display_all_counts_title">"Display count for favorite and read"</string>
|
||||||
<string name="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="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_title">"Open links inside the app"</string>
|
||||||
<string name="pref_general_internal_browser_on">"Articles will open inside the app"</string>
|
<string name="pref_general_internal_browser_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="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_links">"Link handling"</string>
|
||||||
<string name="pref_general_category_displaying">"Displaying"</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_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_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="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_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_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="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_title">Full height cards</string>
|
||||||
<string name="card_height_on">Cards height will adjust to its content</string>
|
<string name="card_height_on">Cards height will adjust to its content</string>
|
||||||
<string name="card_height_off">Card height will be fixed</string>
|
<string name="card_height_off">Card height will be fixed</string>
|
||||||
@ -108,7 +90,6 @@
|
|||||||
<string name="drawer_item_tags">Tags</string>
|
<string name="drawer_item_tags">Tags</string>
|
||||||
<string name="drawer_item_sources">Sources</string>
|
<string name="drawer_item_sources">Sources</string>
|
||||||
<string name="drawer_action_edit">edit</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_tags_loaded">No tags loaded</string>
|
||||||
<string name="no_sources_loaded">No sources loaded</string>
|
<string name="no_sources_loaded">No sources loaded</string>
|
||||||
<string name="drawer_loading">Loading …</string>
|
<string name="drawer_loading">Loading …</string>
|
||||||
@ -159,8 +140,6 @@
|
|||||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||||
<string name="crash_dialog_text">Something went wrong. Please send the report to the developer.</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="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">Automatically send crash reports</string>
|
||||||
<string name="pref_acra_alwaysaccept_enabled">Will send crash reports automatically</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_acra_alwaysaccept_disabled">Will ask everytime when sending crash reports.</string>
|
||||||
@ -169,4 +148,26 @@
|
|||||||
<string name="acra_login">Enable logging</string>
|
<string name="acra_login">Enable logging</string>
|
||||||
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
||||||
<string name="unmark">Mark item as unread</string>
|
<string name="unmark">Mark item as unread</string>
|
||||||
|
<string name="pref_header_offline">Offline and cache</string>
|
||||||
|
<string name="pref_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>
|
||||||
|
<string name="webview_dialog_issue_message">Webview not available. Disabling the article viewer to avoid any future crashes. Will load articles inside of your browser from now on.</string>
|
||||||
|
<string name="webview_dialog_issue_title">Webview issue</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
<string name="action_disconnect">"Desconnecta't"</string>
|
<string name="action_disconnect">"Desconnecta't"</string>
|
||||||
<string name="title_activity_settings">"Configuració"</string>
|
<string name="title_activity_settings">"Configuració"</string>
|
||||||
<string name="pref_header_general">"General"</string>
|
<string name="pref_header_general">"General"</string>
|
||||||
<string name="pref_switch_actions_tap_title">"Fer un toc als articles"</string>
|
|
||||||
<string name="add_source_hint_tags">"Etiqueta1, Etiqueta2, Etiqueta3"</string>
|
<string name="add_source_hint_tags">"Etiqueta1, Etiqueta2, Etiqueta3"</string>
|
||||||
<string name="add_source_hint_url">"Enllaç"</string>
|
<string name="add_source_hint_url">"Enllaç"</string>
|
||||||
<string name="add_source_hint_name">"Nom"</string>
|
<string name="add_source_hint_name">"Nom"</string>
|
||||||
@ -64,9 +63,6 @@
|
|||||||
<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">"Mostra el recompte d'articles no llegits amb un distintiu a la barra inferior."</string>
|
||||||
<string name="switch_unread_count_title">"Recompte d'articles no llegits"</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="display_all_counts_title">"Recompte d'articles llegits i preferits"</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="text_wrong_url">"Sembla que esteu utilitzant un URL no vàlid. Assegureu-vos que és correcte, i si el problema persisteix, poseu-vos en contacte amb mi (a través de l'enllaç de contacte que hi ha a la Botiga). Tingueu en compte que per utilitzar aquesta aplicació cal que també utilitzeu Selfoss. Si no, no podreu accedir a canals RSS."</string>
|
<string name="text_wrong_url">"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_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>
|
<string name="pref_general_internal_browser_on">"Els articles s'obriran dins de l'aplicació"</string>
|
||||||
@ -76,26 +72,12 @@
|
|||||||
<string name="prefer_article_viewer_off">"S'obrirà el navegador intern en lloc del visualitzador d'articles"</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_links">"Gestió d'enllaços"</string>
|
||||||
<string name="pref_general_category_displaying">"Visualització"</string>
|
<string name="pref_general_category_displaying">"Visualització"</string>
|
||||||
<string name="pref_general_category_actions">"Accions"</string>
|
|
||||||
<string name="pref_switch_card_view_on">"Els articles es mostraran com a targetes"</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_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">"En seleccionar un article, s'obrirà al navegador seleccionat"</string>
|
|
||||||
<string name="menu_home_refresh">"Actualitza l'accés remot"</string>
|
<string name="menu_home_refresh">"Actualitza l'accés remot"</string>
|
||||||
<string name="refresh_success_response">"S'ha actualitzat el remot. Torneu a carregar la llista d'articles"</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_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="refresh_in_progress">"S'està actualitzant"</string>
|
||||||
<string name="new_apk_available_title">"Hi ha un 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_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_on">L\'alçada de les targetes s\'ajustarà al seu contingut</string>
|
||||||
<string name="card_height_off">L\'alçada de les targetes serà fixa</string>
|
<string name="card_height_off">L\'alçada de les targetes serà fixa</string>
|
||||||
@ -108,7 +90,6 @@
|
|||||||
<string name="drawer_item_tags">Etiquetes</string>
|
<string name="drawer_item_tags">Etiquetes</string>
|
||||||
<string name="drawer_item_sources">Fonts</string>
|
<string name="drawer_item_sources">Fonts</string>
|
||||||
<string name="drawer_action_edit">Edita</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_tags_loaded">No s\'ha carregat cap etiqueta</string>
|
||||||
<string name="no_sources_loaded">No s\'ha carregat cap font</string>
|
<string name="no_sources_loaded">No s\'ha carregat cap font</string>
|
||||||
<string name="drawer_loading">S\'està carregant…</string>
|
<string name="drawer_loading">S\'està carregant…</string>
|
||||||
@ -127,7 +108,7 @@
|
|||||||
<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="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_selfoss_category">API de Selfoss</string>
|
||||||
<string name="pref_api_items_number_title">Nombre d\'elements carregats</string>
|
<string name="pref_api_items_number_title">Nombre d\'elements carregats</string>
|
||||||
<string name="pref_hidden_tags">Hidden Tags</string>
|
<string name="pref_hidden_tags">Etiquetes ocultes</string>
|
||||||
<string name="read_debug_title">Voleu llegir els articles que apareixen com a no llegits?</string>
|
<string name="read_debug_title">Voleu llegir els articles que apareixen com a no llegits?</string>
|
||||||
<string name="read_debug_off">No es registraran 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="read_debug_on">Les crides de l\'API es registraran en marcar un article com a llegit</string>
|
||||||
@ -159,14 +140,34 @@
|
|||||||
<string name="gdpr_dialog_title">Aquesta aplicació no comparteix cap dada personal vostra.</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_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="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_user_email">Correu electrònic de contacte</string>
|
|
||||||
<string name="pref_acra_user_email_summary">Afegiu una adreça de correu electrònic per tal que pugui contactar-vos en relació amb l\'informe d\'errors que heu enviat.</string>
|
|
||||||
<string name="pref_acra_alwaysaccept">Envia informes d\'error automàtics</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_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_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_crash_reports">Informes d\'error</string>
|
||||||
<string name="pref_debug_debug_logs">Registre de depuració (s\'enviarà automàticament)</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="acra_login">Habilita el registre</string>
|
||||||
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
<string name="drawer_item_hidden_tags">Etiquetes ocultes</string>
|
||||||
<string name="unmark">Mark item as unread</string>
|
<string name="unmark">Marca com no llegit</string>
|
||||||
|
<string name="pref_header_offline">Sense connexió i memòria clau</string>
|
||||||
|
<string name="pref_switch_items_caching_off">Els articles no es guardaran a la memòria del dispositiu i l\'aplicació no es podrà utilitzar sense connexió.</string>
|
||||||
|
<string name="pref_switch_items_caching_on">Els articles es guardaran a la memòria del dispositiu i es podran utilitzar sense connexió.</string>
|
||||||
|
<string name="pref_switch_items_caching">Guarda els elements per utilitzar-los sense connexió</string>
|
||||||
|
<string name="no_network_connectivity">Sense connexió!</string>
|
||||||
|
<string name="pref_switch_periodic_refresh">Sincronitza els articles</string>
|
||||||
|
<string name="pref_switch_periodic_refresh_off">Els articles no se sincronitzaran en segon pla</string>
|
||||||
|
<string name="pref_switch_periodic_refresh_on">Els articles se sincronitzaran periòdicament</string>
|
||||||
|
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Interval de sincronització ( >= 15 minuts)]]></string>
|
||||||
|
<string name="pref_switch_refresh_when_charging">Sincronitza només quan el telèfon s\'està carregant</string>
|
||||||
|
<string name="loading_notification_title">S\'està carregant...</string>
|
||||||
|
<string name="loading_notification_text">Selfoss està sincronitzant els articles</string>
|
||||||
|
<string name="notification_channel_sync">Notificació de sincronització</string>
|
||||||
|
<string name="new_items_channel_sync">Notificació d\'elements nous</string>
|
||||||
|
<string name="new_items_notification_title">Nous articles!</string>
|
||||||
|
<string name="new_items_notification_text">S\'han carregat %1$d articles nous.</string>
|
||||||
|
<string name="pref_switch_notify_new_items">Avisa\'m quan se sincronitzin articles nous.</string>
|
||||||
|
<string name="shortcut_offline">Sense connexió</string>
|
||||||
|
<string name="pref_api_timeout">S\'ha acabat el temps d\'espera de l\'API</string>
|
||||||
|
<string name="pref_header_experimental">Experimental</string>
|
||||||
|
<string name="webview_dialog_issue_message">Webview not available. Disabling the article viewer to avoid any future crashes. Will load articles inside of your browser from now on.</string>
|
||||||
|
<string name="webview_dialog_issue_title">Webview issue</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
<string name="action_disconnect">"Disconnect"</string>
|
<string name="action_disconnect">"Disconnect"</string>
|
||||||
<string name="title_activity_settings">"Settings"</string>
|
<string name="title_activity_settings">"Settings"</string>
|
||||||
<string name="pref_header_general">"General"</string>
|
<string name="pref_header_general">"General"</string>
|
||||||
<string name="pref_switch_actions_tap_title">"Tap action on the articles"</string>
|
|
||||||
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
|
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
|
||||||
<string name="add_source_hint_url">"Link"</string>
|
<string name="add_source_hint_url">"Link"</string>
|
||||||
<string name="add_source_hint_name">"Name"</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">"Display the unread count as a badge for the bottom bar."</string>
|
||||||
<string name="switch_unread_count_title">"Display unread count"</string>
|
<string name="switch_unread_count_title">"Display unread count"</string>
|
||||||
<string name="display_all_counts_title">"Display count for favorite and read"</string>
|
<string name="display_all_counts_title">"Display count for favorite and read"</string>
|
||||||
<string name="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="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_title">"Open links inside the app"</string>
|
||||||
<string name="pref_general_internal_browser_on">"Articles will open inside the app"</string>
|
<string name="pref_general_internal_browser_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="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_links">"Link handling"</string>
|
||||||
<string name="pref_general_category_displaying">"Displaying"</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_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_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="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_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_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="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_title">Full height cards</string>
|
||||||
<string name="card_height_on">Cards height will adjust to its content</string>
|
<string name="card_height_on">Cards height will adjust to its content</string>
|
||||||
<string name="card_height_off">Card height will be fixed</string>
|
<string name="card_height_off">Card height will be fixed</string>
|
||||||
@ -108,7 +90,6 @@
|
|||||||
<string name="drawer_item_tags">Tags</string>
|
<string name="drawer_item_tags">Tags</string>
|
||||||
<string name="drawer_item_sources">Sources</string>
|
<string name="drawer_item_sources">Sources</string>
|
||||||
<string name="drawer_action_edit">edit</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_tags_loaded">No tags loaded</string>
|
||||||
<string name="no_sources_loaded">No sources loaded</string>
|
<string name="no_sources_loaded">No sources loaded</string>
|
||||||
<string name="drawer_loading">Loading …</string>
|
<string name="drawer_loading">Loading …</string>
|
||||||
@ -159,8 +140,6 @@
|
|||||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||||
<string name="crash_dialog_text">Something went wrong. Please send the report to the developer.</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="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">Automatically send crash reports</string>
|
||||||
<string name="pref_acra_alwaysaccept_enabled">Will send crash reports automatically</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_acra_alwaysaccept_disabled">Will ask everytime when sending crash reports.</string>
|
||||||
@ -169,4 +148,26 @@
|
|||||||
<string name="acra_login">Enable logging</string>
|
<string name="acra_login">Enable logging</string>
|
||||||
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
||||||
<string name="unmark">Mark item as unread</string>
|
<string name="unmark">Mark item as unread</string>
|
||||||
|
<string name="pref_header_offline">Offline and cache</string>
|
||||||
|
<string name="pref_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>
|
||||||
|
<string name="webview_dialog_issue_message">Webview not available. Disabling the article viewer to avoid any future crashes. Will load articles inside of your browser from now on.</string>
|
||||||
|
<string name="webview_dialog_issue_title">Webview issue</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
<string name="action_disconnect">"Disconnect"</string>
|
<string name="action_disconnect">"Disconnect"</string>
|
||||||
<string name="title_activity_settings">"Settings"</string>
|
<string name="title_activity_settings">"Settings"</string>
|
||||||
<string name="pref_header_general">"General"</string>
|
<string name="pref_header_general">"General"</string>
|
||||||
<string name="pref_switch_actions_tap_title">"Tap action on the articles"</string>
|
|
||||||
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
|
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
|
||||||
<string name="add_source_hint_url">"Link"</string>
|
<string name="add_source_hint_url">"Link"</string>
|
||||||
<string name="add_source_hint_name">"Name"</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">"Display the unread count as a badge for the bottom bar."</string>
|
||||||
<string name="switch_unread_count_title">"Display unread count"</string>
|
<string name="switch_unread_count_title">"Display unread count"</string>
|
||||||
<string name="display_all_counts_title">"Display count for favorite and read"</string>
|
<string name="display_all_counts_title">"Display count for favorite and read"</string>
|
||||||
<string name="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="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_title">"Open links inside the app"</string>
|
||||||
<string name="pref_general_internal_browser_on">"Articles will open inside the app"</string>
|
<string name="pref_general_internal_browser_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="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_links">"Link handling"</string>
|
||||||
<string name="pref_general_category_displaying">"Displaying"</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_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_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="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_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_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="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_title">Full height cards</string>
|
||||||
<string name="card_height_on">Cards height will adjust to its content</string>
|
<string name="card_height_on">Cards height will adjust to its content</string>
|
||||||
<string name="card_height_off">Card height will be fixed</string>
|
<string name="card_height_off">Card height will be fixed</string>
|
||||||
@ -108,7 +90,6 @@
|
|||||||
<string name="drawer_item_tags">Tags</string>
|
<string name="drawer_item_tags">Tags</string>
|
||||||
<string name="drawer_item_sources">Sources</string>
|
<string name="drawer_item_sources">Sources</string>
|
||||||
<string name="drawer_action_edit">edit</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_tags_loaded">No tags loaded</string>
|
||||||
<string name="no_sources_loaded">No sources loaded</string>
|
<string name="no_sources_loaded">No sources loaded</string>
|
||||||
<string name="drawer_loading">Loading …</string>
|
<string name="drawer_loading">Loading …</string>
|
||||||
@ -159,8 +140,6 @@
|
|||||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||||
<string name="crash_dialog_text">Something went wrong. Please send the report to the developer.</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="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">Automatically send crash reports</string>
|
||||||
<string name="pref_acra_alwaysaccept_enabled">Will send crash reports automatically</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_acra_alwaysaccept_disabled">Will ask everytime when sending crash reports.</string>
|
||||||
@ -169,4 +148,26 @@
|
|||||||
<string name="acra_login">Enable logging</string>
|
<string name="acra_login">Enable logging</string>
|
||||||
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
||||||
<string name="unmark">Mark item as unread</string>
|
<string name="unmark">Mark item as unread</string>
|
||||||
|
<string name="pref_header_offline">Offline and cache</string>
|
||||||
|
<string name="pref_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>
|
||||||
|
<string name="webview_dialog_issue_message">Webview not available. Disabling the article viewer to avoid any future crashes. Will load articles inside of your browser from now on.</string>
|
||||||
|
<string name="webview_dialog_issue_title">Webview issue</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
<string name="action_disconnect">"Verbindung trennen"</string>
|
<string name="action_disconnect">"Verbindung trennen"</string>
|
||||||
<string name="title_activity_settings">"Einstellungen"</string>
|
<string name="title_activity_settings">"Einstellungen"</string>
|
||||||
<string name="pref_header_general">"Allgemein"</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_tags">"Tag1, Tag2, Tag3"</string>
|
||||||
<string name="add_source_hint_url">"Link"</string>
|
<string name="add_source_hint_url">"Link"</string>
|
||||||
<string name="add_source_hint_name">"Name"</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">"Zeige die Zahl ungelesener Artikel in der unteren Leiste."</string>
|
||||||
<string name="switch_unread_count_title">"Zeige Anzahl ungelesener Artikel"</string>
|
<string name="switch_unread_count_title">"Zeige Anzahl ungelesener Artikel"</string>
|
||||||
<string name="display_all_counts_title">"Zeige Anzahl der Favoriten und gelesenen Artikel"</string>
|
<string name="display_all_counts_title">"Zeige Anzahl der Favoriten und gelesenen Artikel"</string>
|
||||||
<string name="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="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_title">"Öffne Links innerhalb der App"</string>
|
||||||
<string name="pref_general_internal_browser_on">"Artikel werden innerhalb der App geöffnet"</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="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_links">"Umgang mit Links"</string>
|
||||||
<string name="pref_general_category_displaying">"Ansicht"</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_on">"Artikel werden als Kacheln angezeigt"</string>
|
||||||
<string name="pref_switch_card_view_off">"Artikel werden als Liste 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="menu_home_refresh">"Remote-Aktualisierung"</string>
|
||||||
<string name="refresh_success_response">"Selfoss wird aktualisiert, du kannst jetzt die Artikel laden"</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_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="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_title">Maximale Kartenhöhe</string>
|
||||||
<string name="card_height_on">Kartenhöhe passt sich Inhalt an</string>
|
<string name="card_height_on">Kartenhöhe passt sich Inhalt an</string>
|
||||||
<string name="card_height_off">Kartenhöhe ist fix</string>
|
<string name="card_height_off">Kartenhöhe ist fix</string>
|
||||||
@ -108,7 +90,6 @@
|
|||||||
<string name="drawer_item_tags">Tags</string>
|
<string name="drawer_item_tags">Tags</string>
|
||||||
<string name="drawer_item_sources">Quellen</string>
|
<string name="drawer_item_sources">Quellen</string>
|
||||||
<string name="drawer_action_edit">bearbeiten</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_tags_loaded">No tags loaded</string>
|
||||||
<string name="no_sources_loaded">Keine Quellen geladen</string>
|
<string name="no_sources_loaded">Keine Quellen geladen</string>
|
||||||
<string name="drawer_loading">Lade…</string>
|
<string name="drawer_loading">Lade…</string>
|
||||||
@ -148,25 +129,45 @@
|
|||||||
<string name="reader_action_share">Teilen</string>
|
<string name="reader_action_share">Teilen</string>
|
||||||
<string name="pref_switch_actions_pager_scroll_on">Mark articles as read when swiping between articles.</string>
|
<string name="pref_switch_actions_pager_scroll_on">Mark articles as read when swiping between articles.</string>
|
||||||
<string name="add_to_favs_reader">Zu Favoriten hinzufügen</string>
|
<string name="add_to_favs_reader">Zu Favoriten hinzufügen</string>
|
||||||
<string name="remove_to_favs_reader">Remove from favorites</string>
|
<string name="remove_to_favs_reader">Aus Favoriten entfernen</string>
|
||||||
<string name="pref_content_reader_font_size">Article reader content font size</string>
|
<string name="pref_content_reader_font_size">Article reader content font size</string>
|
||||||
<string name="pref_header_viewer">Article viewer</string>
|
<string name="pref_header_viewer">Article viewer</string>
|
||||||
<string name="refresh_dialog_message">This will refresh your Selfoss instance.</string>
|
<string name="refresh_dialog_message">This will refresh your Selfoss instance.</string>
|
||||||
<string name="markall_dialog_message">This will mark all the items as read.</string>
|
<string name="markall_dialog_message">Dies wird alle Elemente als gelesen markieren.</string>
|
||||||
<string name="pref_switch_actions_pager_scroll">Mark as read on swipe</string>
|
<string name="pref_switch_actions_pager_scroll">Beim Wischen als gelesen markieren</string>
|
||||||
<string name="pref_switch_actions_pager_scroll_off">Don\'t mark articles as read when swiping.</string>
|
<string name="pref_switch_actions_pager_scroll_off">Don\'t mark articles as read when swiping.</string>
|
||||||
<string name="gdpr_dialog_message">The app does not collect any personal data. Every analytics tools were removed. Crash reports sending is now optional, as is the debug logging. Keep in mind that debugging and crash reports are essential for the app development (You can configure everything in Settings > Debug).</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="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_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="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">Automatically send crash reports</string>
|
||||||
<string name="pref_acra_alwaysaccept_enabled">Will send crash reports automatically</string>
|
<string name="pref_acra_alwaysaccept_enabled">Fehlerberichte werden automatisch gesendet</string>
|
||||||
<string name="pref_acra_alwaysaccept_disabled">Will ask everytime when sending crash reports.</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_crash_reports">Fehlerberichte</string>
|
||||||
<string name="pref_debug_debug_logs">Debug logging (these will be sent without a dialog)</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="acra_login">Protokollierung aktivieren</string>
|
||||||
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
||||||
<string name="unmark">Mark item as unread</string>
|
<string name="unmark">Eintrag als ungelesen markieren</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">Nicht verbunden !</string>
|
||||||
|
<string name="pref_switch_periodic_refresh">Synchronisiere Artikel</string>
|
||||||
|
<string name="pref_switch_periodic_refresh_off">Artikel werden nicht im Hintergrund synchronisiert</string>
|
||||||
|
<string name="pref_switch_periodic_refresh_on">Die Artikel werden regelmäßig synchronisiert</string>
|
||||||
|
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string>
|
||||||
|
<string name="pref_switch_refresh_when_charging">Nur aktualisieren, wenn das Telefon aufgeladen wird</string>
|
||||||
|
<string name="loading_notification_title">Lädt...</string>
|
||||||
|
<string name="loading_notification_text">Selfoss synchronisiert Ihre Artikel</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-Zeitüberschreitung</string>
|
||||||
|
<string name="pref_header_experimental">Experimentell</string>
|
||||||
|
<string name="webview_dialog_issue_message">Webview not available. Disabling the article viewer to avoid any future crashes. Will load articles inside of your browser from now on.</string>
|
||||||
|
<string name="webview_dialog_issue_title">Webview issue</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -19,7 +19,6 @@
|
|||||||
<string name="action_disconnect">"Disconnect"</string>
|
<string name="action_disconnect">"Disconnect"</string>
|
||||||
<string name="title_activity_settings">"Settings"</string>
|
<string name="title_activity_settings">"Settings"</string>
|
||||||
<string name="pref_header_general">"General"</string>
|
<string name="pref_header_general">"General"</string>
|
||||||
<string name="pref_switch_actions_tap_title">"Tap action on the articles"</string>
|
|
||||||
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
|
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
|
||||||
<string name="add_source_hint_url">"Link"</string>
|
<string name="add_source_hint_url">"Link"</string>
|
||||||
<string name="add_source_hint_name">"Name"</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">"Display the unread count as a badge for the bottom bar."</string>
|
||||||
<string name="switch_unread_count_title">"Display unread count"</string>
|
<string name="switch_unread_count_title">"Display unread count"</string>
|
||||||
<string name="display_all_counts_title">"Display count for favorite and read"</string>
|
<string name="display_all_counts_title">"Display count for favorite and read"</string>
|
||||||
<string name="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="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_title">"Open links inside the app"</string>
|
||||||
<string name="pref_general_internal_browser_on">"Articles will open inside the app"</string>
|
<string name="pref_general_internal_browser_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="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_links">"Link handling"</string>
|
||||||
<string name="pref_general_category_displaying">"Displaying"</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_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_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="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_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_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="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_title">Full height cards</string>
|
||||||
<string name="card_height_on">Cards height will adjust to its content</string>
|
<string name="card_height_on">Cards height will adjust to its content</string>
|
||||||
<string name="card_height_off">Card height will be fixed</string>
|
<string name="card_height_off">Card height will be fixed</string>
|
||||||
@ -108,7 +90,6 @@
|
|||||||
<string name="drawer_item_tags">Tags</string>
|
<string name="drawer_item_tags">Tags</string>
|
||||||
<string name="drawer_item_sources">Sources</string>
|
<string name="drawer_item_sources">Sources</string>
|
||||||
<string name="drawer_action_edit">edit</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_tags_loaded">No tags loaded</string>
|
||||||
<string name="no_sources_loaded">No sources loaded</string>
|
<string name="no_sources_loaded">No sources loaded</string>
|
||||||
<string name="drawer_loading">Loading …</string>
|
<string name="drawer_loading">Loading …</string>
|
||||||
@ -159,8 +140,6 @@
|
|||||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||||
<string name="crash_dialog_text">Something went wrong. Please send the report to the developer.</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="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">Automatically send crash reports</string>
|
||||||
<string name="pref_acra_alwaysaccept_enabled">Will send crash reports automatically</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_acra_alwaysaccept_disabled">Will ask everytime when sending crash reports.</string>
|
||||||
@ -169,4 +148,26 @@
|
|||||||
<string name="acra_login">Enable logging</string>
|
<string name="acra_login">Enable logging</string>
|
||||||
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
||||||
<string name="unmark">Mark item as unread</string>
|
<string name="unmark">Mark item as unread</string>
|
||||||
|
<string name="pref_header_offline">Offline and cache</string>
|
||||||
|
<string name="pref_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>
|
||||||
|
<string name="webview_dialog_issue_message">Webview not available. Disabling the article viewer to avoid any future crashes. Will load articles inside of your browser from now on.</string>
|
||||||
|
<string name="webview_dialog_issue_title">Webview issue</string>
|
||||||
</resources>
|
</resources>
|
||||||
|