Compare commits
15 Commits
Author | SHA1 | Date | |
---|---|---|---|
d0557cf653 | |||
f6999dd547 | |||
040c845c15 | |||
cb07227cb7 | |||
cda09a4e8a | |||
3de95ba6e4 | |||
47f00dc694 | |||
584c6869b5 | |||
b0c4b010a2 | |||
e08fc2604f | |||
41386adf4e | |||
59c307323f | |||
41e825bc50 | |||
0bab675560 | |||
55ced4a5fb |
26
CHANGELOG.md
26
CHANGELOG.md
@ -26,30 +26,6 @@
|
|||||||
|
|
||||||
- Closing #38. Only doing api calls on network available.
|
- Closing #38. Only doing api calls on network available.
|
||||||
|
|
||||||
- Closing #298 and #287. Issues with Listview rendering
|
|
||||||
|
|
||||||
- Closing #290. Fixing back button issue in Settings
|
|
||||||
|
|
||||||
- Closing #300. Fixing issues when displaying some special characters.
|
|
||||||
|
|
||||||
- Closing #310. Some feeds don't have icons nor thumbnails.
|
|
||||||
|
|
||||||
- Closing #178. Expending images on tap.
|
|
||||||
|
|
||||||
- Closing #323. Old issue with textview not having the right color.
|
|
||||||
|
|
||||||
- Closing #324. Svg images loading crashes the app.
|
|
||||||
|
|
||||||
- Closing #322. App crashed because of svg images.
|
|
||||||
|
|
||||||
- Closing #236. New sources can be added in Selfoss 2.19.
|
|
||||||
|
|
||||||
- Closing #397 and #355. Tag and Sources filters are now exclusive.
|
|
||||||
|
|
||||||
- Dropped support for android 4, the last version supporting it is v1721030811
|
|
||||||
|
|
||||||
- Added ability to scroll articles up and down using the volume keys #400
|
|
||||||
|
|
||||||
**1.6.x**
|
**1.6.x**
|
||||||
|
|
||||||
- Handling hidden tags.
|
- Handling hidden tags.
|
||||||
@ -70,8 +46,6 @@
|
|||||||
|
|
||||||
- Fixes #215, #208.
|
- Fixes #215, #208.
|
||||||
|
|
||||||
- Fixes #328.
|
|
||||||
|
|
||||||
**1.5.7.x**
|
**1.5.7.x**
|
||||||
|
|
||||||
- Added confirmation to the mark as read and update menues.
|
- Added confirmation to the mark as read and update menues.
|
||||||
|
39
README.md
39
README.md
@ -1 +1,38 @@
|
|||||||
# Project moved to https://github.com/aminecmi/ReaderforSelfoss-multiplatform
|
# ReaderForSelfoss **(Only available from F-Droid)**
|
||||||
|
|
||||||
|
[![Crowdin](https://d322cqt584bo4o.cloudfront.net/readerforselfoss/localized.svg)](https://crowdin.com/project/readerforselfoss)
|
||||||
|
|
||||||
|
It's an RSS Reader for Android, that **only** works with [Selfoss](https://selfoss.aditu.de/)
|
||||||
|
|
||||||
|
**The project is not dead at all.**
|
||||||
|
|
||||||
|
I still want to work on it, but for the last few months, I didn't have that much time to do so.
|
||||||
|
|
||||||
|
If you are a developer, don't hesitate to help with PRs.
|
||||||
|
|
||||||
|
If you are a user, you can still create new issues. I'll fix them when I can.
|
||||||
|
|
||||||
|
<a href="https://f-droid.org/packages/apps.amine.bou.readerforselfoss"><img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="100"></a>
|
||||||
|
|
||||||
|
## Screen captures
|
||||||
|
|
||||||
|
<img src="res//fr-card.png?raw=true" alt="card view" width="400"/> <img src="res//fr-list.png?raw=true" alt="list view" width="400"/>
|
||||||
|
|
||||||
|
## Like my app ?
|
||||||
|
|
||||||
|
<a href="https://www.buymeacoffee.com/aminecmi" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/lato-orange.png" alt="Buy Me A Coffee" style="height: 51px !important;width: 217px !important;" ></a>
|
||||||
|
|
||||||
|
## Want to help ?
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
- [Check what changed](https://github.com/aminecmi/ReaderforSelfoss/blob/master/CHANGELOG.md)
|
||||||
|
- [See what I'm doing](https://github.com/aminecmi/ReaderforSelfoss/projects/1)
|
||||||
|
- [Create an issue, or request a new feature](https://github.com/aminecmi/ReaderforSelfoss/issues)
|
||||||
|
- [Help translation the app](https://crowdin.com/project/readerforselfoss)
|
||||||
|
@ -24,29 +24,25 @@ 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'
|
||||||
|
|
||||||
apply plugin: 'kotlin-kapt'
|
apply plugin: 'kotlin-android-extensions'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileOptions {
|
compileOptions {
|
||||||
// Flag to enable support for the new language APIs
|
|
||||||
coreLibraryDesugaringEnabled true
|
|
||||||
|
|
||||||
sourceCompatibility JavaVersion.VERSION_1_8
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
targetCompatibility JavaVersion.VERSION_1_8
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
}
|
}
|
||||||
compileSdkVersion 31
|
compileSdkVersion 28
|
||||||
buildToolsVersion '31.0.0'
|
buildToolsVersion '28.0.3'
|
||||||
buildFeatures {
|
|
||||||
viewBinding true
|
|
||||||
}
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "apps.amine.bou.readerforselfoss"
|
applicationId "apps.amine.bou.readerforselfoss"
|
||||||
minSdkVersion 21
|
minSdkVersion 16
|
||||||
targetSdkVersion 31
|
targetSdkVersion 28
|
||||||
versionCode versionCodeFromGit()
|
versionCode versionCodeFromGit()
|
||||||
versionName versionNameFromGit()
|
versionName versionNameFromGit()
|
||||||
|
|
||||||
@ -71,14 +67,11 @@ android {
|
|||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
minifyEnabled true
|
minifyEnabled true
|
||||||
shrinkResources true
|
shrinkResources false
|
||||||
proguardFiles getDefaultProguardFile('proguard-android.txt'),
|
proguardFiles getDefaultProguardFile('proguard-android.txt'),
|
||||||
'proguard-rules.pro'
|
'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
debug {
|
debug {
|
||||||
buildConfigField "String", "LOGIN_URL", appLoginUrl
|
|
||||||
buildConfigField "String", "LOGIN_PASSWORD", appLoginPassword
|
|
||||||
buildConfigField "String", "LOGIN_USERNAME", appLoginUsername
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
flavorDimensions "build"
|
flavorDimensions "build"
|
||||||
@ -88,85 +81,73 @@ android {
|
|||||||
dimension "build"
|
dimension "build"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = '1.8'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'androidx.preference:preference-ktx:1.1.1'
|
|
||||||
|
|
||||||
// Testing
|
// Testing
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0-alpha02'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0-beta01'
|
||||||
androidTestImplementation 'androidx.test:runner:1.3.1-alpha02'
|
androidTestImplementation 'androidx.test:runner:1.2.0-beta01'
|
||||||
// Espresso-contrib for DatePicker, RecyclerView, Drawer actions, Accessibility checks, CountingIdlingResource
|
// Espresso-contrib for DatePicker, RecyclerView, Drawer actions, Accessibility checks, CountingIdlingResource
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.4.0-alpha02'
|
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.2.0-beta01'
|
||||||
// Espresso-intents for validation and stubbing of Intents
|
// Espresso-intents for validation and stubbing of Intents
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.4.0-alpha02'
|
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.2.0-beta01'
|
||||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
// Android Support
|
// Android Support
|
||||||
implementation 'androidx.appcompat:appcompat:1.4.1'
|
implementation "androidx.appcompat:appcompat:$androidx_version"
|
||||||
implementation 'com.google.android.material:material:1.5.0'
|
implementation "com.google.android.material:material:1.1.0-alpha06"
|
||||||
implementation 'androidx.recyclerview:recyclerview:1.3.0-alpha01'
|
implementation "androidx.recyclerview:recyclerview:1.1.0-alpha05"
|
||||||
implementation "androidx.legacy:legacy-support-v4:$android_version"
|
implementation "androidx.legacy:legacy-support-v4:$android_version"
|
||||||
implementation 'androidx.vectordrawable:vectordrawable:1.2.0-alpha02'
|
implementation "androidx.vectordrawable:vectordrawable:1.1.0-beta01"
|
||||||
implementation 'androidx.browser:browser:1.4.0'
|
implementation "androidx.browser:browser:$android_version"
|
||||||
implementation "androidx.cardview:cardview:$android_version"
|
implementation "androidx.cardview:cardview:$android_version"
|
||||||
implementation 'androidx.annotation:annotation:1.3.0'
|
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha5'
|
||||||
implementation 'androidx.work:work-runtime-ktx:2.7.1'
|
implementation 'org.jsoup:jsoup:1.13.1'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
|
|
||||||
implementation 'org.jsoup:jsoup:1.14.3'
|
|
||||||
|
|
||||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:1.1.5")
|
|
||||||
|
|
||||||
//multidex
|
//multidex
|
||||||
implementation 'androidx.multidex:multidex:2.0.1'
|
implementation 'androidx.multidex:multidex:2.0.1'
|
||||||
|
|
||||||
// About
|
// About
|
||||||
implementation 'com.mikepenz:aboutlibraries-core:8.9.4'
|
implementation('com.mikepenz:aboutlibraries:6.2.0@aar') {
|
||||||
implementation 'com.mikepenz:aboutlibraries:8.9.4'
|
transitive = true
|
||||||
implementation "com.mikepenz:aboutlibraries-definitions:8.9.4"
|
}
|
||||||
|
|
||||||
// Async
|
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.0'
|
|
||||||
|
|
||||||
// Retrofit + http logging + okhttp
|
// Retrofit + http logging + okhttp
|
||||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
|
||||||
implementation 'com.squareup.okhttp3:logging-interceptor:5.0.0-alpha.3'
|
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.0'
|
||||||
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
|
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
|
||||||
implementation 'com.burgstaller:okhttp-digest:2.5'
|
implementation 'com.burgstaller:okhttp-digest:1.12'
|
||||||
|
|
||||||
// Material-ish things
|
// Material-ish things
|
||||||
implementation 'com.ashokvarma.android:bottom-navigation-bar:2.2.0'
|
implementation 'com.ashokvarma.android:bottom-navigation-bar:2.1.0'
|
||||||
|
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
|
||||||
kapt 'com.github.bumptech.glide:compiler:4.11.0'
|
implementation 'com.github.bumptech.glide:glide:4.1.1'
|
||||||
implementation 'com.github.bumptech.glide:okhttp3-integration:4.1.1'
|
implementation 'com.github.bumptech.glide:okhttp3-integration:4.1.1'
|
||||||
|
|
||||||
// Drawer
|
// Drawer
|
||||||
implementation 'com.mikepenz:materialdrawer:8.4.5'
|
implementation 'co.zsmb:materialdrawer-kt:2.0.2'
|
||||||
|
|
||||||
// Themes
|
// Themes
|
||||||
implementation 'com.52inc:scoops:1.0.0'
|
implementation 'com.52inc:scoops:1.0.0'
|
||||||
implementation 'com.jaredrummler:colorpicker:1.1.0'
|
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:2.1.6'
|
implementation 'me.relex:circleindicator:2.0.0@aar'
|
||||||
implementation "androidx.viewpager2:viewpager2:1.1.0-beta01"
|
|
||||||
|
|
||||||
//PhotoView
|
//PhotoView
|
||||||
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
|
implementation 'com.github.chrisbanes:PhotoView:2.0.0'
|
||||||
|
|
||||||
implementation 'androidx.core:core-ktx:1.7.0'
|
implementation 'androidx.core:core-ktx:1.1.0-beta01'
|
||||||
|
|
||||||
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.0'
|
implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
|
||||||
implementation 'androidx.lifecycle:lifecycle-common-java8:2.4.0'
|
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
|
||||||
|
|
||||||
implementation "androidx.room:room-ktx:2.4.0-beta01"
|
implementation "androidx.room:room-runtime:$room_version"
|
||||||
kapt "androidx.room:room-compiler:2.4.0-beta01"
|
kapt "androidx.room:room-compiler:$room_version"
|
||||||
|
|
||||||
implementation "android.arch.work:work-runtime-ktx:$work_version"
|
implementation "android.arch.work:work-runtime-ktx:$work_version"
|
||||||
}
|
}
|
@ -1,226 +0,0 @@
|
|||||||
{
|
|
||||||
"formatVersion": 1,
|
|
||||||
"database": {
|
|
||||||
"version": 4,
|
|
||||||
"identityHash": "9cf8b03d32f80dfd58160599a1df197d",
|
|
||||||
"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, `icon` TEXT, `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": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"fieldPath": "icon",
|
|
||||||
"columnName": "icon",
|
|
||||||
"affinity": "TEXT",
|
|
||||||
"notNull": false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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, \"9cf8b03d32f80dfd58160599a1df197d\")"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
@ -7,20 +7,23 @@ import androidx.test.espresso.Espresso.onView
|
|||||||
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
||||||
import androidx.test.espresso.action.ViewActions.click
|
import androidx.test.espresso.action.ViewActions.click
|
||||||
import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
|
import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
|
||||||
|
import androidx.test.espresso.action.ViewActions.pressBack
|
||||||
import androidx.test.espresso.action.ViewActions.pressKey
|
import androidx.test.espresso.action.ViewActions.pressKey
|
||||||
import androidx.test.espresso.action.ViewActions.typeText
|
import androidx.test.espresso.action.ViewActions.typeText
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
|
import androidx.test.espresso.contrib.DrawerActions
|
||||||
import androidx.test.espresso.intent.Intents
|
import androidx.test.espresso.intent.Intents
|
||||||
import androidx.test.espresso.intent.Intents.intended
|
import androidx.test.espresso.intent.Intents.intended
|
||||||
|
import androidx.test.espresso.intent.Intents.times
|
||||||
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||||
|
import androidx.test.espresso.matcher.ViewMatchers.isRoot
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
|
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||||
import androidx.test.rule.ActivityTestRule
|
import androidx.test.rule.ActivityTestRule
|
||||||
import androidx.test.runner.AndroidJUnit4
|
import androidx.test.runner.AndroidJUnit4
|
||||||
import android.view.KeyEvent
|
import android.view.KeyEvent
|
||||||
import androidx.test.espresso.matcher.RootMatchers.isDialog
|
|
||||||
import apps.amine.bou.readerforselfoss.utils.Config
|
import apps.amine.bou.readerforselfoss.utils.Config
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@ -81,14 +84,11 @@ class HomeActivityEspressoTest {
|
|||||||
onView(withMenu(id = R.id.refresh, titleId = R.string.menu_home_refresh))
|
onView(withMenu(id = R.id.refresh, titleId = R.string.menu_home_refresh))
|
||||||
.perform(click())
|
.perform(click())
|
||||||
|
|
||||||
onView(withText(android.R.string.ok))
|
|
||||||
.inRoot(isDialog()).check(matches(isDisplayed())).perform(click())
|
|
||||||
|
|
||||||
openActionBarOverflowOrOptionsMenu(context)
|
openActionBarOverflowOrOptionsMenu(context)
|
||||||
|
|
||||||
onView(withText(R.string.action_disconnect)).perform(click())
|
onView(withText(R.string.action_disconnect)).perform(click())
|
||||||
|
|
||||||
intended(hasComponent(LoginActivity::class.java.name))
|
intended(hasComponent(LoginActivity::class.java.name), times(1))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: test articles opening and actions for cards and lists
|
// TODO: test articles opening and actions for cards and lists
|
||||||
|
@ -85,7 +85,7 @@ class LoginActivityEspressoTest {
|
|||||||
|
|
||||||
onView(withId(R.id.signInButton)).perform(click())
|
onView(withId(R.id.signInButton)).perform(click())
|
||||||
|
|
||||||
onView(withId(R.id.urlView)).check(matches(isHintOrErrorEnabled()))
|
onView(withId(R.id.urlLayout)).check(matches(isHintOrErrorEnabled()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add tests for multiple false urls with dialog
|
// TODO: Add tests for multiple false urls with dialog
|
||||||
@ -101,19 +101,19 @@ class LoginActivityEspressoTest {
|
|||||||
|
|
||||||
onView(withId(R.id.signInButton)).perform(click())
|
onView(withId(R.id.signInButton)).perform(click())
|
||||||
|
|
||||||
onView(withId(R.id.loginView)).check(matches(isHintOrErrorEnabled()))
|
onView(withId(R.id.loginLayout)).check(matches(isHintOrErrorEnabled()))
|
||||||
onView(withId(R.id.passwordView)).check(matches(isHintOrErrorEnabled()))
|
onView(withId(R.id.passwordLayout)).check(matches(isHintOrErrorEnabled()))
|
||||||
|
|
||||||
onView(withId(R.id.loginView)).perform(click()).perform(
|
onView(withId(R.id.loginView)).perform(click()).perform(
|
||||||
typeText(username),
|
typeText(username),
|
||||||
closeSoftKeyboard()
|
closeSoftKeyboard()
|
||||||
)
|
)
|
||||||
|
|
||||||
onView(withId(R.id.passwordView)).check(matches(isHintOrErrorEnabled()))
|
onView(withId(R.id.passwordLayout)).check(matches(isHintOrErrorEnabled()))
|
||||||
|
|
||||||
onView(withId(R.id.signInButton)).perform(click())
|
onView(withId(R.id.signInButton)).perform(click())
|
||||||
|
|
||||||
onView(withId(R.id.passwordView)).check(
|
onView(withId(R.id.passwordLayout)).check(
|
||||||
matches(
|
matches(
|
||||||
isHintOrErrorEnabled()
|
isHintOrErrorEnabled()
|
||||||
)
|
)
|
||||||
@ -141,9 +141,9 @@ class LoginActivityEspressoTest {
|
|||||||
|
|
||||||
onView(withId(R.id.signInButton)).perform(click())
|
onView(withId(R.id.signInButton)).perform(click())
|
||||||
|
|
||||||
onView(withId(R.id.urlView)).check(matches(isHintOrErrorEnabled()))
|
onView(withId(R.id.urlLayout)).check(matches(isHintOrErrorEnabled()))
|
||||||
onView(withId(R.id.loginView)).check(matches(isHintOrErrorEnabled()))
|
onView(withId(R.id.loginLayout)).check(matches(isHintOrErrorEnabled()))
|
||||||
onView(withId(R.id.passwordView)).check(matches(isHintOrErrorEnabled()))
|
onView(withId(R.id.passwordLayout)).check(matches(isHintOrErrorEnabled()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -167,7 +167,6 @@ class LoginActivityEspressoTest {
|
|||||||
|
|
||||||
onView(withId(R.id.signInButton)).perform(click())
|
onView(withId(R.id.signInButton)).perform(click())
|
||||||
|
|
||||||
Thread.sleep(2000)
|
|
||||||
intended(hasComponent(HomeActivity::class.java.name))
|
intended(hasComponent(HomeActivity::class.java.name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package apps.amine.bou.readerforselfoss
|
package apps.amine.bou.readerforselfoss
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
@ -11,7 +10,6 @@ import androidx.test.espresso.intent.Intents.times
|
|||||||
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
||||||
import androidx.test.rule.ActivityTestRule
|
import androidx.test.rule.ActivityTestRule
|
||||||
import androidx.test.runner.AndroidJUnit4
|
import androidx.test.runner.AndroidJUnit4
|
||||||
import apps.amine.bou.readerforselfoss.utils.Config
|
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
|
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@ -24,9 +22,6 @@ class MainActivityEspressoTest {
|
|||||||
|
|
||||||
lateinit var intent: Intent
|
lateinit var intent: Intent
|
||||||
lateinit var preferencesEditor: SharedPreferences.Editor
|
lateinit var preferencesEditor: SharedPreferences.Editor
|
||||||
private lateinit var url: String
|
|
||||||
private lateinit var username: String
|
|
||||||
private lateinit var password: String
|
|
||||||
|
|
||||||
@Rule @JvmField
|
@Rule @JvmField
|
||||||
val rule = ActivityTestRule(MainActivity::class.java, true, false)
|
val rule = ActivityTestRule(MainActivity::class.java, true, false)
|
||||||
@ -37,39 +32,31 @@ class MainActivityEspressoTest {
|
|||||||
val context = getInstrumentation().targetContext
|
val context = getInstrumentation().targetContext
|
||||||
|
|
||||||
// create a SharedPreferences editor
|
// create a SharedPreferences editor
|
||||||
preferencesEditor = context.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE).edit()
|
preferencesEditor = PreferenceManager.getDefaultSharedPreferences(context).edit()
|
||||||
|
|
||||||
url = BuildConfig.LOGIN_URL
|
|
||||||
username = BuildConfig.LOGIN_USERNAME
|
|
||||||
password = BuildConfig.LOGIN_PASSWORD
|
|
||||||
|
|
||||||
Intents.init()
|
Intents.init()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun checkFirstOpenLaunchesIntro() {
|
fun checkFirstOpenLaunchesIntro() {
|
||||||
preferencesEditor.putString("url", "")
|
preferencesEditor.putBoolean("firstStart", true)
|
||||||
preferencesEditor.putString("password", "")
|
|
||||||
preferencesEditor.putString("login", "")
|
|
||||||
preferencesEditor.commit()
|
|
||||||
|
|
||||||
rule.launchActivity(intent)
|
|
||||||
|
|
||||||
intended(hasComponent(LoginActivity::class.java.name))
|
|
||||||
intended(hasComponent(HomeActivity::class.java.name), times(0))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun checkNotFirstOpenLaunchesLogin() {
|
|
||||||
preferencesEditor.putString("url", url)
|
|
||||||
preferencesEditor.putString("password", password)
|
|
||||||
preferencesEditor.putString("login", username)
|
|
||||||
preferencesEditor.commit()
|
preferencesEditor.commit()
|
||||||
|
|
||||||
rule.launchActivity(intent)
|
rule.launchActivity(intent)
|
||||||
|
|
||||||
intended(hasComponent(MainActivity::class.java.name))
|
intended(hasComponent(MainActivity::class.java.name))
|
||||||
intended(hasComponent(HomeActivity::class.java.name))
|
intended(hasComponent(LoginActivity::class.java.name), times(0))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun checkNotFirstOpenLaunchesLogin() {
|
||||||
|
preferencesEditor.putBoolean("firstStart", false)
|
||||||
|
preferencesEditor.commit()
|
||||||
|
|
||||||
|
rule.launchActivity(intent)
|
||||||
|
|
||||||
|
intended(hasComponent(MainActivity::class.java.name))
|
||||||
|
intended(hasComponent(LoginActivity::class.java.name))
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package apps.amine.bou.readerforselfoss
|
package apps.amine.bou.readerforselfoss
|
||||||
|
|
||||||
|
import com.google.android.material.textfield.TextInputLayout
|
||||||
import androidx.test.espresso.matcher.ViewMatchers
|
import androidx.test.espresso.matcher.ViewMatchers
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.EditText
|
|
||||||
import org.hamcrest.Description
|
import org.hamcrest.Description
|
||||||
import org.hamcrest.Matcher
|
import org.hamcrest.Matcher
|
||||||
import org.hamcrest.Matchers
|
import org.hamcrest.Matchers
|
||||||
@ -14,11 +14,11 @@ fun isHintOrErrorEnabled(): Matcher<View> =
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun matchesSafely(item: View?): Boolean {
|
override fun matchesSafely(item: View?): Boolean {
|
||||||
if (item !is EditText) {
|
if (item !is TextInputLayout) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return item.error.isNotEmpty()
|
return item.isHintEnabled || item.isErrorEnabled
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
package apps.amine.bou.readerforselfoss.utils
|
|
||||||
|
|
||||||
import org.junit.Test
|
|
||||||
|
|
||||||
class DateUtilsTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun parseDateV4() {
|
|
||||||
|
|
||||||
Config.apiVersion = 4
|
|
||||||
val dateString = "2013-04-07T13:43:00+01:00"
|
|
||||||
|
|
||||||
val milliseconds = parseDate(dateString).toEpochMilli()
|
|
||||||
val correctMilliseconds : Long = 1365338580000
|
|
||||||
|
|
||||||
assert(milliseconds == correctMilliseconds)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun parseDateV1() {
|
|
||||||
Config.apiVersion = 0
|
|
||||||
val dateString = "2013-04-07 13:43:00"
|
|
||||||
|
|
||||||
val milliseconds = parseDate(dateString).toEpochMilli()
|
|
||||||
val correctMilliseconds = 1365342180000
|
|
||||||
|
|
||||||
assert(milliseconds == correctMilliseconds)
|
|
||||||
}
|
|
||||||
}
|
|
@ -15,8 +15,7 @@
|
|||||||
android:theme="@style/NoBar">
|
android:theme="@style/NoBar">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:theme="@style/SplashTheme"
|
android:theme="@style/SplashTheme">
|
||||||
android:exported="true">
|
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
@ -49,8 +48,7 @@
|
|||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".AddSourceActivity"
|
android:name=".AddSourceActivity"
|
||||||
android:parentActivityName=".SourcesActivity"
|
android:parentActivityName=".SourcesActivity">
|
||||||
android:exported="true">
|
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".SourcesActivity" />
|
android:value=".SourcesActivity" />
|
||||||
|
@ -2,8 +2,9 @@ package apps.amine.bou.readerforselfoss
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import android.view.View
|
import android.view.View
|
||||||
@ -22,11 +23,11 @@ import apps.amine.bou.readerforselfoss.themes.Toppings
|
|||||||
import apps.amine.bou.readerforselfoss.utils.Config
|
import apps.amine.bou.readerforselfoss.utils.Config
|
||||||
import apps.amine.bou.readerforselfoss.utils.isBaseUrlValid
|
import apps.amine.bou.readerforselfoss.utils.isBaseUrlValid
|
||||||
import com.ftinc.scoop.Scoop
|
import com.ftinc.scoop.Scoop
|
||||||
|
import kotlinx.android.synthetic.main.activity_add_source.*
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.Callback
|
import retrofit2.Callback
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import android.graphics.PorterDuff
|
import android.graphics.PorterDuff
|
||||||
import apps.amine.bou.readerforselfoss.databinding.ActivityAddSourceBinding
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -36,39 +37,50 @@ class AddSourceActivity : AppCompatActivity() {
|
|||||||
private lateinit var api: SelfossApi
|
private lateinit var api: SelfossApi
|
||||||
|
|
||||||
private lateinit var appColors: AppColors
|
private lateinit var appColors: AppColors
|
||||||
private lateinit var binding: ActivityAddSourceBinding
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
appColors = AppColors(this@AddSourceActivity)
|
appColors = AppColors(this@AddSourceActivity)
|
||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityAddSourceBinding.inflate(layoutInflater)
|
|
||||||
val view = binding.root
|
|
||||||
|
|
||||||
setContentView(view)
|
setContentView(R.layout.activity_add_source)
|
||||||
|
|
||||||
val scoop = Scoop.getInstance()
|
val scoop = Scoop.getInstance()
|
||||||
scoop.bind(this, Toppings.PRIMARY.value, binding.toolbar)
|
scoop.bind(this, Toppings.PRIMARY.value, toolbar)
|
||||||
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
|
||||||
|
}
|
||||||
|
|
||||||
val drawable = binding.nameInput.background
|
val drawable = nameInput.background
|
||||||
drawable.setTint(appColors.colorAccent)
|
drawable.setColorFilter(appColors.colorAccent, PorterDuff.Mode.SRC_ATOP)
|
||||||
|
|
||||||
|
|
||||||
// TODO: clean
|
// TODO: clean
|
||||||
binding.nameInput.background = drawable
|
if(Build.VERSION.SDK_INT > 16) {
|
||||||
|
nameInput.background = drawable
|
||||||
|
} else{
|
||||||
|
nameInput.setBackgroundDrawable(drawable)
|
||||||
|
}
|
||||||
|
|
||||||
val drawable1 = binding.sourceUri.background
|
val drawable1 = sourceUri.background
|
||||||
drawable1.setTint(appColors.colorAccent)
|
drawable1.setColorFilter(appColors.colorAccent, PorterDuff.Mode.SRC_ATOP)
|
||||||
|
|
||||||
binding.sourceUri.background = drawable1
|
if(Build.VERSION.SDK_INT > 16) {
|
||||||
|
sourceUri.background = drawable1
|
||||||
|
} else{
|
||||||
|
sourceUri.setBackgroundDrawable(drawable1)
|
||||||
|
}
|
||||||
|
|
||||||
val drawable2 = binding.tags.background
|
val drawable2 = tags.background
|
||||||
drawable2.setTint(appColors.colorAccent)
|
drawable2.setColorFilter(appColors.colorAccent, PorterDuff.Mode.SRC_ATOP)
|
||||||
|
|
||||||
binding.tags.background = drawable2
|
if(Build.VERSION.SDK_INT > 16) {
|
||||||
|
tags.background = drawable2
|
||||||
|
} else{
|
||||||
|
tags.setBackgroundDrawable(drawable2)
|
||||||
|
}
|
||||||
|
|
||||||
setSupportActionBar(binding.toolbar)
|
setSupportActionBar(toolbar)
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||||
|
|
||||||
@ -80,18 +92,18 @@ class AddSourceActivity : AppCompatActivity() {
|
|||||||
this,
|
this,
|
||||||
this@AddSourceActivity,
|
this@AddSourceActivity,
|
||||||
settings.getBoolean("isSelfSignedCert", false),
|
settings.getBoolean("isSelfSignedCert", false),
|
||||||
prefs.getString("api_timeout", "-1")!!.toLong()
|
prefs.getString("api_timeout", "-1").toLong()
|
||||||
)
|
)
|
||||||
} catch (e: IllegalArgumentException) {
|
} catch (e: IllegalArgumentException) {
|
||||||
mustLoginToAddSource()
|
mustLoginToAddSource()
|
||||||
}
|
}
|
||||||
|
|
||||||
maybeGetDetailsFromIntentSharing(intent, binding.sourceUri, binding.nameInput)
|
maybeGetDetailsFromIntentSharing(intent, sourceUri, nameInput)
|
||||||
|
|
||||||
binding.saveBtn.setTextColor(appColors.colorAccent)
|
saveBtn.setTextColor(appColors.colorAccent)
|
||||||
|
|
||||||
binding.saveBtn.setOnClickListener {
|
saveBtn.setOnClickListener {
|
||||||
handleSaveSource(binding.tags, binding.nameInput.text.toString(), binding.sourceUri.text.toString(), api)
|
handleSaveSource(tags, nameInput.text.toString(), sourceUri.text.toString(), api!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,7 +114,7 @@ class AddSourceActivity : AppCompatActivity() {
|
|||||||
if (config.baseUrl.isEmpty() || !config.baseUrl.isBaseUrlValid(this@AddSourceActivity)) {
|
if (config.baseUrl.isEmpty() || !config.baseUrl.isBaseUrlValid(this@AddSourceActivity)) {
|
||||||
mustLoginToAddSource()
|
mustLoginToAddSource()
|
||||||
} else {
|
} else {
|
||||||
handleSpoutsSpinner(binding.spoutsSpinner, api, binding.progress, binding.formContainer)
|
handleSpoutsSpinner(spoutsSpinner, api, progress, formContainer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -191,78 +203,42 @@ class AddSourceActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
private fun handleSaveSource(tags: EditText, title: String, url: String, api: SelfossApi) {
|
private fun handleSaveSource(tags: EditText, title: String, url: String, api: SelfossApi) {
|
||||||
|
|
||||||
val sourceDetailsUnavailable =
|
val sourceDetailsAvailable =
|
||||||
title.isEmpty() || url.isEmpty() || mSpoutsValue == null || mSpoutsValue!!.isEmpty()
|
title.isEmpty() || url.isEmpty() || mSpoutsValue == null || mSpoutsValue!!.isEmpty()
|
||||||
|
|
||||||
when {
|
if (sourceDetailsAvailable) {
|
||||||
sourceDetailsUnavailable -> {
|
Toast.makeText(this, R.string.form_not_complete, Toast.LENGTH_SHORT).show()
|
||||||
Toast.makeText(this, R.string.form_not_complete, Toast.LENGTH_SHORT).show()
|
} else {
|
||||||
}
|
api.createSource(
|
||||||
PreferenceManager.getDefaultSharedPreferences(this).getInt("apiVersionMajor", 0) > 1 -> {
|
title,
|
||||||
val tagList = tags.text.toString().split(",").map { it.trim() }
|
url,
|
||||||
api.createSourceApi2(
|
mSpoutsValue!!,
|
||||||
title,
|
tags.text.toString(),
|
||||||
url,
|
""
|
||||||
mSpoutsValue!!,
|
).enqueue(object : Callback<SuccessResponse> {
|
||||||
tagList,
|
override fun onResponse(
|
||||||
""
|
call: Call<SuccessResponse>,
|
||||||
).enqueue(object : Callback<SuccessResponse> {
|
response: Response<SuccessResponse>
|
||||||
override fun onResponse(
|
) {
|
||||||
call: Call<SuccessResponse>,
|
if (response.body() != null && response.body()!!.isSuccess) {
|
||||||
response: Response<SuccessResponse>
|
finish()
|
||||||
) {
|
} else {
|
||||||
if (response.body() != null && response.body()!!.isSuccess) {
|
|
||||||
finish()
|
|
||||||
} else {
|
|
||||||
Toast.makeText(
|
|
||||||
this@AddSourceActivity,
|
|
||||||
R.string.cant_create_source,
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
this@AddSourceActivity,
|
this@AddSourceActivity,
|
||||||
R.string.cant_create_source,
|
R.string.cant_create_source,
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
api.createSource(
|
|
||||||
title,
|
|
||||||
url,
|
|
||||||
mSpoutsValue!!,
|
|
||||||
tags.text.toString(),
|
|
||||||
""
|
|
||||||
).enqueue(object : Callback<SuccessResponse> {
|
|
||||||
override fun onResponse(
|
|
||||||
call: Call<SuccessResponse>,
|
|
||||||
response: Response<SuccessResponse>
|
|
||||||
) {
|
|
||||||
if (response.body() != null && response.body()!!.isSuccess) {
|
|
||||||
finish()
|
|
||||||
} else {
|
|
||||||
Toast.makeText(
|
|
||||||
this@AddSourceActivity,
|
|
||||||
R.string.cant_create_source,
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
this@AddSourceActivity,
|
this@AddSourceActivity,
|
||||||
R.string.cant_create_source,
|
R.string.cant_create_source,
|
||||||
Toast.LENGTH_SHORT
|
Toast.LENGTH_SHORT
|
||||||
).show()
|
).show()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -3,34 +3,29 @@ package apps.amine.bou.readerforselfoss
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.FragmentManager
|
||||||
import androidx.fragment.app.FragmentActivity
|
import androidx.fragment.app.FragmentStatePagerAdapter
|
||||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
|
||||||
import apps.amine.bou.readerforselfoss.databinding.ActivityImageBinding
|
|
||||||
import apps.amine.bou.readerforselfoss.fragments.ImageFragment
|
import apps.amine.bou.readerforselfoss.fragments.ImageFragment
|
||||||
|
import kotlinx.android.synthetic.main.activity_reader.*
|
||||||
|
|
||||||
class ImageActivity : AppCompatActivity() {
|
class ImageActivity : AppCompatActivity() {
|
||||||
private lateinit var allImages : ArrayList<String>
|
private lateinit var allImages : ArrayList<String>
|
||||||
private var position : Int = 0
|
private var position : Int = 0
|
||||||
|
|
||||||
private lateinit var binding: ActivityImageBinding
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityImageBinding.inflate(layoutInflater)
|
|
||||||
val view = binding.root
|
|
||||||
|
|
||||||
setContentView(view)
|
setContentView(R.layout.activity_image)
|
||||||
|
|
||||||
setSupportActionBar(binding.toolBar)
|
setSupportActionBar(toolBar)
|
||||||
supportActionBar?.setDisplayShowTitleEnabled(false)
|
supportActionBar?.setDisplayShowTitleEnabled(false)
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
|
||||||
allImages = intent.getStringArrayListExtra("allImages") as ArrayList<String>
|
allImages = intent.getStringArrayListExtra("allImages")
|
||||||
position = intent.getIntExtra("position", 0)
|
position = intent.getIntExtra("position", 0)
|
||||||
|
|
||||||
binding.pager.adapter = ScreenSlidePagerAdapter(this)
|
pager.adapter = ScreenSlidePagerAdapter(supportFragmentManager)
|
||||||
binding.pager.setCurrentItem(position, false)
|
pager.currentItem = position
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
@ -44,10 +39,14 @@ class ImageActivity : AppCompatActivity() {
|
|||||||
return super.onOptionsItemSelected(item)
|
return super.onOptionsItemSelected(item)
|
||||||
}
|
}
|
||||||
|
|
||||||
private inner class ScreenSlidePagerAdapter(fa: FragmentActivity) : FragmentStateAdapter(fa) {
|
private inner class ScreenSlidePagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm, FragmentStatePagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
|
||||||
|
|
||||||
override fun getItemCount(): Int = allImages.size
|
override fun getCount(): Int {
|
||||||
|
return allImages.size
|
||||||
|
}
|
||||||
|
|
||||||
override fun createFragment(position: Int): Fragment = ImageFragment.newInstance(allImages[position])
|
override fun getItem(position: Int): ImageFragment {
|
||||||
|
return ImageFragment.newInstance(allImages[position])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -14,14 +14,16 @@ import android.view.MenuItem
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.inputmethod.EditorInfo
|
import android.view.inputmethod.EditorInfo
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
import androidx.preference.PreferenceManager
|
import android.widget.Toast
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||||
import apps.amine.bou.readerforselfoss.databinding.ActivityLoginBinding
|
|
||||||
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.isBaseUrlValid
|
import apps.amine.bou.readerforselfoss.utils.isBaseUrlValid
|
||||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
||||||
|
import com.mikepenz.aboutlibraries.Libs
|
||||||
import com.mikepenz.aboutlibraries.LibsBuilder
|
import com.mikepenz.aboutlibraries.LibsBuilder
|
||||||
|
import kotlinx.android.synthetic.main.activity_login.*
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.Callback
|
import retrofit2.Callback
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
@ -37,27 +39,24 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
private lateinit var editor: SharedPreferences.Editor
|
private lateinit var editor: SharedPreferences.Editor
|
||||||
private lateinit var userIdentifier: String
|
private lateinit var userIdentifier: String
|
||||||
private lateinit var appColors: AppColors
|
private lateinit var appColors: AppColors
|
||||||
private lateinit var binding: ActivityLoginBinding
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
appColors = AppColors(this@LoginActivity)
|
appColors = AppColors(this@LoginActivity)
|
||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityLoginBinding.inflate(layoutInflater)
|
|
||||||
val view = binding.root
|
|
||||||
|
|
||||||
setContentView(view)
|
setContentView(R.layout.activity_login)
|
||||||
|
|
||||||
setSupportActionBar(binding.toolbar)
|
setSupportActionBar(toolbar)
|
||||||
|
|
||||||
handleBaseUrlFail()
|
handleBaseUrlFail()
|
||||||
|
|
||||||
settings = PreferenceManager.getDefaultSharedPreferences(applicationContext)
|
settings = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||||
userIdentifier = settings.getString("unique_id", "")!!
|
userIdentifier = settings.getString("unique_id", "")
|
||||||
|
|
||||||
editor = settings.edit()
|
editor = settings.edit()
|
||||||
|
|
||||||
if (settings.getString("url", "")!!.isNotEmpty()) {
|
if (settings.getString("url", "").isNotEmpty()) {
|
||||||
goToMain()
|
goToMain()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,14 +65,14 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
private fun handleActions() {
|
private fun handleActions() {
|
||||||
|
|
||||||
binding.withSelfhostedCert.setOnCheckedChangeListener { _, b ->
|
withSelfhostedCert.setOnCheckedChangeListener { _, b ->
|
||||||
isWithSelfSignedCert = !isWithSelfSignedCert
|
isWithSelfSignedCert = !isWithSelfSignedCert
|
||||||
val visi: Int = if (b) View.VISIBLE else View.GONE
|
val visi: Int = if (b) View.VISIBLE else View.GONE
|
||||||
|
|
||||||
binding.warningText.visibility = visi
|
warningText.visibility = visi
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.passwordView.setOnEditorActionListener(
|
passwordView.setOnEditorActionListener(
|
||||||
TextView.OnEditorActionListener { _, id, _ ->
|
TextView.OnEditorActionListener { _, id, _ ->
|
||||||
if (id == R.id.loginView || id == EditorInfo.IME_NULL) {
|
if (id == R.id.loginView || id == EditorInfo.IME_NULL) {
|
||||||
attemptLogin()
|
attemptLogin()
|
||||||
@ -83,22 +82,22 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
binding.signInButton.setOnClickListener { attemptLogin() }
|
signInButton.setOnClickListener { attemptLogin() }
|
||||||
|
|
||||||
binding.withLogin.setOnCheckedChangeListener { _, b ->
|
withLogin.setOnCheckedChangeListener { _, b ->
|
||||||
isWithLogin = !isWithLogin
|
isWithLogin = !isWithLogin
|
||||||
val visi: Int = if (b) View.VISIBLE else View.GONE
|
val visi: Int = if (b) View.VISIBLE else View.GONE
|
||||||
|
|
||||||
binding.loginView.visibility = visi
|
loginLayout.visibility = visi
|
||||||
binding.passwordView.visibility = visi
|
passwordLayout.visibility = visi
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.withHttpLogin.setOnCheckedChangeListener { _, b ->
|
withHttpLogin.setOnCheckedChangeListener { _, b ->
|
||||||
isWithHTTPLogin = !isWithHTTPLogin
|
isWithHTTPLogin = !isWithHTTPLogin
|
||||||
val visi: Int = if (b) View.VISIBLE else View.GONE
|
val visi: Int = if (b) View.VISIBLE else View.GONE
|
||||||
|
|
||||||
binding.httpLoginView.visibility = visi
|
httpLoginInput.visibility = visi
|
||||||
binding.httpPasswordView.visibility = visi
|
httpPasswordInput.visibility = visi
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,8 +108,9 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
alertDialog.setMessage(getString(R.string.base_url_error))
|
alertDialog.setMessage(getString(R.string.base_url_error))
|
||||||
alertDialog.setButton(
|
alertDialog.setButton(
|
||||||
AlertDialog.BUTTON_NEUTRAL,
|
AlertDialog.BUTTON_NEUTRAL,
|
||||||
"OK"
|
"OK",
|
||||||
) { dialog, _ -> dialog.dismiss() }
|
{ dialog, _ -> dialog.dismiss() }
|
||||||
|
)
|
||||||
alertDialog.show()
|
alertDialog.show()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,25 +124,25 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
private fun attemptLogin() {
|
private fun attemptLogin() {
|
||||||
|
|
||||||
// Reset errors.
|
// Reset errors.
|
||||||
binding.urlView.error = null
|
urlView.error = null
|
||||||
binding.loginView.error = null
|
loginView.error = null
|
||||||
binding.httpLoginView.error = null
|
httpLoginView.error = null
|
||||||
binding.passwordView.error = null
|
passwordView.error = null
|
||||||
binding.httpPasswordView.error = null
|
httpPasswordView.error = null
|
||||||
|
|
||||||
// Store values at the time of the login attempt.
|
// Store values at the time of the login attempt.
|
||||||
val url = binding.urlView.text.toString()
|
val url = urlView.text.toString()
|
||||||
val login = binding.loginView.text.toString()
|
val login = loginView.text.toString()
|
||||||
val httpLogin = binding.httpLoginView.text.toString()
|
val httpLogin = httpLoginView.text.toString()
|
||||||
val password = binding.passwordView.text.toString()
|
val password = passwordView.text.toString()
|
||||||
val httpPassword = binding.httpPasswordView.text.toString()
|
val httpPassword = httpPasswordView.text.toString()
|
||||||
|
|
||||||
var cancel = false
|
var cancel = false
|
||||||
var focusView: View? = null
|
var focusView: View? = null
|
||||||
|
|
||||||
if (!url.isBaseUrlValid(this@LoginActivity)) {
|
if (!url.isBaseUrlValid(this@LoginActivity)) {
|
||||||
binding.urlView.error = getString(R.string.login_url_problem)
|
urlView.error = getString(R.string.login_url_problem)
|
||||||
focusView = binding.urlView
|
focusView = urlView
|
||||||
cancel = true
|
cancel = true
|
||||||
inValidCount++
|
inValidCount++
|
||||||
if (inValidCount == 3) {
|
if (inValidCount == 3) {
|
||||||
@ -151,8 +151,9 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
alertDialog.setMessage(getString(R.string.text_wrong_url))
|
alertDialog.setMessage(getString(R.string.text_wrong_url))
|
||||||
alertDialog.setButton(
|
alertDialog.setButton(
|
||||||
AlertDialog.BUTTON_NEUTRAL,
|
AlertDialog.BUTTON_NEUTRAL,
|
||||||
"OK"
|
"OK",
|
||||||
) { dialog, _ -> dialog.dismiss() }
|
{ dialog, _ -> dialog.dismiss() }
|
||||||
|
)
|
||||||
alertDialog.show()
|
alertDialog.show()
|
||||||
inValidCount = 0
|
inValidCount = 0
|
||||||
}
|
}
|
||||||
@ -160,28 +161,28 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
if (isWithLogin) {
|
if (isWithLogin) {
|
||||||
if (TextUtils.isEmpty(password)) {
|
if (TextUtils.isEmpty(password)) {
|
||||||
binding.passwordView.error = getString(R.string.error_invalid_password)
|
passwordView.error = getString(R.string.error_invalid_password)
|
||||||
focusView = binding.passwordView
|
focusView = passwordView
|
||||||
cancel = true
|
cancel = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TextUtils.isEmpty(login)) {
|
if (TextUtils.isEmpty(login)) {
|
||||||
binding.loginView.error = getString(R.string.error_field_required)
|
loginView.error = getString(R.string.error_field_required)
|
||||||
focusView = binding.loginView
|
focusView = loginView
|
||||||
cancel = true
|
cancel = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isWithHTTPLogin) {
|
if (isWithHTTPLogin) {
|
||||||
if (TextUtils.isEmpty(httpPassword)) {
|
if (TextUtils.isEmpty(httpPassword)) {
|
||||||
binding.httpPasswordView.error = getString(R.string.error_invalid_password)
|
httpPasswordView.error = getString(R.string.error_invalid_password)
|
||||||
focusView = binding.httpPasswordView
|
focusView = httpPasswordView
|
||||||
cancel = true
|
cancel = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TextUtils.isEmpty(httpLogin)) {
|
if (TextUtils.isEmpty(httpLogin)) {
|
||||||
binding.httpLoginView.error = getString(R.string.error_field_required)
|
httpLoginView.error = getString(R.string.error_field_required)
|
||||||
focusView = binding.httpLoginView
|
focusView = httpLoginView
|
||||||
cancel = true
|
cancel = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -215,11 +216,11 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
editor.remove("password")
|
editor.remove("password")
|
||||||
editor.remove("httpPassword")
|
editor.remove("httpPassword")
|
||||||
editor.apply()
|
editor.apply()
|
||||||
binding.urlView.error = getString(R.string.wrong_infos)
|
urlView.error = getString(R.string.wrong_infos)
|
||||||
binding.loginView.error = getString(R.string.wrong_infos)
|
loginView.error = getString(R.string.wrong_infos)
|
||||||
binding.passwordView.error = getString(R.string.wrong_infos)
|
passwordView.error = getString(R.string.wrong_infos)
|
||||||
binding.httpLoginView.error = getString(R.string.wrong_infos)
|
httpLoginView.error = getString(R.string.wrong_infos)
|
||||||
binding.httpPasswordView.error = getString(R.string.wrong_infos)
|
httpPasswordView.error = getString(R.string.wrong_infos)
|
||||||
showProgress(false)
|
showProgress(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,28 +248,28 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
private fun showProgress(show: Boolean) {
|
private fun showProgress(show: Boolean) {
|
||||||
val shortAnimTime = resources.getInteger(android.R.integer.config_shortAnimTime)
|
val shortAnimTime = resources.getInteger(android.R.integer.config_shortAnimTime)
|
||||||
|
|
||||||
binding.loginForm.visibility = if (show) View.GONE else View.VISIBLE
|
loginForm.visibility = if (show) View.GONE else View.VISIBLE
|
||||||
binding.loginForm
|
loginForm
|
||||||
.animate()
|
.animate()
|
||||||
.setDuration(shortAnimTime.toLong())
|
.setDuration(shortAnimTime.toLong())
|
||||||
.alpha(
|
.alpha(
|
||||||
if (show) 0F else 1F
|
if (show) 0F else 1F
|
||||||
).setListener(object : AnimatorListenerAdapter() {
|
).setListener(object : AnimatorListenerAdapter() {
|
||||||
override fun onAnimationEnd(animation: Animator) {
|
override fun onAnimationEnd(animation: Animator) {
|
||||||
binding.loginForm.visibility = if (show) View.GONE else View.VISIBLE
|
loginForm.visibility = if (show) View.GONE else View.VISIBLE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
binding.loginProgress.visibility = if (show) View.VISIBLE else View.GONE
|
loginProgress.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
binding.loginProgress
|
loginProgress
|
||||||
.animate()
|
.animate()
|
||||||
.setDuration(shortAnimTime.toLong())
|
.setDuration(shortAnimTime.toLong())
|
||||||
.alpha(
|
.alpha(
|
||||||
if (show) 1F else 0F
|
if (show) 1F else 0F
|
||||||
).setListener(object : AnimatorListenerAdapter() {
|
).setListener(object : AnimatorListenerAdapter() {
|
||||||
override fun onAnimationEnd(animation: Animator) {
|
override fun onAnimationEnd(animation: Animator) {
|
||||||
binding.loginProgress.visibility = if (show) View.VISIBLE else View.GONE
|
loginProgress.visibility = if (show) View.VISIBLE else View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -283,6 +284,7 @@ class LoginActivity : AppCompatActivity() {
|
|||||||
return when (item.itemId) {
|
return when (item.itemId) {
|
||||||
R.id.about -> {
|
R.id.about -> {
|
||||||
LibsBuilder()
|
LibsBuilder()
|
||||||
|
.withActivityStyle(Libs.ActivityStyle.LIGHT_DARK_TOOLBAR)
|
||||||
.withAboutIconShown(true)
|
.withAboutIconShown(true)
|
||||||
.withAboutVersionShown(true)
|
.withAboutVersionShown(true)
|
||||||
.start(this)
|
.start(this)
|
||||||
|
@ -2,18 +2,14 @@ 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 androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import apps.amine.bou.readerforselfoss.databinding.ActivityMainBinding
|
|
||||||
|
|
||||||
class MainActivity : AppCompatActivity() {
|
class MainActivity : AppCompatActivity() {
|
||||||
private lateinit var binding: ActivityMainBinding
|
|
||||||
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
setContentView(R.layout.activity_main)
|
||||||
val view = binding.root
|
|
||||||
setContentView(view)
|
|
||||||
|
|
||||||
val intent = Intent(this, LoginActivity::class.java)
|
val intent = Intent(this, LoginActivity::class.java)
|
||||||
|
|
||||||
|
@ -6,7 +6,7 @@ import android.content.Context
|
|||||||
import android.graphics.drawable.Drawable
|
import android.graphics.drawable.Drawable
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import androidx.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
import android.widget.ImageView
|
import android.widget.ImageView
|
||||||
import androidx.multidex.MultiDexApplication
|
import androidx.multidex.MultiDexApplication
|
||||||
import apps.amine.bou.readerforselfoss.utils.Config
|
import apps.amine.bou.readerforselfoss.utils.Config
|
||||||
@ -26,7 +26,7 @@ class MyApp : MultiDexApplication() {
|
|||||||
config = Config(baseContext)
|
config = Config(baseContext)
|
||||||
|
|
||||||
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()
|
||||||
editor.putString("unique_id", randomUUID().toString())
|
editor.putString("unique_id", randomUUID().toString())
|
||||||
editor.apply()
|
editor.apply()
|
||||||
@ -60,18 +60,23 @@ class MyApp : MultiDexApplication() {
|
|||||||
|
|
||||||
private fun initDrawerImageLoader() {
|
private fun initDrawerImageLoader() {
|
||||||
DrawerImageLoader.init(object : AbstractDrawerImageLoader() {
|
DrawerImageLoader.init(object : AbstractDrawerImageLoader() {
|
||||||
override fun set(imageView: ImageView, uri: Uri, placeholder: Drawable, tag: String?) {
|
override fun set(
|
||||||
Glide.with(imageView.context)
|
imageView: ImageView?,
|
||||||
|
uri: Uri?,
|
||||||
|
placeholder: Drawable?,
|
||||||
|
tag: String?
|
||||||
|
) {
|
||||||
|
Glide.with(imageView?.context)
|
||||||
.loadMaybeBasicAuth(config, uri.toString())
|
.loadMaybeBasicAuth(config, uri.toString())
|
||||||
.apply(RequestOptions.fitCenterTransform().placeholder(placeholder))
|
.apply(RequestOptions.fitCenterTransform().placeholder(placeholder))
|
||||||
.into(imageView)
|
.into(imageView)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun cancel(imageView: ImageView) {
|
override fun cancel(imageView: ImageView?) {
|
||||||
Glide.with(imageView.context).clear(imageView)
|
Glide.with(imageView?.context).clear(imageView)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun placeholder(ctx: Context, tag: String?): Drawable {
|
override fun placeholder(ctx: Context?, tag: String?): Drawable {
|
||||||
return baseContext.resources.getDrawable(R.mipmap.ic_launcher)
|
return baseContext.resources.getDrawable(R.mipmap.ic_launcher)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -2,39 +2,50 @@ package apps.amine.bou.readerforselfoss
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import android.graphics.Color
|
import android.graphics.drawable.ColorDrawable
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.KeyEvent
|
import android.preference.PreferenceManager
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.fragment.app.FragmentManager
|
||||||
|
import androidx.fragment.app.FragmentStatePagerAdapter
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.viewpager.widget.ViewPager
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import 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.widget.Toast
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import androidx.fragment.app.FragmentActivity
|
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
|
||||||
import androidx.viewpager2.widget.ViewPager2
|
|
||||||
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.databinding.ActivityReaderBinding
|
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.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_1_2
|
||||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_2_3
|
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_2_3
|
||||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_3_4
|
|
||||||
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.utils.Config
|
import apps.amine.bou.readerforselfoss.utils.Config
|
||||||
import apps.amine.bou.readerforselfoss.utils.SharedItems
|
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
||||||
|
import apps.amine.bou.readerforselfoss.utils.persistence.toEntity
|
||||||
|
import apps.amine.bou.readerforselfoss.utils.succeeded
|
||||||
import apps.amine.bou.readerforselfoss.utils.toggleStar
|
import apps.amine.bou.readerforselfoss.utils.toggleStar
|
||||||
import com.ftinc.scoop.Scoop
|
import com.ftinc.scoop.Scoop
|
||||||
|
import kotlinx.android.synthetic.main.activity_reader.*
|
||||||
|
import me.relex.circleindicator.CircleIndicator
|
||||||
|
import retrofit2.Call
|
||||||
|
import retrofit2.Callback
|
||||||
|
import retrofit2.Response
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
class ReaderActivity : AppCompatActivity() {
|
class ReaderActivity : AppCompatActivity() {
|
||||||
|
|
||||||
private var markOnScroll: Boolean = false
|
private var markOnScroll: Boolean = false
|
||||||
private var currentItem: Int = 0
|
private var currentItem: Int = 0
|
||||||
private lateinit var userIdentifier: String
|
private lateinit var userIdentifier: String
|
||||||
private lateinit var appColors: AppColors
|
|
||||||
|
|
||||||
private lateinit var api: SelfossApi
|
private lateinit var api: SelfossApi
|
||||||
|
|
||||||
@ -42,18 +53,14 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
private lateinit var db: AppDatabase
|
private lateinit var db: AppDatabase
|
||||||
private lateinit var prefs: SharedPreferences
|
private lateinit var prefs: SharedPreferences
|
||||||
private lateinit var binding: ActivityReaderBinding
|
|
||||||
|
|
||||||
private var activeAlignment: Int = 1
|
private var activeAlignment: Int = 1
|
||||||
private val JUSTIFY = 1
|
val JUSTIFY = 1
|
||||||
private val ALIGN_LEFT = 2
|
val ALIGN_LEFT = 2
|
||||||
|
|
||||||
private fun showMenuItem(willAddToFavorite: Boolean) {
|
private fun showMenuItem(willAddToFavorite: Boolean) {
|
||||||
if (willAddToFavorite) {
|
toolbarMenu.findItem(R.id.save).isVisible = willAddToFavorite
|
||||||
toolbarMenu.findItem(R.id.star).icon.setTint(Color.WHITE)
|
toolbarMenu.findItem(R.id.unsave).isVisible = !willAddToFavorite
|
||||||
} else {
|
|
||||||
toolbarMenu.findItem(R.id.star).icon.setTint(Color.RED)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun canFavorite() {
|
private fun canFavorite() {
|
||||||
@ -68,22 +75,21 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
appColors = AppColors(this)
|
|
||||||
binding = ActivityReaderBinding.inflate(layoutInflater)
|
|
||||||
val view = binding.root
|
|
||||||
|
|
||||||
setContentView(view)
|
setContentView(R.layout.activity_reader)
|
||||||
|
|
||||||
db = Room.databaseBuilder(
|
db = Room.databaseBuilder(
|
||||||
applicationContext,
|
applicationContext,
|
||||||
AppDatabase::class.java, "selfoss-database"
|
AppDatabase::class.java, "selfoss-database"
|
||||||
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).addMigrations(MIGRATION_3_4).build()
|
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).build()
|
||||||
|
|
||||||
val scoop = Scoop.getInstance()
|
val scoop = Scoop.getInstance()
|
||||||
scoop.bind(this, Toppings.PRIMARY.value, binding.toolBar)
|
scoop.bind(this, Toppings.PRIMARY.value, toolBar)
|
||||||
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
|
||||||
|
}
|
||||||
|
|
||||||
setSupportActionBar(binding.toolBar)
|
setSupportActionBar(toolBar)
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||||
|
|
||||||
@ -93,7 +99,7 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
editor = prefs.edit()
|
editor = prefs.edit()
|
||||||
|
|
||||||
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)
|
activeAlignment = prefs.getInt("text_align", JUSTIFY)
|
||||||
|
|
||||||
@ -101,7 +107,7 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
this,
|
this,
|
||||||
this@ReaderActivity,
|
this@ReaderActivity,
|
||||||
settings.getBoolean("isSelfSignedCert", false),
|
settings.getBoolean("isSelfSignedCert", false),
|
||||||
prefs.getString("api_timeout", "-1")!!.toLong()
|
prefs.getString("api_timeout", "-1").toLong()
|
||||||
)
|
)
|
||||||
|
|
||||||
if (allItems.isEmpty()) {
|
if (allItems.isEmpty()) {
|
||||||
@ -112,55 +118,107 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
readItem(allItems[currentItem])
|
readItem(allItems[currentItem])
|
||||||
|
|
||||||
binding.pager.adapter = ScreenSlidePagerAdapter(this)
|
pager.adapter =
|
||||||
binding.pager.setCurrentItem(currentItem, false)
|
ScreenSlidePagerAdapter(supportFragmentManager, AppColors(this@ReaderActivity))
|
||||||
|
pager.currentItem = currentItem
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
|
||||||
binding.indicator.setViewPager(binding.pager)
|
notifyAdapter()
|
||||||
|
|
||||||
|
pager.setPageTransformer(true, DepthPageTransformer())
|
||||||
|
(indicator as CircleIndicator).setViewPager(pager)
|
||||||
|
|
||||||
|
pager.addOnPageChangeListener(
|
||||||
|
object : ViewPager.SimpleOnPageChangeListener() {
|
||||||
|
|
||||||
|
override fun onPageSelected(position: Int) {
|
||||||
|
|
||||||
|
if (allItems[position].starred) {
|
||||||
|
canRemoveFromFavorite()
|
||||||
|
} else {
|
||||||
|
canFavorite()
|
||||||
|
}
|
||||||
|
readItem(allItems[pager.currentItem])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun readItem(item: Item) {
|
fun readItem(item: Item) {
|
||||||
if (markOnScroll) {
|
if (markOnScroll) {
|
||||||
SharedItems.readItem(applicationContext, api, db, item)
|
thread {
|
||||||
|
db.itemsDao().delete(item.toEntity())
|
||||||
}
|
}
|
||||||
}
|
if (this@ReaderActivity.isNetworkAccessible(this@ReaderActivity.findViewById(R.id.reader_activity_view))) {
|
||||||
|
api.markItem(item.id).enqueue(
|
||||||
|
object : Callback<SuccessResponse> {
|
||||||
|
override fun onResponse(
|
||||||
|
call: Call<SuccessResponse>,
|
||||||
|
response: Response<SuccessResponse>
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
override fun onSaveInstanceState(oldInstanceState: Bundle) {
|
override fun onFailure(
|
||||||
super.onSaveInstanceState(oldInstanceState)
|
call: Call<SuccessResponse>,
|
||||||
oldInstanceState.clear()
|
t: Throwable
|
||||||
}
|
) {
|
||||||
|
thread {
|
||||||
private inner class ScreenSlidePagerAdapter(fa: FragmentActivity) :
|
db.itemsDao().insertAllItems(item.toEntity())
|
||||||
FragmentStateAdapter(fa) {
|
}
|
||||||
|
}
|
||||||
override fun getItemCount(): Int = allItems.size
|
}
|
||||||
|
)
|
||||||
override fun createFragment(position: Int): Fragment = ArticleFragment.newInstance(allItems[position])
|
} else {
|
||||||
|
thread {
|
||||||
}
|
db.actionsDao().insertAllActions(ActionEntity(item.id, true, false, false, false))
|
||||||
|
}
|
||||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
|
||||||
return when (keyCode) {
|
|
||||||
KeyEvent.KEYCODE_VOLUME_DOWN -> {
|
|
||||||
val currentFragment = supportFragmentManager.findFragmentByTag("f" + binding.pager.currentItem) as ArticleFragment
|
|
||||||
currentFragment.scrollDown()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
KeyEvent.KEYCODE_VOLUME_UP -> {
|
|
||||||
val currentFragment = supportFragmentManager.findFragmentByTag("f" + binding.pager.currentItem) as ArticleFragment
|
|
||||||
currentFragment.scrollUp()
|
|
||||||
true
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
super.onKeyDown(keyCode, event)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun alignmentMenu(showJustify: Boolean) {
|
private fun notifyAdapter() {
|
||||||
|
(pager.adapter as ScreenSlidePagerAdapter).notifyDataSetChanged()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onPause() {
|
||||||
|
super.onPause()
|
||||||
|
if (markOnScroll) {
|
||||||
|
pager.clearOnPageChangeListeners()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(oldInstanceState: Bundle) {
|
||||||
|
super.onSaveInstanceState(oldInstanceState)
|
||||||
|
oldInstanceState!!.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
private inner class ScreenSlidePagerAdapter(fm: FragmentManager, val appColors: AppColors) :
|
||||||
|
FragmentStatePagerAdapter(fm) {
|
||||||
|
|
||||||
|
override fun getCount(): Int {
|
||||||
|
return allItems.size
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItem(position: Int): ArticleFragment {
|
||||||
|
return ArticleFragment.newInstance(position, allItems)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun startUpdate(container: ViewGroup) {
|
||||||
|
super.startUpdate(container)
|
||||||
|
|
||||||
|
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_left).isVisible = !showJustify
|
||||||
toolbarMenu.findItem(R.id.align_justify).isVisible = showJustify
|
toolbarMenu.findItem(R.id.align_justify).isVisible = showJustify
|
||||||
}
|
}
|
||||||
@ -170,7 +228,7 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
inflater.inflate(R.menu.reader_menu, menu)
|
inflater.inflate(R.menu.reader_menu, menu)
|
||||||
toolbarMenu = menu
|
toolbarMenu = menu
|
||||||
|
|
||||||
if (allItems.isNotEmpty() && allItems[currentItem].starred) {
|
if (!allItems.isEmpty() && allItems[currentItem].starred) {
|
||||||
canRemoveFromFavorite()
|
canRemoveFromFavorite()
|
||||||
} else {
|
} else {
|
||||||
canFavorite()
|
canFavorite()
|
||||||
@ -181,34 +239,20 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
alignmentMenu(true)
|
alignmentMenu(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.pager.registerOnPageChangeCallback(
|
|
||||||
object : ViewPager2.OnPageChangeCallback() {
|
|
||||||
|
|
||||||
override fun onPageSelected(position: Int) {
|
|
||||||
super.onPageSelected(position)
|
|
||||||
|
|
||||||
if (allItems[position].starred) {
|
|
||||||
canRemoveFromFavorite()
|
|
||||||
} else {
|
|
||||||
canFavorite()
|
|
||||||
}
|
|
||||||
readItem(allItems[position])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||||
fun afterSave() {
|
fun afterSave() {
|
||||||
allItems[binding.pager.currentItem] =
|
allItems[pager.currentItem] =
|
||||||
allItems[binding.pager.currentItem].toggleStar()
|
allItems[pager.currentItem].toggleStar()
|
||||||
|
notifyAdapter()
|
||||||
canRemoveFromFavorite()
|
canRemoveFromFavorite()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun afterUnsave() {
|
fun afterUnsave() {
|
||||||
allItems[binding.pager.currentItem] = allItems[binding.pager.currentItem].toggleStar()
|
allItems[pager.currentItem] = allItems[pager.currentItem].toggleStar()
|
||||||
|
notifyAdapter()
|
||||||
canFavorite()
|
canFavorite()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -217,23 +261,62 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
onBackPressed()
|
onBackPressed()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
R.id.star -> {
|
R.id.save -> {
|
||||||
if (allItems[binding.pager.currentItem].starred) {
|
if (this@ReaderActivity.isNetworkAccessible(null)) {
|
||||||
SharedItems.unstarItem(
|
api.starrItem(allItems[pager.currentItem].id)
|
||||||
this@ReaderActivity,
|
.enqueue(object : Callback<SuccessResponse> {
|
||||||
api,
|
override fun onResponse(
|
||||||
db,
|
call: Call<SuccessResponse>,
|
||||||
allItems[binding.pager.currentItem]
|
response: Response<SuccessResponse>
|
||||||
)
|
) {
|
||||||
afterUnsave()
|
afterSave()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(
|
||||||
|
call: Call<SuccessResponse>,
|
||||||
|
t: Throwable
|
||||||
|
) {
|
||||||
|
Toast.makeText(
|
||||||
|
baseContext,
|
||||||
|
R.string.cant_mark_favortie,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
SharedItems.starItem(
|
thread {
|
||||||
this@ReaderActivity,
|
db.actionsDao().insertAllActions(ActionEntity(allItems[pager.currentItem].id, false, false, true, false))
|
||||||
api,
|
afterSave()
|
||||||
db,
|
}
|
||||||
allItems[binding.pager.currentItem]
|
}
|
||||||
)
|
}
|
||||||
afterSave()
|
R.id.unsave -> {
|
||||||
|
if (this@ReaderActivity.isNetworkAccessible(null)) {
|
||||||
|
api.unstarrItem(allItems[pager.currentItem].id)
|
||||||
|
.enqueue(object : Callback<SuccessResponse> {
|
||||||
|
override fun onResponse(
|
||||||
|
call: Call<SuccessResponse>,
|
||||||
|
response: Response<SuccessResponse>
|
||||||
|
) {
|
||||||
|
afterUnsave()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(
|
||||||
|
call: Call<SuccessResponse>,
|
||||||
|
t: Throwable
|
||||||
|
) {
|
||||||
|
Toast.makeText(
|
||||||
|
baseContext,
|
||||||
|
R.string.cant_unmark_favortie,
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
thread {
|
||||||
|
db.actionsDao().insertAllActions(ActionEntity(allItems[pager.currentItem].id, false, false, false, true))
|
||||||
|
afterUnsave()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
R.id.align_left -> {
|
R.id.align_left -> {
|
||||||
|
@ -3,20 +3,21 @@ package apps.amine.bou.readerforselfoss
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.res.ColorStateList
|
import android.content.res.ColorStateList
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
import androidx.recyclerview.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.Source
|
import apps.amine.bou.readerforselfoss.api.selfoss.Source
|
||||||
import apps.amine.bou.readerforselfoss.databinding.ActivitySourcesBinding
|
|
||||||
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.Config
|
import apps.amine.bou.readerforselfoss.utils.Config
|
||||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
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 retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.Callback
|
import retrofit2.Callback
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
@ -24,32 +25,31 @@ import retrofit2.Response
|
|||||||
class SourcesActivity : AppCompatActivity() {
|
class SourcesActivity : AppCompatActivity() {
|
||||||
|
|
||||||
private lateinit var appColors: AppColors
|
private lateinit var appColors: AppColors
|
||||||
private lateinit var binding: ActivitySourcesBinding
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
appColors = AppColors(this@SourcesActivity)
|
appColors = AppColors(this@SourcesActivity)
|
||||||
binding = ActivitySourcesBinding.inflate(layoutInflater)
|
|
||||||
val view = binding.root
|
|
||||||
|
|
||||||
val scoop = Scoop.getInstance()
|
|
||||||
scoop.bind(this, Toppings.PRIMARY.value, binding.toolbar)
|
|
||||||
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
|
|
||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
setContentView(view)
|
setContentView(R.layout.activity_sources)
|
||||||
|
|
||||||
setSupportActionBar(binding.toolbar)
|
val scoop = Scoop.getInstance()
|
||||||
|
scoop.bind(this, Toppings.PRIMARY.value, toolbar)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
setSupportActionBar(toolbar)
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||||
|
|
||||||
binding.fab.rippleColor = appColors.colorAccentDark
|
fab.rippleColor = appColors.colorAccentDark
|
||||||
binding.fab.backgroundTintList = ColorStateList.valueOf(appColors.colorAccent)
|
fab.backgroundTintList = ColorStateList.valueOf(appColors.colorAccent)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
super.onStop()
|
super.onStop()
|
||||||
binding.recyclerView.clearOnScrollListeners()
|
recyclerView.clearOnScrollListeners()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
@ -64,14 +64,14 @@ class SourcesActivity : AppCompatActivity() {
|
|||||||
this,
|
this,
|
||||||
this@SourcesActivity,
|
this@SourcesActivity,
|
||||||
settings.getBoolean("isSelfSignedCert", false),
|
settings.getBoolean("isSelfSignedCert", false),
|
||||||
prefs.getString("api_timeout", "-1")!!.toLong()
|
prefs.getString("api_timeout", "-1").toLong()
|
||||||
)
|
)
|
||||||
var items: ArrayList<Source> = ArrayList()
|
var items: ArrayList<Source> = ArrayList()
|
||||||
|
|
||||||
binding.recyclerView.setHasFixedSize(true)
|
recyclerView.setHasFixedSize(true)
|
||||||
binding.recyclerView.layoutManager = mLayoutManager
|
recyclerView.layoutManager = mLayoutManager
|
||||||
|
|
||||||
if (this@SourcesActivity.isNetworkAccessible(binding.recyclerView)) {
|
if (this@SourcesActivity.isNetworkAccessible(this@SourcesActivity.findViewById(R.id.recyclerView))) {
|
||||||
api.sources.enqueue(object : Callback<List<Source>> {
|
api.sources.enqueue(object : Callback<List<Source>> {
|
||||||
override fun onResponse(
|
override fun onResponse(
|
||||||
call: Call<List<Source>>,
|
call: Call<List<Source>>,
|
||||||
@ -81,7 +81,7 @@ class SourcesActivity : AppCompatActivity() {
|
|||||||
items = response.body() as ArrayList<Source>
|
items = response.body() as ArrayList<Source>
|
||||||
}
|
}
|
||||||
val mAdapter = SourcesListAdapter(this@SourcesActivity, items, api)
|
val mAdapter = SourcesListAdapter(this@SourcesActivity, items, api)
|
||||||
binding.recyclerView.adapter = mAdapter
|
recyclerView.adapter = mAdapter
|
||||||
mAdapter.notifyDataSetChanged()
|
mAdapter.notifyDataSetChanged()
|
||||||
if (items.isEmpty()) {
|
if (items.isEmpty()) {
|
||||||
Toast.makeText(
|
Toast.makeText(
|
||||||
@ -102,7 +102,7 @@ class SourcesActivity : AppCompatActivity() {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.fab.setOnClickListener {
|
fab.setOnClickListener {
|
||||||
startActivity(Intent(this@SourcesActivity, AddSourceActivity::class.java))
|
startActivity(Intent(this@SourcesActivity, AddSourceActivity::class.java))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,25 +2,27 @@ package apps.amine.bou.readerforselfoss.adapters
|
|||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.cardview.widget.CardView
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageView.ScaleType
|
import android.widget.ImageView.ScaleType
|
||||||
|
import android.widget.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.databinding.CardItemBinding
|
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||||
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
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.Config
|
import apps.amine.bou.readerforselfoss.utils.Config
|
||||||
import apps.amine.bou.readerforselfoss.utils.LinkOnTouchListener
|
import apps.amine.bou.readerforselfoss.utils.LinkOnTouchListener
|
||||||
import apps.amine.bou.readerforselfoss.utils.SharedItems
|
|
||||||
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.isNetworkAvailable
|
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
|
||||||
@ -29,6 +31,13 @@ import apps.amine.bou.readerforselfoss.utils.toTextDrawableString
|
|||||||
import com.amulyakhare.textdrawable.TextDrawable
|
import com.amulyakhare.textdrawable.TextDrawable
|
||||||
import com.amulyakhare.textdrawable.util.ColorGenerator
|
import com.amulyakhare.textdrawable.util.ColorGenerator
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
|
import com.like.LikeButton
|
||||||
|
import com.like.OnLikeListener
|
||||||
|
import kotlinx.android.synthetic.main.card_item.view.*
|
||||||
|
import retrofit2.Call
|
||||||
|
import retrofit2.Callback
|
||||||
|
import retrofit2.Response
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
class ItemCardAdapter(
|
class ItemCardAdapter(
|
||||||
override val app: Activity,
|
override val app: Activity,
|
||||||
@ -50,98 +59,144 @@ class ItemCardAdapter(
|
|||||||
c.resources.getDimension(R.dimen.card_image_max_height).toInt()
|
c.resources.getDimension(R.dimen.card_image_max_height).toInt()
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
val binding = CardItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
val v = LayoutInflater.from(c).inflate(R.layout.card_item, parent, false) as CardView
|
||||||
return ViewHolder(binding)
|
return ViewHolder(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
with(holder) {
|
val itm = items[position]
|
||||||
val itm = items[position]
|
|
||||||
|
|
||||||
binding.favButton.isSelected = itm.starred
|
|
||||||
binding.title.text = itm.getTitleDecoded()
|
|
||||||
|
|
||||||
binding.title.setOnTouchListener(LinkOnTouchListener())
|
holder.mView.favButton.isLiked = itm.starred
|
||||||
|
holder.mView.title.text = itm.getTitleDecoded()
|
||||||
|
holder.mView.title.setOnTouchListener(LinkOnTouchListener())
|
||||||
|
|
||||||
binding.title.setLinkTextColor(appColors.colorAccent)
|
holder.mView.title.setLinkTextColor(appColors.colorAccent)
|
||||||
|
|
||||||
binding.sourceTitleAndDate.text = itm.sourceAndDateText()
|
holder.mView.sourceTitleAndDate.text = itm.sourceAndDateText()
|
||||||
|
|
||||||
if (!fullHeightCards) {
|
if (!fullHeightCards) {
|
||||||
binding.itemImage.maxHeight = imageMaxHeight
|
holder.mView.itemImage.maxHeight = imageMaxHeight
|
||||||
binding.itemImage.scaleType = ScaleType.CENTER_CROP
|
holder.mView.itemImage.scaleType = ScaleType.CENTER_CROP
|
||||||
}
|
|
||||||
|
|
||||||
if (itm.getThumbnail(c).isEmpty()) {
|
|
||||||
binding.itemImage.visibility = View.GONE
|
|
||||||
Glide.with(c).clear(binding.itemImage)
|
|
||||||
binding.itemImage.setImageDrawable(null)
|
|
||||||
} else {
|
|
||||||
binding.itemImage.visibility = View.VISIBLE
|
|
||||||
c.bitmapCenterCrop(config, itm.getThumbnail(c), binding.itemImage)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (itm.getIcon(c).isEmpty()) {
|
|
||||||
val color = generator.getColor(itm.getSourceTitle())
|
|
||||||
|
|
||||||
val drawable =
|
|
||||||
TextDrawable
|
|
||||||
.builder()
|
|
||||||
.round()
|
|
||||||
.build(itm.getSourceTitle().toTextDrawableString(c), color)
|
|
||||||
binding.sourceImage.setImageDrawable(drawable)
|
|
||||||
} else {
|
|
||||||
c.circularBitmapDrawable(config, itm.getIcon(c), binding.sourceImage)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (itm.getThumbnail(c).isEmpty()) {
|
||||||
|
holder.mView.itemImage.visibility = View.GONE
|
||||||
|
Glide.with(c).clear(holder.mView.itemImage)
|
||||||
|
holder.mView.itemImage.setImageDrawable(null)
|
||||||
|
} else {
|
||||||
|
holder.mView.itemImage.visibility = View.VISIBLE
|
||||||
|
c.bitmapCenterCrop(config, itm.getThumbnail(c), holder.mView.itemImage)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (itm.getIcon(c).isEmpty()) {
|
||||||
|
val color = generator.getColor(itm.sourcetitle)
|
||||||
|
|
||||||
|
val drawable =
|
||||||
|
TextDrawable
|
||||||
|
.builder()
|
||||||
|
.round()
|
||||||
|
.build(itm.sourcetitle.toTextDrawableString(c), color)
|
||||||
|
holder.mView.sourceImage.setImageDrawable(drawable)
|
||||||
|
} else {
|
||||||
|
c.circularBitmapDrawable(config, itm.getIcon(c), holder.mView.sourceImage)
|
||||||
|
}
|
||||||
|
|
||||||
|
holder.mView.favButton.isLiked = itm.starred
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int {
|
override fun getItemCount(): Int {
|
||||||
return items.size
|
return items.size
|
||||||
}
|
}
|
||||||
|
|
||||||
inner class ViewHolder(val binding: CardItemBinding) : RecyclerView.ViewHolder(binding.root) {
|
inner class ViewHolder(val mView: CardView) : RecyclerView.ViewHolder(mView) {
|
||||||
init {
|
init {
|
||||||
|
mView.setCardBackgroundColor(appColors.cardBackgroundColor)
|
||||||
handleClickListeners()
|
handleClickListeners()
|
||||||
handleCustomTabActions()
|
handleCustomTabActions()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleClickListeners() {
|
private fun handleClickListeners() {
|
||||||
|
|
||||||
binding.favButton.setOnClickListener {
|
mView.favButton.setOnLikeListener(object : OnLikeListener {
|
||||||
val item = items[bindingAdapterPosition]
|
override fun liked(likeButton: LikeButton) {
|
||||||
if (isNetworkAvailable(c)) {
|
val (id) = items[adapterPosition]
|
||||||
if (item.starred) {
|
if (c.isNetworkAccessible(null)) {
|
||||||
SharedItems.unstarItem(c, api, db, item)
|
api.starrItem(id).enqueue(object : Callback<SuccessResponse> {
|
||||||
item.starred = false
|
override fun onResponse(
|
||||||
binding.favButton.isSelected = false
|
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()
|
||||||
|
}
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
SharedItems.starItem(c, api, db, item)
|
thread {
|
||||||
item.starred = true
|
db.actionsDao().insertAllActions(ActionEntity(id, false, false, true, false))
|
||||||
binding.favButton.isSelected = true
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun unLiked(likeButton: LikeButton) {
|
||||||
|
val (id) = items[adapterPosition]
|
||||||
|
if (c.isNetworkAccessible(null)) {
|
||||||
|
api.unstarrItem(id).enqueue(object : Callback<SuccessResponse> {
|
||||||
|
override fun onResponse(
|
||||||
|
call: Call<SuccessResponse>,
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
thread {
|
||||||
|
db.actionsDao().insertAllActions(ActionEntity(id, false, false, false, true))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
mView.shareBtn.setOnClickListener {
|
||||||
|
val item = items[adapterPosition]
|
||||||
|
c.shareLink(item.getLinkDecoded(), item.getTitleDecoded())
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.shareBtn.setOnClickListener {
|
mView.browserBtn.setOnClickListener {
|
||||||
val item = items[bindingAdapterPosition]
|
c.openInBrowserAsNewTask(items[adapterPosition])
|
||||||
c.shareLink(item.getLinkDecoded(), item.getTitleDecoded())
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.browserBtn.setOnClickListener {
|
|
||||||
c.openInBrowserAsNewTask(items[bindingAdapterPosition])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleCustomTabActions() {
|
private fun handleCustomTabActions() {
|
||||||
val customTabsIntent = c.buildCustomTabsIntent()
|
val customTabsIntent = c.buildCustomTabsIntent()
|
||||||
helper.bindCustomTabsService(app)
|
helper.bindCustomTabsService(app)
|
||||||
|
|
||||||
binding.root.setOnClickListener {
|
mView.setOnClickListener {
|
||||||
c.openItemUrl(
|
c.openItemUrl(
|
||||||
items,
|
items,
|
||||||
bindingAdapterPosition,
|
adapterPosition,
|
||||||
items[bindingAdapterPosition].getLinkDecoded(),
|
items[adapterPosition].getLinkDecoded(),
|
||||||
customTabsIntent,
|
customTabsIntent,
|
||||||
internalBrowser,
|
internalBrowser,
|
||||||
articleViewer,
|
articleViewer,
|
||||||
|
@ -2,12 +2,21 @@ package apps.amine.bou.readerforselfoss.adapters
|
|||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import android.text.Spannable
|
||||||
|
import android.text.style.ClickableSpan
|
||||||
|
import android.util.TypedValue
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
import android.widget.TextView
|
||||||
|
import android.widget.Toast
|
||||||
|
import apps.amine.bou.readerforselfoss.R
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||||
import apps.amine.bou.readerforselfoss.databinding.ListItemBinding
|
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||||
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
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.Config
|
import apps.amine.bou.readerforselfoss.utils.Config
|
||||||
@ -16,11 +25,20 @@ 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.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.sourceAndDateText
|
import apps.amine.bou.readerforselfoss.utils.sourceAndDateText
|
||||||
import apps.amine.bou.readerforselfoss.utils.toTextDrawableString
|
import apps.amine.bou.readerforselfoss.utils.toTextDrawableString
|
||||||
import com.amulyakhare.textdrawable.TextDrawable
|
import com.amulyakhare.textdrawable.TextDrawable
|
||||||
import com.amulyakhare.textdrawable.util.ColorGenerator
|
import com.amulyakhare.textdrawable.util.ColorGenerator
|
||||||
|
import com.like.LikeButton
|
||||||
|
import com.like.OnLikeListener
|
||||||
|
import kotlinx.android.synthetic.main.list_item.view.*
|
||||||
|
import retrofit2.Call
|
||||||
|
import retrofit2.Callback
|
||||||
|
import retrofit2.Response
|
||||||
|
import java.util.*
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
|
||||||
class ItemListAdapter(
|
class ItemListAdapter(
|
||||||
@ -40,46 +58,49 @@ class ItemListAdapter(
|
|||||||
private val c: Context = app.baseContext
|
private val c: Context = app.baseContext
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
val binding = ListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
val v = LayoutInflater.from(c).inflate(
|
||||||
return ViewHolder(binding)
|
R.layout.list_item,
|
||||||
|
parent,
|
||||||
|
false
|
||||||
|
) as ConstraintLayout
|
||||||
|
return ViewHolder(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
with(holder) {
|
val itm = items[position]
|
||||||
val itm = items[position]
|
|
||||||
|
|
||||||
binding.title.text = itm.getTitleDecoded()
|
|
||||||
|
|
||||||
binding.title.setOnTouchListener(LinkOnTouchListener())
|
holder.mView.title.text = itm.getTitleDecoded()
|
||||||
|
|
||||||
binding.title.setLinkTextColor(appColors.colorAccent)
|
holder.mView.title.setOnTouchListener(LinkOnTouchListener())
|
||||||
|
|
||||||
binding.sourceTitleAndDate.text = itm.sourceAndDateText()
|
holder.mView.title.setLinkTextColor(appColors.colorAccent)
|
||||||
|
|
||||||
if (itm.getThumbnail(c).isEmpty()) {
|
holder.mView.sourceTitleAndDate.text = itm.sourceAndDateText()
|
||||||
|
|
||||||
if (itm.getIcon(c).isEmpty()) {
|
if (itm.getThumbnail(c).isEmpty()) {
|
||||||
val color = generator.getColor(itm.getSourceTitle())
|
|
||||||
|
|
||||||
val drawable =
|
if (itm.getIcon(c).isEmpty()) {
|
||||||
TextDrawable
|
val color = generator.getColor(itm.sourcetitle)
|
||||||
.builder()
|
|
||||||
.round()
|
|
||||||
.build(itm.getSourceTitle().toTextDrawableString(c), color)
|
|
||||||
|
|
||||||
binding.itemImage.setImageDrawable(drawable)
|
val drawable =
|
||||||
} else {
|
TextDrawable
|
||||||
c.circularBitmapDrawable(config, itm.getIcon(c), binding.itemImage)
|
.builder()
|
||||||
}
|
.round()
|
||||||
|
.build(itm.sourcetitle.toTextDrawableString(c), color)
|
||||||
|
|
||||||
|
holder.mView.itemImage.setImageDrawable(drawable)
|
||||||
} else {
|
} else {
|
||||||
c.bitmapCenterCrop(config, itm.getThumbnail(c), binding.itemImage)
|
c.circularBitmapDrawable(config, itm.getIcon(c), holder.mView.itemImage)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
c.bitmapCenterCrop(config, itm.getThumbnail(c), holder.mView.itemImage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int = items.size
|
override fun getItemCount(): Int = items.size
|
||||||
|
|
||||||
inner class ViewHolder(val binding: ListItemBinding) : RecyclerView.ViewHolder(binding.root) {
|
inner class ViewHolder(val mView: ConstraintLayout) : RecyclerView.ViewHolder(mView) {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
handleCustomTabActions()
|
handleCustomTabActions()
|
||||||
@ -89,11 +110,11 @@ class ItemListAdapter(
|
|||||||
val customTabsIntent = c.buildCustomTabsIntent()
|
val customTabsIntent = c.buildCustomTabsIntent()
|
||||||
helper.bindCustomTabsService(app)
|
helper.bindCustomTabsService(app)
|
||||||
|
|
||||||
binding.root.setOnClickListener {
|
mView.setOnClickListener {
|
||||||
c.openItemUrl(
|
c.openItemUrl(
|
||||||
items,
|
items,
|
||||||
bindingAdapterPosition,
|
adapterPosition,
|
||||||
items[bindingAdapterPosition].getLinkDecoded(),
|
items[adapterPosition].getLinkDecoded(),
|
||||||
customTabsIntent,
|
customTabsIntent,
|
||||||
internalBrowser,
|
internalBrowser,
|
||||||
articleViewer,
|
articleViewer,
|
||||||
|
@ -2,16 +2,25 @@ package apps.amine.bou.readerforselfoss.adapters
|
|||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.widget.TextView
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import android.widget.TextView
|
||||||
|
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.persistence.database.AppDatabase
|
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.Config
|
import apps.amine.bou.readerforselfoss.utils.Config
|
||||||
import apps.amine.bou.readerforselfoss.utils.SharedItems
|
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import apps.amine.bou.readerforselfoss.utils.persistence.toEntity
|
||||||
|
import apps.amine.bou.readerforselfoss.utils.succeeded
|
||||||
|
import retrofit2.Call
|
||||||
|
import retrofit2.Callback
|
||||||
|
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>
|
||||||
@ -23,8 +32,8 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
|
|||||||
abstract val config: Config
|
abstract val config: Config
|
||||||
abstract val updateItems: (ArrayList<Item>) -> Unit
|
abstract val updateItems: (ArrayList<Item>) -> Unit
|
||||||
|
|
||||||
fun updateAllItems() {
|
fun updateAllItems(newItems: ArrayList<Item>) {
|
||||||
items = SharedItems.focusedItems
|
items = newItems
|
||||||
notifyDataSetChanged()
|
notifyDataSetChanged()
|
||||||
updateItems(items)
|
updateItems(items)
|
||||||
}
|
}
|
||||||
@ -37,11 +46,34 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
|
|||||||
Snackbar.LENGTH_LONG
|
Snackbar.LENGTH_LONG
|
||||||
)
|
)
|
||||||
.setAction(R.string.undo_string) {
|
.setAction(R.string.undo_string) {
|
||||||
SharedItems.unreadItem(app, api, db, i)
|
items.add(position, i)
|
||||||
if (SharedItems.displayedItems == "unread") {
|
thread {
|
||||||
addItemAtIndex(i, position)
|
db.itemsDao().insertAllItems(i.toEntity())
|
||||||
|
}
|
||||||
|
notifyItemInserted(position)
|
||||||
|
updateItems(items)
|
||||||
|
|
||||||
|
if (app.isNetworkAccessible(null)) {
|
||||||
|
api.unmarkItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
||||||
|
override fun onResponse(
|
||||||
|
call: Call<SuccessResponse>,
|
||||||
|
response: Response<SuccessResponse>
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||||
|
items.remove(i)
|
||||||
|
thread {
|
||||||
|
db.itemsDao().delete(i.toEntity())
|
||||||
|
}
|
||||||
|
notifyItemRemoved(position)
|
||||||
|
updateItems(items)
|
||||||
|
}
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
notifyItemChanged(position)
|
thread {
|
||||||
|
db.actionsDao().insertAllActions(ActionEntity(i.id, false, true, false, false))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,7 +83,7 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
|
|||||||
s.show()
|
s.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun markSnackbar(position: Int) {
|
private fun markSnackbar(i: Item, position: Int) {
|
||||||
val s = Snackbar
|
val s = Snackbar
|
||||||
.make(
|
.make(
|
||||||
app.findViewById(R.id.coordLayout),
|
app.findViewById(R.id.coordLayout),
|
||||||
@ -59,13 +91,34 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
|
|||||||
Snackbar.LENGTH_LONG
|
Snackbar.LENGTH_LONG
|
||||||
)
|
)
|
||||||
.setAction(R.string.undo_string) {
|
.setAction(R.string.undo_string) {
|
||||||
SharedItems.readItem(app, api, db, items[position])
|
items.add(position, i)
|
||||||
items = SharedItems.focusedItems
|
thread {
|
||||||
if (SharedItems.displayedItems == "unread") {
|
db.itemsDao().delete(i.toEntity())
|
||||||
notifyItemRemoved(position)
|
}
|
||||||
updateItems(items)
|
notifyItemInserted(position)
|
||||||
|
updateItems(items)
|
||||||
|
|
||||||
|
if (app.isNetworkAccessible(null)) {
|
||||||
|
api.markItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
||||||
|
override fun onResponse(
|
||||||
|
call: Call<SuccessResponse>,
|
||||||
|
response: Response<SuccessResponse>
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||||
|
items.remove(i)
|
||||||
|
thread {
|
||||||
|
db.itemsDao().insertAllItems(i.toEntity())
|
||||||
|
}
|
||||||
|
notifyItemRemoved(position)
|
||||||
|
updateItems(items)
|
||||||
|
}
|
||||||
|
})
|
||||||
} else {
|
} else {
|
||||||
notifyItemChanged(position)
|
thread {
|
||||||
|
db.actionsDao().insertAllActions(ActionEntity(i.id, true, false, false, false))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,30 +129,99 @@ abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapte
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun handleItemAtIndex(position: Int) {
|
fun handleItemAtIndex(position: Int) {
|
||||||
if (SharedItems.unreadItemStatusAtIndex(position)) {
|
if (unreadItemStatusAtIndex(position)) {
|
||||||
readItemAtIndex(position)
|
readItemAtIndex(position)
|
||||||
} else {
|
} else {
|
||||||
unreadItemAtIndex(position)
|
unreadItemAtIndex(position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun unreadItemStatusAtIndex(position: Int): Boolean {
|
||||||
|
return items[position].unread
|
||||||
|
}
|
||||||
|
|
||||||
private fun readItemAtIndex(position: Int) {
|
private fun readItemAtIndex(position: Int) {
|
||||||
val i = items[position]
|
val i = items[position]
|
||||||
SharedItems.readItem(app, api, db, i)
|
items.remove(i)
|
||||||
if (SharedItems.displayedItems == "unread") {
|
notifyItemRemoved(position)
|
||||||
items.remove(i)
|
updateItems(items)
|
||||||
notifyItemRemoved(position)
|
|
||||||
updateItems(items)
|
thread {
|
||||||
} else {
|
db.itemsDao().delete(i.toEntity())
|
||||||
notifyItemChanged(position)
|
}
|
||||||
|
|
||||||
|
if (app.isNetworkAccessible(null)) {
|
||||||
|
api.markItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
||||||
|
override fun onResponse(
|
||||||
|
call: Call<SuccessResponse>,
|
||||||
|
response: Response<SuccessResponse>
|
||||||
|
) {
|
||||||
|
|
||||||
|
unmarkSnackbar(i, position)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||||
|
Toast.makeText(
|
||||||
|
app,
|
||||||
|
app.getString(R.string.cant_mark_read),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
items.add(position, i)
|
||||||
|
notifyItemInserted(position)
|
||||||
|
updateItems(items)
|
||||||
|
|
||||||
|
thread {
|
||||||
|
db.itemsDao().insertAllItems(i.toEntity())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
thread {
|
||||||
|
db.actionsDao().insertAllActions(ActionEntity(i.id, true, false, false, false))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
unmarkSnackbar(i, position)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun unreadItemAtIndex(position: Int) {
|
private fun unreadItemAtIndex(position: Int) {
|
||||||
SharedItems.unreadItem(app, api, db, items[position])
|
val i = items[position]
|
||||||
notifyItemChanged(position)
|
items.remove(i)
|
||||||
markSnackbar(position)
|
notifyItemRemoved(position)
|
||||||
|
updateItems(items)
|
||||||
|
|
||||||
|
thread {
|
||||||
|
db.itemsDao().insertAllItems(i.toEntity())
|
||||||
|
}
|
||||||
|
|
||||||
|
if (app.isNetworkAccessible(null)) {
|
||||||
|
api.unmarkItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
||||||
|
override fun onResponse(
|
||||||
|
call: Call<SuccessResponse>,
|
||||||
|
response: Response<SuccessResponse>
|
||||||
|
) {
|
||||||
|
|
||||||
|
markSnackbar(i, position)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||||
|
Toast.makeText(
|
||||||
|
app,
|
||||||
|
app.getString(R.string.cant_mark_unread),
|
||||||
|
Toast.LENGTH_SHORT
|
||||||
|
).show()
|
||||||
|
items.add(i)
|
||||||
|
notifyItemInserted(position)
|
||||||
|
updateItems(items)
|
||||||
|
|
||||||
|
thread {
|
||||||
|
db.itemsDao().delete(i.toEntity())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
thread {
|
||||||
|
db.actionsDao().insertAllActions(ActionEntity(i.id, false, true, false, false))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addItemAtIndex(item: Item, position: Int) {
|
fun addItemAtIndex(item: Item, position: Int) {
|
||||||
|
@ -12,13 +12,13 @@ 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.Source
|
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.databinding.SourceListItemBinding
|
|
||||||
import apps.amine.bou.readerforselfoss.utils.Config
|
import apps.amine.bou.readerforselfoss.utils.Config
|
||||||
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.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
|
||||||
|
import kotlinx.android.synthetic.main.source_list_item.view.*
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.Callback
|
import retrofit2.Callback
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
@ -31,11 +31,14 @@ class SourcesListAdapter(
|
|||||||
private val c: Context = app.baseContext
|
private val c: Context = app.baseContext
|
||||||
private val generator: ColorGenerator = ColorGenerator.MATERIAL
|
private val generator: ColorGenerator = ColorGenerator.MATERIAL
|
||||||
private lateinit var config: Config
|
private lateinit var config: Config
|
||||||
private lateinit var binding: SourceListItemBinding
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
binding = SourceListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
val v = LayoutInflater.from(c).inflate(
|
||||||
return ViewHolder(binding.root)
|
R.layout.source_list_item,
|
||||||
|
parent,
|
||||||
|
false
|
||||||
|
) as ConstraintLayout
|
||||||
|
return ViewHolder(v)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
@ -43,19 +46,19 @@ class SourcesListAdapter(
|
|||||||
config = Config(c)
|
config = Config(c)
|
||||||
|
|
||||||
if (itm.getIcon(c).isEmpty()) {
|
if (itm.getIcon(c).isEmpty()) {
|
||||||
val color = generator.getColor(itm.getTitleDecoded())
|
val color = generator.getColor(itm.title)
|
||||||
|
|
||||||
val drawable =
|
val drawable =
|
||||||
TextDrawable
|
TextDrawable
|
||||||
.builder()
|
.builder()
|
||||||
.round()
|
.round()
|
||||||
.build(itm.getTitleDecoded().toTextDrawableString(c), color)
|
.build(itm.title.toTextDrawableString(c), color)
|
||||||
binding.itemImage.setImageDrawable(drawable)
|
holder.mView.itemImage.setImageDrawable(drawable)
|
||||||
} else {
|
} else {
|
||||||
c.circularBitmapDrawable(config, itm.getIcon(c), binding.itemImage)
|
c.circularBitmapDrawable(config, itm.getIcon(c), holder.mView.itemImage)
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.sourceTitle.text = itm.getTitleDecoded()
|
holder.mView.sourceTitle.text = itm.title
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemCount(): Int = items.size
|
override fun getItemCount(): Int = items.size
|
||||||
|
@ -28,17 +28,17 @@ class ParsedContent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(source: Parcel) : this(
|
constructor(source: Parcel) : this(
|
||||||
title = source.readString().orEmpty(),
|
title = source.readString(),
|
||||||
content = source.readString(),
|
content = source.readString(),
|
||||||
date_published = source.readString().orEmpty(),
|
date_published = source.readString(),
|
||||||
lead_image_url = source.readString(),
|
lead_image_url = source.readString(),
|
||||||
dek = source.readString().orEmpty(),
|
dek = source.readString(),
|
||||||
url = source.readString().orEmpty(),
|
url = source.readString(),
|
||||||
domain = source.readString().orEmpty(),
|
domain = source.readString(),
|
||||||
excerpt = source.readString().orEmpty(),
|
excerpt = source.readString(),
|
||||||
total_pages = source.readInt(),
|
total_pages = source.readInt(),
|
||||||
rendered_pages = source.readInt(),
|
rendered_pages = source.readInt(),
|
||||||
next_page_url = source.readString().orEmpty()
|
next_page_url = source.readString()
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun describeContents() = 0
|
override fun describeContents() = 0
|
||||||
|
@ -3,7 +3,6 @@ package apps.amine.bou.readerforselfoss.api.selfoss
|
|||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import apps.amine.bou.readerforselfoss.utils.Config
|
import apps.amine.bou.readerforselfoss.utils.Config
|
||||||
import apps.amine.bou.readerforselfoss.utils.SharedItems
|
|
||||||
import apps.amine.bou.readerforselfoss.utils.getUnsafeHttpClient
|
import apps.amine.bou.readerforselfoss.utils.getUnsafeHttpClient
|
||||||
import com.burgstaller.okhttp.AuthenticationCacheInterceptor
|
import com.burgstaller.okhttp.AuthenticationCacheInterceptor
|
||||||
import com.burgstaller.okhttp.CachingAuthenticatorDecorator
|
import com.burgstaller.okhttp.CachingAuthenticatorDecorator
|
||||||
@ -14,8 +13,6 @@ import com.burgstaller.okhttp.digest.Credentials
|
|||||||
import com.burgstaller.okhttp.digest.DigestAuthenticator
|
import com.burgstaller.okhttp.digest.DigestAuthenticator
|
||||||
import com.google.gson.GsonBuilder
|
import com.google.gson.GsonBuilder
|
||||||
import okhttp3.*
|
import okhttp3.*
|
||||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
|
||||||
import okhttp3.ResponseBody.Companion.toResponseBody
|
|
||||||
import okhttp3.logging.HttpLoggingInterceptor
|
import okhttp3.logging.HttpLoggingInterceptor
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.Retrofit
|
import retrofit2.Retrofit
|
||||||
@ -70,7 +67,7 @@ class SelfossApi(
|
|||||||
val request: Request = chain.request()
|
val request: Request = chain.request()
|
||||||
val response: Response = chain.proceed(request)
|
val response: Response = chain.proceed(request)
|
||||||
|
|
||||||
if (response.code == 408) {
|
if (response.code() == 408) {
|
||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
return response
|
return response
|
||||||
@ -105,7 +102,7 @@ class SelfossApi(
|
|||||||
httpClient
|
httpClient
|
||||||
.addInterceptor { chain ->
|
.addInterceptor { chain ->
|
||||||
val res = chain.proceed(chain.request())
|
val res = chain.proceed(chain.request())
|
||||||
if (res.code == timeoutCode) {
|
if (res.code() == timeoutCode) {
|
||||||
throw SocketTimeoutException("timeout")
|
throw SocketTimeoutException("timeout")
|
||||||
}
|
}
|
||||||
res
|
res
|
||||||
@ -119,7 +116,7 @@ class SelfossApi(
|
|||||||
Response.Builder()
|
Response.Builder()
|
||||||
.code(timeoutCode)
|
.code(timeoutCode)
|
||||||
.protocol(Protocol.HTTP_2)
|
.protocol(Protocol.HTTP_2)
|
||||||
.body("".toResponseBody("text/plain".toMediaTypeOrNull()))
|
.body(ResponseBody.create(MediaType.parse("text/plain"), ""))
|
||||||
.message("")
|
.message("")
|
||||||
.request(request)
|
.request(request)
|
||||||
.build()
|
.build()
|
||||||
@ -145,50 +142,45 @@ class SelfossApi(
|
|||||||
fun login(): Call<SuccessResponse> =
|
fun login(): Call<SuccessResponse> =
|
||||||
service.loginToSelfoss(config.userLogin, config.userPassword)
|
service.loginToSelfoss(config.userLogin, config.userPassword)
|
||||||
|
|
||||||
suspend fun readItems(
|
fun readItems(
|
||||||
|
tag: String?,
|
||||||
|
sourceId: Long?,
|
||||||
|
search: String?,
|
||||||
itemsNumber: Int,
|
itemsNumber: Int,
|
||||||
offset: Int
|
offset: Int
|
||||||
): retrofit2.Response<List<Item>> =
|
): Call<List<Item>> =
|
||||||
getItems("read", SharedItems.tagFilter, SharedItems.sourceIDFilter, SharedItems.searchFilter, itemsNumber, offset)
|
getItems("read", tag, sourceId, search, itemsNumber, offset)
|
||||||
|
|
||||||
suspend fun newItems(
|
fun newItems(
|
||||||
|
tag: String?,
|
||||||
|
sourceId: Long?,
|
||||||
|
search: String?,
|
||||||
itemsNumber: Int,
|
itemsNumber: Int,
|
||||||
offset: Int
|
offset: Int
|
||||||
): retrofit2.Response<List<Item>> =
|
): Call<List<Item>> =
|
||||||
getItems("unread", SharedItems.tagFilter, SharedItems.sourceIDFilter, SharedItems.searchFilter, itemsNumber, offset)
|
getItems("unread", tag, sourceId, search, itemsNumber, offset)
|
||||||
|
|
||||||
suspend fun starredItems(
|
fun starredItems(
|
||||||
|
tag: String?,
|
||||||
|
sourceId: Long?,
|
||||||
|
search: String?,
|
||||||
itemsNumber: Int,
|
itemsNumber: Int,
|
||||||
offset: Int
|
offset: Int
|
||||||
): retrofit2.Response<List<Item>> =
|
): Call<List<Item>> =
|
||||||
getItems("starred", SharedItems.tagFilter, SharedItems.sourceIDFilter, SharedItems.searchFilter, itemsNumber, offset)
|
getItems("starred", tag, sourceId, search, itemsNumber, offset)
|
||||||
|
|
||||||
fun allItems(): Call<List<Item>> =
|
fun allItems(): Call<List<Item>> =
|
||||||
service.allItems(userName, password)
|
service.allItems(userName, password)
|
||||||
|
|
||||||
suspend fun allNewItems(): retrofit2.Response<List<Item>> =
|
private fun getItems(
|
||||||
getItems("unread", null, null, null, 200, 0)
|
|
||||||
|
|
||||||
suspend fun allReadItems(): retrofit2.Response<List<Item>> =
|
|
||||||
getItems("read", null, null, null, 200, 0)
|
|
||||||
|
|
||||||
suspend fun allStarredItems(): retrofit2.Response<List<Item>> =
|
|
||||||
getItems("read", null, null, null, 200, 0)
|
|
||||||
|
|
||||||
private suspend fun getItems(
|
|
||||||
type: String,
|
type: String,
|
||||||
tag: String?,
|
tag: String?,
|
||||||
sourceId: Long?,
|
sourceId: Long?,
|
||||||
search: String?,
|
search: String?,
|
||||||
items: Int,
|
items: Int,
|
||||||
offset: Int
|
offset: Int
|
||||||
): retrofit2.Response<List<Item>> =
|
): Call<List<Item>> =
|
||||||
service.getItems(type, tag, sourceId, search, null, userName, password, items, offset)
|
service.getItems(type, tag, sourceId, search, userName, password, items, offset)
|
||||||
|
|
||||||
suspend fun updateItems(
|
|
||||||
updatedSince: String
|
|
||||||
): retrofit2.Response<List<Item>> =
|
|
||||||
service.getItems("read", null, null, null, updatedSince, userName, password, 200, 0)
|
|
||||||
|
|
||||||
fun markItem(itemId: String): Call<SuccessResponse> =
|
fun markItem(itemId: String): Call<SuccessResponse> =
|
||||||
service.markAsRead(itemId, userName, password)
|
service.markAsRead(itemId, userName, password)
|
||||||
@ -196,7 +188,7 @@ class SelfossApi(
|
|||||||
fun unmarkItem(itemId: String): Call<SuccessResponse> =
|
fun unmarkItem(itemId: String): Call<SuccessResponse> =
|
||||||
service.unmarkAsRead(itemId, userName, password)
|
service.unmarkAsRead(itemId, userName, password)
|
||||||
|
|
||||||
suspend fun readAll(ids: List<String>): SuccessResponse =
|
fun readAll(ids: List<String>): Call<SuccessResponse> =
|
||||||
service.markAllAsRead(ids, userName, password)
|
service.markAllAsRead(ids, userName, password)
|
||||||
|
|
||||||
fun starrItem(itemId: String): Call<SuccessResponse> =
|
fun starrItem(itemId: String): Call<SuccessResponse> =
|
||||||
@ -205,7 +197,8 @@ class SelfossApi(
|
|||||||
fun unstarrItem(itemId: String): Call<SuccessResponse> =
|
fun unstarrItem(itemId: String): Call<SuccessResponse> =
|
||||||
service.unstarr(itemId, userName, password)
|
service.unstarr(itemId, userName, password)
|
||||||
|
|
||||||
suspend fun stats(): retrofit2.Response<Stats> = service.stats(userName, password)
|
val stats: Call<Stats>
|
||||||
|
get() = service.stats(userName, password)
|
||||||
|
|
||||||
val tags: Call<List<Tag>>
|
val tags: Call<List<Tag>>
|
||||||
get() = service.tags(userName, password)
|
get() = service.tags(userName, password)
|
||||||
@ -213,9 +206,6 @@ class SelfossApi(
|
|||||||
fun update(): Call<String> =
|
fun update(): Call<String> =
|
||||||
service.update(userName, password)
|
service.update(userName, password)
|
||||||
|
|
||||||
val apiVersion: Call<ApiVersion>
|
|
||||||
get() = service.version()
|
|
||||||
|
|
||||||
val sources: Call<List<Source>>
|
val sources: Call<List<Source>>
|
||||||
get() = service.sources(userName, password)
|
get() = service.sources(userName, password)
|
||||||
|
|
||||||
@ -233,13 +223,4 @@ class SelfossApi(
|
|||||||
filter: String
|
filter: String
|
||||||
): Call<SuccessResponse> =
|
): Call<SuccessResponse> =
|
||||||
service.createSource(title, url, spout, tags, filter, userName, password)
|
service.createSource(title, url, spout, tags, filter, userName, password)
|
||||||
|
|
||||||
fun createSourceApi2(
|
|
||||||
title: String,
|
|
||||||
url: String,
|
|
||||||
spout: String,
|
|
||||||
tags: List<String>,
|
|
||||||
filter: String
|
|
||||||
): Call<SuccessResponse> =
|
|
||||||
service.createSourceApi2(title, url, spout, tags, filter, userName, password)
|
|
||||||
}
|
}
|
||||||
|
@ -1,134 +0,0 @@
|
|||||||
package apps.amine.bou.readerforselfoss.api.selfoss
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
|
||||||
import apps.amine.bou.readerforselfoss.utils.SharedItems
|
|
||||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAvailable
|
|
||||||
import kotlinx.coroutines.*
|
|
||||||
import retrofit2.Response
|
|
||||||
|
|
||||||
suspend fun getAndStoreAllItems(context: Context, api: SelfossApi, db: AppDatabase) = withContext(Dispatchers.IO) {
|
|
||||||
if (isNetworkAvailable(context)) {
|
|
||||||
launch {
|
|
||||||
try {
|
|
||||||
enqueueArticles(api.allNewItems(), db, true)
|
|
||||||
} catch (e: Throwable) {}
|
|
||||||
}
|
|
||||||
launch {
|
|
||||||
try {
|
|
||||||
enqueueArticles(api.allReadItems(), db, false)
|
|
||||||
} catch (e: Throwable) {}
|
|
||||||
}
|
|
||||||
launch {
|
|
||||||
try {
|
|
||||||
enqueueArticles(api.allStarredItems(), db, false)
|
|
||||||
} catch (e: Throwable) {}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
launch { SharedItems.updateDatabase(db) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun updateItems(context: Context, api: SelfossApi, db: AppDatabase) = coroutineScope {
|
|
||||||
if (isNetworkAvailable(context)) {
|
|
||||||
launch {
|
|
||||||
try {
|
|
||||||
enqueueArticles(api.updateItems(SharedItems.items[0].datetime), db, true)
|
|
||||||
} catch (e: Throwable) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun refreshFocusedItems(context: Context, api: SelfossApi, db: AppDatabase, itemsNumber: Int) = withContext(Dispatchers.IO) {
|
|
||||||
if (isNetworkAvailable(context)) {
|
|
||||||
val response = when (SharedItems.displayedItems) {
|
|
||||||
"read" -> api.readItems(itemsNumber, 0)
|
|
||||||
"unread" -> api.newItems(itemsNumber, 0)
|
|
||||||
"starred" -> api.starredItems(itemsNumber, 0)
|
|
||||||
else -> api.readItems(itemsNumber, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.isSuccessful) {
|
|
||||||
SharedItems.refreshFocusedItems(response.body() as ArrayList<Item>)
|
|
||||||
SharedItems.updateDatabase(db)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun getReadItems(context: Context, api: SelfossApi, db: AppDatabase, itemsNumber: Int, offset: Int) = withContext(Dispatchers.IO) {
|
|
||||||
if (isNetworkAvailable(context)) {
|
|
||||||
try {
|
|
||||||
enqueueArticles(api.readItems( itemsNumber, offset), db, false)
|
|
||||||
SharedItems.fetchedAll = true
|
|
||||||
SharedItems.updateDatabase(db)
|
|
||||||
} catch (e: Throwable) {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun getUnreadItems(context: Context, api: SelfossApi, db: AppDatabase, itemsNumber: Int, offset: Int) = withContext(Dispatchers.IO) {
|
|
||||||
if (isNetworkAvailable(context)) {
|
|
||||||
try {
|
|
||||||
if (!SharedItems.fetchedUnread) {
|
|
||||||
SharedItems.clearDBItems(db)
|
|
||||||
}
|
|
||||||
enqueueArticles(api.newItems(itemsNumber, offset), db, false)
|
|
||||||
SharedItems.fetchedUnread = true
|
|
||||||
} catch (e: Throwable) {}
|
|
||||||
}
|
|
||||||
SharedItems.updateDatabase(db)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun getStarredItems(context: Context, api: SelfossApi, db: AppDatabase, itemsNumber: Int, offset: Int) = withContext(Dispatchers.IO) {
|
|
||||||
if (isNetworkAvailable(context)) {
|
|
||||||
try {
|
|
||||||
enqueueArticles(api.starredItems(itemsNumber, offset), db, false)
|
|
||||||
SharedItems.fetchedStarred = true
|
|
||||||
SharedItems.updateDatabase(db)
|
|
||||||
} catch (e: Throwable) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun readAll(context: Context, api: SelfossApi, db: AppDatabase): Boolean {
|
|
||||||
var success = false
|
|
||||||
if (isNetworkAvailable(context)) {
|
|
||||||
try {
|
|
||||||
val ids = SharedItems.focusedItems.map { it.id }
|
|
||||||
if (ids.isNotEmpty()) {
|
|
||||||
val result = api.readAll(ids)
|
|
||||||
SharedItems.readItems(db, ids)
|
|
||||||
success = result.isSuccess
|
|
||||||
}
|
|
||||||
} catch (e: Throwable) {}
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun reloadBadges(context: Context, api: SelfossApi) = withContext(Dispatchers.IO) {
|
|
||||||
if (isNetworkAvailable(context)) {
|
|
||||||
try {
|
|
||||||
val response = api.stats()
|
|
||||||
|
|
||||||
if (response.isSuccessful) {
|
|
||||||
val badges = response.body()
|
|
||||||
SharedItems.badgeUnread = badges!!.unread
|
|
||||||
SharedItems.badgeAll = badges.total
|
|
||||||
SharedItems.badgeStarred = badges.starred
|
|
||||||
}
|
|
||||||
} catch (e: Throwable) {}
|
|
||||||
} else {
|
|
||||||
SharedItems.computeBadges()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun enqueueArticles(response: Response<List<Item>>, db: AppDatabase, clearDatabase: Boolean) {
|
|
||||||
if (response.isSuccessful) {
|
|
||||||
if (clearDatabase) {
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
SharedItems.clearDBItems(db)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val allItems = response.body() as ArrayList<Item>
|
|
||||||
SharedItems.appendNewItems(allItems)
|
|
||||||
}
|
|
||||||
}
|
|
@ -14,8 +14,6 @@ import com.bumptech.glide.Glide
|
|||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
import com.bumptech.glide.request.RequestOptions
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
import java.util.*
|
|
||||||
import kotlin.collections.ArrayList
|
|
||||||
|
|
||||||
private fun constructUrl(config: Config?, path: String, file: String?): String {
|
private fun constructUrl(config: Config?, path: String, file: String?): String {
|
||||||
return if (file.isEmptyOrNullOrNullString()) {
|
return if (file.isEmptyOrNullOrNullString()) {
|
||||||
@ -32,11 +30,7 @@ data class Tag(
|
|||||||
@SerializedName("tag") val tag: String,
|
@SerializedName("tag") val tag: String,
|
||||||
@SerializedName("color") val color: String,
|
@SerializedName("color") val color: String,
|
||||||
@SerializedName("unread") val unread: Int
|
@SerializedName("unread") val unread: Int
|
||||||
) {
|
)
|
||||||
fun getTitleDecoded(): String {
|
|
||||||
return Html.fromHtml(tag).toString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SuccessResponse(@SerializedName("success") val success: Boolean) {
|
class SuccessResponse(@SerializedName("success") val success: Boolean) {
|
||||||
val isSuccess: Boolean
|
val isSuccess: Boolean
|
||||||
@ -54,19 +48,6 @@ data class Spout(
|
|||||||
@SerializedName("description") val description: String
|
@SerializedName("description") val description: String
|
||||||
)
|
)
|
||||||
|
|
||||||
data class ApiVersion(
|
|
||||||
@SerializedName("version") val version: String?,
|
|
||||||
@SerializedName("apiversion") val apiversion: String?
|
|
||||||
) {
|
|
||||||
fun getApiMajorVersion() : Int {
|
|
||||||
var versionNumber = 0
|
|
||||||
if (apiversion != null) {
|
|
||||||
versionNumber = apiversion.substringBefore(".").toInt()
|
|
||||||
}
|
|
||||||
return versionNumber
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
data class Source(
|
data class Source(
|
||||||
@SerializedName("id") val id: String,
|
@SerializedName("id") val id: String,
|
||||||
@SerializedName("title") val title: String,
|
@SerializedName("title") val title: String,
|
||||||
@ -83,10 +64,6 @@ data class Source(
|
|||||||
}
|
}
|
||||||
return constructUrl(config, "favicons", icon)
|
return constructUrl(config, "favicons", icon)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getTitleDecoded(): String {
|
|
||||||
return Html.fromHtml(title).toString()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Item(
|
data class Item(
|
||||||
@ -94,7 +71,7 @@ data class Item(
|
|||||||
@SerializedName("datetime") val datetime: String,
|
@SerializedName("datetime") val datetime: String,
|
||||||
@SerializedName("title") val title: String,
|
@SerializedName("title") val title: String,
|
||||||
@SerializedName("content") val content: String,
|
@SerializedName("content") val content: String,
|
||||||
@SerializedName("unread") var unread: Boolean,
|
@SerializedName("unread") val unread: Boolean,
|
||||||
@SerializedName("starred") var starred: Boolean,
|
@SerializedName("starred") var starred: Boolean,
|
||||||
@SerializedName("thumbnail") val thumbnail: String?,
|
@SerializedName("thumbnail") val thumbnail: String?,
|
||||||
@SerializedName("icon") val icon: String?,
|
@SerializedName("icon") val icon: String?,
|
||||||
@ -113,17 +90,17 @@ data class Item(
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(source: Parcel) : this(
|
constructor(source: Parcel) : this(
|
||||||
id = source.readString().orEmpty(),
|
id = source.readString(),
|
||||||
datetime = source.readString().orEmpty(),
|
datetime = source.readString(),
|
||||||
title = source.readString().orEmpty(),
|
title = source.readString(),
|
||||||
content = source.readString().orEmpty(),
|
content = source.readString(),
|
||||||
unread = 0.toByte() != source.readByte(),
|
unread = 0.toByte() != source.readByte(),
|
||||||
starred = 0.toByte() != source.readByte(),
|
starred = 0.toByte() != source.readByte(),
|
||||||
thumbnail = source.readString(),
|
thumbnail = source.readString(),
|
||||||
icon = source.readString(),
|
icon = source.readString(),
|
||||||
link = source.readString().orEmpty(),
|
link = source.readString(),
|
||||||
sourcetitle = source.readString().orEmpty(),
|
sourcetitle = source.readString(),
|
||||||
tags = if (source.readParcelable<SelfossTagType>(ClassLoader.getSystemClassLoader()) != null) source.readParcelable(ClassLoader.getSystemClassLoader())!! else SelfossTagType("")
|
tags = source.readParcelable(ClassLoader.getSystemClassLoader())
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun describeContents() = 0
|
override fun describeContents() = 0
|
||||||
@ -157,17 +134,10 @@ data class Item(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun getImages() : ArrayList<String> {
|
fun getImages() : ArrayList<String> {
|
||||||
val allImages = ArrayList<String>()
|
var allImages = ArrayList<String>()
|
||||||
|
|
||||||
for ( image in Jsoup.parse(content).getElementsByTag("img")) {
|
for ( image in Jsoup.parse(content).getElementsByTag("img")) {
|
||||||
val url = image.attr("src")
|
allImages.add(image.attr("src"))
|
||||||
if (url.lowercase(Locale.US).contains(".jpg") ||
|
|
||||||
url.lowercase(Locale.US).contains(".jpeg") ||
|
|
||||||
url.lowercase(Locale.US).contains(".png") ||
|
|
||||||
url.lowercase(Locale.US).contains(".webp"))
|
|
||||||
{
|
|
||||||
allImages.add(url)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return allImages
|
return allImages
|
||||||
}
|
}
|
||||||
@ -175,7 +145,7 @@ data class Item(
|
|||||||
fun preloadImages(context: Context) : Boolean {
|
fun preloadImages(context: Context) : Boolean {
|
||||||
val imageUrls = this.getImages()
|
val imageUrls = this.getImages()
|
||||||
|
|
||||||
val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL).timeout(10000)
|
val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL)
|
||||||
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -183,7 +153,7 @@ data class Item(
|
|||||||
if ( URLUtil.isValidUrl(url)) {
|
if ( URLUtil.isValidUrl(url)) {
|
||||||
val image = Glide.with(context).asBitmap()
|
val image = Glide.with(context).asBitmap()
|
||||||
.apply(glideOptions)
|
.apply(glideOptions)
|
||||||
.load(url).submit()
|
.load(url).submit().get()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e : Error) {
|
} catch (e : Error) {
|
||||||
@ -197,10 +167,6 @@ data class Item(
|
|||||||
return Html.fromHtml(title).toString()
|
return Html.fromHtml(title).toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSourceTitle(): String {
|
|
||||||
return Html.fromHtml(sourcetitle).toString()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: maybe find a better way to handle these kind of urls
|
// TODO: maybe find a better way to handle these kind of urls
|
||||||
fun getLinkDecoded(): String {
|
fun getLinkDecoded(): String {
|
||||||
var stringUrl: String
|
var stringUrl: String
|
||||||
@ -242,7 +208,7 @@ data class SelfossTagType(val tags: String) : Parcelable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
constructor(source: Parcel) : this(
|
constructor(source: Parcel) : this(
|
||||||
tags = source.readString().orEmpty()
|
tags = source.readString()
|
||||||
)
|
)
|
||||||
|
|
||||||
override fun describeContents() = 0
|
override fun describeContents() = 0
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package apps.amine.bou.readerforselfoss.api.selfoss
|
package apps.amine.bou.readerforselfoss.api.selfoss
|
||||||
|
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.Response
|
|
||||||
import retrofit2.http.DELETE
|
import retrofit2.http.DELETE
|
||||||
import retrofit2.http.Field
|
import retrofit2.http.Field
|
||||||
import retrofit2.http.FormUrlEncoded
|
import retrofit2.http.FormUrlEncoded
|
||||||
@ -17,17 +16,16 @@ internal interface SelfossService {
|
|||||||
fun loginToSelfoss(@Query("username") username: String, @Query("password") password: String): Call<SuccessResponse>
|
fun loginToSelfoss(@Query("username") username: String, @Query("password") password: String): Call<SuccessResponse>
|
||||||
|
|
||||||
@GET("items")
|
@GET("items")
|
||||||
suspend fun getItems(
|
fun getItems(
|
||||||
@Query("type") type: String,
|
@Query("type") type: String,
|
||||||
@Query("tag") tag: String?,
|
@Query("tag") tag: String?,
|
||||||
@Query("source") source: Long?,
|
@Query("source") source: Long?,
|
||||||
@Query("search") search: String?,
|
@Query("search") search: String?,
|
||||||
@Query("updatedsince") updatedSince: String?,
|
|
||||||
@Query("username") username: String,
|
@Query("username") username: String,
|
||||||
@Query("password") password: String,
|
@Query("password") password: String,
|
||||||
@Query("items") items: Int,
|
@Query("items") items: Int,
|
||||||
@Query("offset") offset: Int
|
@Query("offset") offset: Int
|
||||||
): Response<List<Item>>
|
): Call<List<Item>>
|
||||||
|
|
||||||
@GET("items")
|
@GET("items")
|
||||||
fun allItems(
|
fun allItems(
|
||||||
@ -53,11 +51,11 @@ internal interface SelfossService {
|
|||||||
|
|
||||||
@FormUrlEncoded
|
@FormUrlEncoded
|
||||||
@POST("mark")
|
@POST("mark")
|
||||||
suspend fun markAllAsRead(
|
fun markAllAsRead(
|
||||||
@Field("ids[]") ids: List<String>,
|
@Field("ids[]") ids: List<String>,
|
||||||
@Query("username") username: String,
|
@Query("username") username: String,
|
||||||
@Query("password") password: String
|
@Query("password") password: String
|
||||||
): SuccessResponse
|
): Call<SuccessResponse>
|
||||||
|
|
||||||
@Headers("Content-Type: application/x-www-form-urlencoded")
|
@Headers("Content-Type: application/x-www-form-urlencoded")
|
||||||
@POST("starr/{id}")
|
@POST("starr/{id}")
|
||||||
@ -76,10 +74,10 @@ internal interface SelfossService {
|
|||||||
): Call<SuccessResponse>
|
): Call<SuccessResponse>
|
||||||
|
|
||||||
@GET("stats")
|
@GET("stats")
|
||||||
suspend fun stats(
|
fun stats(
|
||||||
@Query("username") username: String,
|
@Query("username") username: String,
|
||||||
@Query("password") password: String
|
@Query("password") password: String
|
||||||
): Response<Stats>
|
): Call<Stats>
|
||||||
|
|
||||||
@GET("tags")
|
@GET("tags")
|
||||||
fun tags(
|
fun tags(
|
||||||
@ -105,9 +103,6 @@ internal interface SelfossService {
|
|||||||
@Query("password") password: String
|
@Query("password") password: String
|
||||||
): Call<List<Source>>
|
): Call<List<Source>>
|
||||||
|
|
||||||
@GET("api/about")
|
|
||||||
fun version(): Call<ApiVersion>
|
|
||||||
|
|
||||||
@DELETE("source/{id}")
|
@DELETE("source/{id}")
|
||||||
fun deleteSource(
|
fun deleteSource(
|
||||||
@Path("id") id: String,
|
@Path("id") id: String,
|
||||||
@ -126,16 +121,4 @@ internal interface SelfossService {
|
|||||||
@Query("username") username: String,
|
@Query("username") username: String,
|
||||||
@Query("password") password: String
|
@Query("password") password: String
|
||||||
): Call<SuccessResponse>
|
): Call<SuccessResponse>
|
||||||
|
|
||||||
@FormUrlEncoded
|
|
||||||
@POST("source")
|
|
||||||
fun createSourceApi2(
|
|
||||||
@Field("title") title: String,
|
|
||||||
@Field("url") url: String,
|
|
||||||
@Field("spout") spout: String,
|
|
||||||
@Field("tags[]") tags: List<String>,
|
|
||||||
@Field("filter") filter: String,
|
|
||||||
@Query("username") username: String,
|
|
||||||
@Query("password") password: String
|
|
||||||
): Call<SuccessResponse>
|
|
||||||
}
|
}
|
||||||
|
@ -4,8 +4,7 @@ import android.app.NotificationManager
|
|||||||
import android.app.PendingIntent
|
import android.app.PendingIntent
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.preference.PreferenceManager
|
||||||
import androidx.preference.PreferenceManager
|
|
||||||
import androidx.core.app.NotificationCompat
|
import androidx.core.app.NotificationCompat
|
||||||
import androidx.core.app.NotificationCompat.PRIORITY_DEFAULT
|
import androidx.core.app.NotificationCompat.PRIORITY_DEFAULT
|
||||||
import androidx.core.app.NotificationCompat.PRIORITY_LOW
|
import androidx.core.app.NotificationCompat.PRIORITY_LOW
|
||||||
@ -14,19 +13,15 @@ import androidx.work.Worker
|
|||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import apps.amine.bou.readerforselfoss.MainActivity
|
import apps.amine.bou.readerforselfoss.MainActivity
|
||||||
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.SelfossApi
|
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.getAndStoreAllItems
|
|
||||||
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
||||||
import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity
|
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_1_2
|
||||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_2_3
|
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_2_3
|
||||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_3_4
|
|
||||||
import apps.amine.bou.readerforselfoss.utils.Config
|
import apps.amine.bou.readerforselfoss.utils.Config
|
||||||
import apps.amine.bou.readerforselfoss.utils.SharedItems
|
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
||||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAvailable
|
import apps.amine.bou.readerforselfoss.utils.persistence.toEntity
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.Callback
|
import retrofit2.Callback
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
@ -37,118 +32,103 @@ import kotlin.concurrent.thread
|
|||||||
class LoadingWorker(val context: Context, params: WorkerParameters) : Worker(context, params) {
|
class LoadingWorker(val context: Context, params: WorkerParameters) : Worker(context, params) {
|
||||||
lateinit var db: AppDatabase
|
lateinit var db: AppDatabase
|
||||||
|
|
||||||
override fun doWork(): Result {
|
override fun doWork(): Result {
|
||||||
val settings =
|
if (context.isNetworkAccessible(null)) {
|
||||||
this.context.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
|
||||||
val sharedPref = PreferenceManager.getDefaultSharedPreferences(this.context)
|
|
||||||
val periodicRefresh = sharedPref.getBoolean("periodic_refresh", false)
|
|
||||||
if (periodicRefresh) {
|
|
||||||
val api = SelfossApi(
|
|
||||||
this.context,
|
|
||||||
null,
|
|
||||||
settings.getBoolean("isSelfSignedCert", false),
|
|
||||||
sharedPref.getString("api_timeout", "-1")!!.toLong()
|
|
||||||
)
|
|
||||||
|
|
||||||
if (isNetworkAvailable(context)) {
|
val notificationManager =
|
||||||
|
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
val notification = NotificationCompat.Builder(applicationContext, Config.syncChannelId)
|
||||||
val notificationManager =
|
.setContentTitle(context.getString(R.string.loading_notification_title))
|
||||||
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
.setContentText(context.getString(R.string.loading_notification_text))
|
||||||
|
.setOngoing(true)
|
||||||
|
.setPriority(PRIORITY_LOW)
|
||||||
|
.setChannelId(Config.syncChannelId)
|
||||||
|
.setSmallIcon(R.drawable.ic_stat_cloud_download_black_24dp)
|
||||||
|
|
||||||
val notification =
|
notificationManager.notify(1, notification.build())
|
||||||
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_stat_cloud_download_black_24dp)
|
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
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()
|
||||||
|
|
||||||
db = Room.databaseBuilder(
|
val api = SelfossApi(
|
||||||
applicationContext,
|
this.context,
|
||||||
AppDatabase::class.java, "selfoss-database"
|
null,
|
||||||
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3)
|
settings.getBoolean("isSelfSignedCert", false),
|
||||||
.addMigrations(MIGRATION_3_4).build()
|
sharedPref.getString("api_timeout", "-1").toLong()
|
||||||
|
)
|
||||||
|
|
||||||
|
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_tab_fiber_new_black_24dp)
|
||||||
|
|
||||||
|
Timer("", false).schedule(4000) {
|
||||||
|
notificationManager.notify(2, newItemsNotification.build())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
apiItems.map {it.preloadImages(context)}
|
||||||
|
}
|
||||||
|
Timer("", false).schedule(4000) {
|
||||||
|
notificationManager.cancel(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
thread {
|
||||||
val actions = db.actionsDao().actions()
|
val actions = db.actionsDao().actions()
|
||||||
|
|
||||||
actions.forEach { action ->
|
actions.forEach { action ->
|
||||||
when {
|
when {
|
||||||
action.read -> doAndReportOnFail(
|
action.read -> doAndReportOnFail(api.markItem(action.articleId), action)
|
||||||
api.markItem(action.articleId),
|
action.unread -> doAndReportOnFail(api.unmarkItem(action.articleId), action)
|
||||||
action
|
action.starred -> doAndReportOnFail(api.starrItem(action.articleId), action)
|
||||||
)
|
|
||||||
action.unread -> doAndReportOnFail(
|
|
||||||
api.unmarkItem(action.articleId),
|
|
||||||
action
|
|
||||||
)
|
|
||||||
action.starred -> doAndReportOnFail(
|
|
||||||
api.starrItem(action.articleId),
|
|
||||||
action
|
|
||||||
)
|
|
||||||
action.unstarred -> doAndReportOnFail(
|
action.unstarred -> doAndReportOnFail(
|
||||||
api.unstarrItem(action.articleId),
|
api.unstarrItem(action.articleId),
|
||||||
action
|
action
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getAndStoreAllItems(context, api, db)
|
|
||||||
SharedItems.updateDatabase(db)
|
|
||||||
storeItems(notifyNewItems, notificationManager)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Result.success()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun storeItems(notifyNewItems: Boolean, notificationManager: NotificationManager) {
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
val apiItems = SharedItems.items
|
|
||||||
|
|
||||||
|
|
||||||
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 pflags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
||||||
PendingIntent.FLAG_IMMUTABLE
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, pflags)
|
|
||||||
|
|
||||||
val newItemsNotification =
|
|
||||||
NotificationCompat.Builder(applicationContext, 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_tab_fiber_new_black_24dp)
|
|
||||||
|
|
||||||
Timer("", false).schedule(4000) {
|
|
||||||
notificationManager.notify(2, newItemsNotification.build())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
apiItems.map { it.preloadImages(context) }
|
|
||||||
Timer("", false).schedule(4000) {
|
|
||||||
notificationManager.cancel(1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return Result.success()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T> doAndReportOnFail(call: Call<T>, action: ActionEntity) {
|
private fun <T> doAndReportOnFail(call: Call<T>, action: ActionEntity) {
|
||||||
|
@ -9,16 +9,17 @@ import android.graphics.Bitmap
|
|||||||
import android.graphics.Typeface
|
import android.graphics.Typeface
|
||||||
import android.graphics.drawable.ColorDrawable
|
import android.graphics.drawable.ColorDrawable
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.preference.PreferenceManager
|
import android.preference.PreferenceManager
|
||||||
import android.view.*
|
import android.view.*
|
||||||
import android.webkit.*
|
import android.webkit.*
|
||||||
import android.widget.Toast
|
import androidx.browser.customtabs.CustomTabsIntent
|
||||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.widget.NestedScrollView
|
import androidx.core.widget.NestedScrollView
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.browser.customtabs.CustomTabsIntent
|
|
||||||
import androidx.core.content.res.ResourcesCompat
|
import androidx.core.content.res.ResourcesCompat
|
||||||
import androidx.room.Room
|
import androidx.room.Room
|
||||||
import apps.amine.bou.readerforselfoss.ImageActivity
|
import apps.amine.bou.readerforselfoss.ImageActivity
|
||||||
@ -27,34 +28,42 @@ 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.databinding.FragmentArticleBinding
|
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||||
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
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_1_2
|
||||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_2_3
|
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_2_3
|
||||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_3_4
|
|
||||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||||
import apps.amine.bou.readerforselfoss.utils.*
|
import apps.amine.bou.readerforselfoss.utils.Config
|
||||||
|
import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent
|
||||||
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
||||||
import apps.amine.bou.readerforselfoss.utils.glide.loadMaybeBasicAuth
|
import apps.amine.bou.readerforselfoss.utils.glide.loadMaybeBasicAuth
|
||||||
import apps.amine.bou.readerforselfoss.utils.glide.getBitmapInputStream
|
import apps.amine.bou.readerforselfoss.utils.glide.getBitmapInputStream
|
||||||
|
import apps.amine.bou.readerforselfoss.utils.isEmptyOrNullOrNullString
|
||||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
||||||
|
import apps.amine.bou.readerforselfoss.utils.openItemUrl
|
||||||
|
import apps.amine.bou.readerforselfoss.utils.shareLink
|
||||||
|
import apps.amine.bou.readerforselfoss.utils.sourceAndDateText
|
||||||
|
import apps.amine.bou.readerforselfoss.utils.succeeded
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
import com.bumptech.glide.request.RequestOptions
|
import com.bumptech.glide.request.RequestOptions
|
||||||
import com.github.rubensousa.floatingtoolbar.FloatingToolbar
|
import com.github.rubensousa.floatingtoolbar.FloatingToolbar
|
||||||
|
import kotlinx.android.synthetic.main.fragment_article.view.*
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.Callback
|
import retrofit2.Callback
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
import java.net.MalformedURLException
|
import java.net.MalformedURLException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.util.*
|
|
||||||
import java.util.concurrent.ExecutionException
|
import java.util.concurrent.ExecutionException
|
||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
|
import kotlin.concurrent.thread
|
||||||
|
|
||||||
class ArticleFragment : Fragment() {
|
class ArticleFragment : Fragment() {
|
||||||
|
private lateinit var pageNumber: Number
|
||||||
private var fontSize: Int = 16
|
private var fontSize: Int = 16
|
||||||
private lateinit var item: Item
|
private lateinit var allItems: ArrayList<Item>
|
||||||
private var mCustomTabActivityHelper: CustomTabActivityHelper? = null
|
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
|
||||||
@ -67,15 +76,14 @@ class ArticleFragment : Fragment() {
|
|||||||
private lateinit var db: AppDatabase
|
private lateinit var db: AppDatabase
|
||||||
private lateinit var textAlignment: String
|
private lateinit var textAlignment: String
|
||||||
private lateinit var config: Config
|
private lateinit var config: Config
|
||||||
private var _binding: FragmentArticleBinding? = null
|
|
||||||
private val binding get() = _binding!!
|
private var rootView: ViewGroup? = null
|
||||||
|
|
||||||
private lateinit var prefs: SharedPreferences
|
private lateinit var prefs: SharedPreferences
|
||||||
|
|
||||||
private var typeface: Typeface? = null
|
private var typeface: Typeface? = null
|
||||||
private var resId: Int = 0
|
private var resId: Int = 0
|
||||||
private var font = ""
|
private var font = ""
|
||||||
private var staticBar = false
|
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
super.onStop()
|
super.onStop()
|
||||||
@ -85,46 +93,47 @@ class ArticleFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
appColors = AppColors(requireActivity())
|
appColors = AppColors(activity!!)
|
||||||
config = Config(requireActivity())
|
config = Config(activity!!)
|
||||||
|
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
item = requireArguments().getParcelable(ARG_ITEMS)!!
|
pageNumber = arguments!!.getInt(ARG_POSITION)
|
||||||
|
allItems = arguments!!.getParcelableArrayList(ARG_ITEMS)
|
||||||
|
|
||||||
db = Room.databaseBuilder(
|
db = Room.databaseBuilder(
|
||||||
requireContext(),
|
context!!,
|
||||||
AppDatabase::class.java, "selfoss-database"
|
AppDatabase::class.java, "selfoss-database"
|
||||||
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).addMigrations(MIGRATION_3_4).build()
|
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).build()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater,
|
inflater: LayoutInflater,
|
||||||
container: ViewGroup?,
|
container: ViewGroup?,
|
||||||
savedInstanceState: Bundle?
|
savedInstanceState: Bundle?
|
||||||
): View {
|
): View? {
|
||||||
try {
|
try {
|
||||||
_binding = FragmentArticleBinding.inflate(inflater, container, false)
|
rootView = inflater
|
||||||
|
.inflate(R.layout.fragment_article, container, false) as ViewGroup
|
||||||
|
|
||||||
url = item.getLinkDecoded()
|
url = allItems[pageNumber.toInt()].getLinkDecoded()
|
||||||
contentText = item.content
|
contentText = allItems[pageNumber.toInt()].content
|
||||||
contentTitle = item.getTitleDecoded()
|
contentTitle = allItems[pageNumber.toInt()].getTitleDecoded()
|
||||||
contentImage = item.getThumbnail(requireActivity())
|
contentImage = allItems[pageNumber.toInt()].getThumbnail(activity!!)
|
||||||
contentSource = item.sourceAndDateText()
|
contentSource = allItems[pageNumber.toInt()].sourceAndDateText()
|
||||||
allImages = item.getImages()
|
allImages = allItems[pageNumber.toInt()].getImages()
|
||||||
|
|
||||||
prefs = PreferenceManager.getDefaultSharedPreferences(activity)
|
prefs = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||||
editor = prefs.edit()
|
editor = prefs.edit()
|
||||||
fontSize = prefs.getString("reader_font_size", "16")!!.toInt()
|
fontSize = prefs.getString("reader_font_size", "16").toInt()
|
||||||
staticBar = prefs.getBoolean("reader_static_bar", false)
|
|
||||||
|
|
||||||
font = prefs.getString("reader_font", "")!!
|
font = prefs.getString("reader_font", "")
|
||||||
if (font.isNotEmpty()) {
|
if (font.isNotEmpty()) {
|
||||||
resId = requireContext().resources.getIdentifier(font, "font", requireContext().packageName)
|
resId = context!!.resources.getIdentifier(font, "font", context!!.packageName)
|
||||||
typeface = try {
|
typeface = try {
|
||||||
ResourcesCompat.getFont(requireContext(), resId)!!
|
ResourcesCompat.getFont(context!!, resId)!!
|
||||||
} catch (e: java.lang.Exception) {
|
} catch (e: java.lang.Exception) {
|
||||||
// ACRA.getErrorReporter().maybeHandleSilentException(Throwable("Font loading issue: ${e.message}"), requireContext())
|
// ACRA.getErrorReporter().maybeHandleSilentException(Throwable("Font loading issue: ${e.message}"), context!!)
|
||||||
// Just to be sure
|
// Just to be sure
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@ -132,27 +141,27 @@ class ArticleFragment : Fragment() {
|
|||||||
|
|
||||||
refreshAlignment()
|
refreshAlignment()
|
||||||
|
|
||||||
val settings = requireActivity().getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
val settings = activity!!.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||||
|
|
||||||
val api = SelfossApi(
|
val api = SelfossApi(
|
||||||
requireContext(),
|
context!!,
|
||||||
requireActivity(),
|
activity!!,
|
||||||
settings.getBoolean("isSelfSignedCert", false),
|
settings.getBoolean("isSelfSignedCert", false),
|
||||||
prefs.getString("api_timeout", "-1")!!.toLong()
|
prefs.getString("api_timeout", "-1").toLong()
|
||||||
)
|
)
|
||||||
|
|
||||||
fab = binding.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 = binding.floatingToolbar
|
val floatingToolbar: FloatingToolbar = rootView!!.floatingToolbar
|
||||||
floatingToolbar.attachFab(fab)
|
floatingToolbar.attachFab(fab)
|
||||||
|
|
||||||
floatingToolbar.background = ColorDrawable(appColors.colorAccent)
|
floatingToolbar.background = ColorDrawable(appColors.colorAccent)
|
||||||
|
|
||||||
val customTabsIntent = requireActivity().buildCustomTabsIntent()
|
val customTabsIntent = activity!!.buildCustomTabsIntent()
|
||||||
mCustomTabActivityHelper = CustomTabActivityHelper()
|
mCustomTabActivityHelper = CustomTabActivityHelper()
|
||||||
mCustomTabActivityHelper!!.bindCustomTabsService(activity)
|
mCustomTabActivityHelper!!.bindCustomTabsService(activity)
|
||||||
|
|
||||||
@ -161,36 +170,36 @@ class ArticleFragment : Fragment() {
|
|||||||
object : FloatingToolbar.ItemClickListener {
|
object : FloatingToolbar.ItemClickListener {
|
||||||
override fun onItemClick(item: MenuItem) {
|
override fun onItemClick(item: MenuItem) {
|
||||||
when (item.itemId) {
|
when (item.itemId) {
|
||||||
R.id.more_action -> getContentFromMercury(customTabsIntent)
|
R.id.more_action -> getContentFromMercury(customTabsIntent, prefs)
|
||||||
R.id.share_action -> requireActivity().shareLink(url, contentTitle)
|
R.id.share_action -> activity!!.shareLink(url, contentTitle)
|
||||||
R.id.open_action -> requireActivity().openInBrowserAsNewTask(this@ArticleFragment.item)
|
R.id.open_action -> activity!!.openItemUrl(
|
||||||
R.id.unread_action -> if (context != null) {
|
allItems,
|
||||||
if (this@ArticleFragment.item.unread) {
|
pageNumber.toInt(),
|
||||||
SharedItems.readItem(
|
url,
|
||||||
context!!,
|
customTabsIntent,
|
||||||
api,
|
false,
|
||||||
db,
|
false,
|
||||||
this@ArticleFragment.item
|
activity!!
|
||||||
)
|
)
|
||||||
this@ArticleFragment.item.unread = false
|
R.id.unread_action -> if ((context != null && context!!.isNetworkAccessible(null)) || context == null) {
|
||||||
Toast.makeText(
|
api.unmarkItem(allItems[pageNumber.toInt()].id).enqueue(
|
||||||
context,
|
object : Callback<SuccessResponse> {
|
||||||
R.string.marked_as_read,
|
override fun onResponse(
|
||||||
Toast.LENGTH_LONG
|
call: Call<SuccessResponse>,
|
||||||
).show()
|
response: Response<SuccessResponse>
|
||||||
} else {
|
) {
|
||||||
SharedItems.unreadItem(
|
}
|
||||||
context!!,
|
|
||||||
api,
|
override fun onFailure(
|
||||||
db,
|
call: Call<SuccessResponse>,
|
||||||
this@ArticleFragment.item
|
t: Throwable
|
||||||
)
|
) {
|
||||||
this@ArticleFragment.item.unread = true
|
}
|
||||||
Toast.makeText(
|
}
|
||||||
context,
|
)
|
||||||
R.string.marked_as_unread,
|
} else {
|
||||||
Toast.LENGTH_LONG
|
thread {
|
||||||
).show()
|
db.actionsDao().insertAllActions(ActionEntity(allItems[pageNumber.toInt()].id, false, true, false, false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> Unit
|
else -> Unit
|
||||||
@ -202,76 +211,61 @@ class ArticleFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
if (staticBar) {
|
rootView!!.source.text = contentSource
|
||||||
fab.hide()
|
|
||||||
floatingToolbar.show()
|
|
||||||
}
|
|
||||||
|
|
||||||
binding.source.text = contentSource
|
|
||||||
if (typeface != null) {
|
if (typeface != null) {
|
||||||
binding.source.typeface = typeface
|
rootView!!.source.typeface = typeface
|
||||||
}
|
}
|
||||||
|
|
||||||
if (contentText.isEmptyOrNullOrNullString()) {
|
if (contentText.isEmptyOrNullOrNullString()) {
|
||||||
getContentFromMercury(customTabsIntent)
|
getContentFromMercury(customTabsIntent, prefs)
|
||||||
} else {
|
} else {
|
||||||
binding.titleView.text = contentTitle
|
rootView!!.titleView.text = contentTitle
|
||||||
if (typeface != null) {
|
if (typeface != null) {
|
||||||
binding.titleView.typeface = typeface
|
rootView!!.titleView.typeface = typeface
|
||||||
}
|
}
|
||||||
|
|
||||||
htmlToWebview()
|
htmlToWebview()
|
||||||
|
|
||||||
if (!contentImage.isEmptyOrNullOrNullString() && context != null) {
|
if (!contentImage.isEmptyOrNullOrNullString() && context != null) {
|
||||||
binding.imageView.visibility = View.VISIBLE
|
rootView!!.imageView.visibility = View.VISIBLE
|
||||||
Glide
|
Glide
|
||||||
.with(requireContext())
|
.with(context!!)
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
.loadMaybeBasicAuth(config, contentImage)
|
.loadMaybeBasicAuth(config, contentImage)
|
||||||
.apply(RequestOptions.fitCenterTransform())
|
.apply(RequestOptions.fitCenterTransform())
|
||||||
.into(binding.imageView)
|
.into(rootView!!.imageView)
|
||||||
} else {
|
} else {
|
||||||
binding.imageView.visibility = View.GONE
|
rootView!!.imageView.visibility = View.GONE
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.nestedScrollView.setOnScrollChangeListener(
|
rootView!!.nestedScrollView.setOnScrollChangeListener(
|
||||||
NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
|
NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
|
||||||
if (scrollY > oldScrollY) {
|
if (scrollY > oldScrollY) {
|
||||||
floatingToolbar.hide()
|
|
||||||
fab.hide()
|
fab.hide()
|
||||||
} else {
|
} else {
|
||||||
if (staticBar) {
|
if (floatingToolbar.isShowing) floatingToolbar.hide() else fab.show()
|
||||||
floatingToolbar.show()
|
|
||||||
} else {
|
|
||||||
if (floatingToolbar.isShowing) floatingToolbar.hide() else fab.show()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
} catch (e: InflateException) {
|
} catch (e: InflateException) {
|
||||||
AlertDialog.Builder(requireContext())
|
AlertDialog.Builder(context!!)
|
||||||
.setMessage(requireContext().getString(R.string.webview_dialog_issue_message))
|
.setMessage(context!!.getString(R.string.webview_dialog_issue_message))
|
||||||
.setTitle(requireContext().getString(R.string.webview_dialog_issue_title))
|
.setTitle(context!!.getString(R.string.webview_dialog_issue_title))
|
||||||
.setPositiveButton(android.R.string.ok
|
.setPositiveButton(android.R.string.ok
|
||||||
) { _, _ ->
|
) { dialog, which ->
|
||||||
val sharedPref = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
val sharedPref = PreferenceManager.getDefaultSharedPreferences(context!!)
|
||||||
val editor = sharedPref.edit()
|
val editor = sharedPref.edit()
|
||||||
editor.putBoolean("prefer_article_viewer", false)
|
editor.putBoolean("prefer_article_viewer", false)
|
||||||
editor.apply()
|
editor.commit()
|
||||||
requireActivity().finish()
|
activity!!.finish()
|
||||||
}
|
}
|
||||||
.create()
|
.create()
|
||||||
.show()
|
.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
return binding.root
|
return rootView
|
||||||
}
|
|
||||||
|
|
||||||
override fun onDestroyView() {
|
|
||||||
super.onDestroyView()
|
|
||||||
_binding = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun refreshAlignment() {
|
private fun refreshAlignment() {
|
||||||
@ -282,9 +276,12 @@ class ArticleFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getContentFromMercury(customTabsIntent: CustomTabsIntent) {
|
private fun getContentFromMercury(
|
||||||
if ((context != null && requireContext().isNetworkAccessible(null)) || context == null) {
|
customTabsIntent: CustomTabsIntent,
|
||||||
binding.progressBar.visibility = View.VISIBLE
|
prefs: SharedPreferences
|
||||||
|
) {
|
||||||
|
if ((context != null && context!!.isNetworkAccessible(null)) || context == null) {
|
||||||
|
rootView!!.progressBar.visibility = View.VISIBLE
|
||||||
val parser = MercuryApi()
|
val parser = MercuryApi()
|
||||||
|
|
||||||
parser.parseUrl(url).enqueue(
|
parser.parseUrl(url).enqueue(
|
||||||
@ -297,9 +294,9 @@ 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 {
|
||||||
binding.titleView.text = response.body()!!.title
|
rootView!!.titleView.text = response.body()!!.title
|
||||||
if (typeface != null) {
|
if (typeface != null) {
|
||||||
binding.titleView.typeface = typeface
|
rootView!!.titleView.typeface = typeface
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// Note: Mercury may return relative urls... If it does the url val will not be changed.
|
// Note: Mercury may return relative urls... If it does the url val will not be changed.
|
||||||
@ -319,18 +316,18 @@ 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) {
|
||||||
binding.imageView.visibility = View.VISIBLE
|
rootView!!.imageView.visibility = View.VISIBLE
|
||||||
try {
|
try {
|
||||||
Glide
|
Glide
|
||||||
.with(requireContext())
|
.with(context!!)
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
.loadMaybeBasicAuth(config, response.body()!!.lead_image_url.orEmpty())
|
.loadMaybeBasicAuth(config, response.body()!!.lead_image_url.orEmpty())
|
||||||
.apply(RequestOptions.fitCenterTransform())
|
.apply(RequestOptions.fitCenterTransform())
|
||||||
.into(binding.imageView)
|
.into(rootView!!.imageView)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
binding.imageView.visibility = View.GONE
|
rootView!!.imageView.visibility = View.GONE
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
@ -338,9 +335,9 @@ class ArticleFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
binding.nestedScrollView.scrollTo(0, 0)
|
rootView!!.nestedScrollView.scrollTo(0, 0)
|
||||||
|
|
||||||
binding.progressBar.visibility = View.GONE
|
rootView!!.progressBar.visibility = View.GONE
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
if (context != null) {
|
if (context != null) {
|
||||||
}
|
}
|
||||||
@ -372,46 +369,76 @@ class ArticleFragment : Fragment() {
|
|||||||
val stringColor = String.format("#%06X", 0xFFFFFF and appColors.colorAccent)
|
val stringColor = String.format("#%06X", 0xFFFFFF and appColors.colorAccent)
|
||||||
|
|
||||||
val attrs: IntArray = intArrayOf(android.R.attr.fontFamily)
|
val attrs: IntArray = intArrayOf(android.R.attr.fontFamily)
|
||||||
val a: TypedArray = requireContext().obtainStyledAttributes(resId, attrs)
|
val a: TypedArray = context!!.obtainStyledAttributes(resId, attrs)
|
||||||
|
|
||||||
|
|
||||||
binding.webcontent.settings.standardFontFamily = a.getString(0)
|
rootView!!.webcontent.settings.standardFontFamily = a.getString(0)
|
||||||
binding.webcontent.visibility = View.VISIBLE
|
rootView!!.webcontent.visibility = View.VISIBLE
|
||||||
|
val (textColor, backgroundColor) = if (appColors.isDarkTheme) {
|
||||||
// TODO: Set the color strings programmatically
|
if (context != null) {
|
||||||
val (stringTextColor, stringBackgroundColor) = if (appColors.isDarkTheme) {
|
rootView!!.webcontent.setBackgroundColor(
|
||||||
Pair("#FFFFFF", "#303030")
|
ContextCompat.getColor(
|
||||||
|
context!!,
|
||||||
|
R.color.dark_webview
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Pair(ContextCompat.getColor(context!!, R.color.dark_webview_text), ContextCompat.getColor(context!!, R.color.dark_webview))
|
||||||
|
} else {
|
||||||
|
Pair(null, null)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Pair("#212121", "#FAFAFA")
|
if (context != null) {
|
||||||
|
rootView!!.webcontent.setBackgroundColor(
|
||||||
|
ContextCompat.getColor(
|
||||||
|
context!!,
|
||||||
|
R.color.light_webview
|
||||||
|
)
|
||||||
|
)
|
||||||
|
Pair(ContextCompat.getColor(context!!, R.color.light_webview_text), ContextCompat.getColor(context!!, R.color.light_webview))
|
||||||
|
} else {
|
||||||
|
Pair(null, null)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.webcontent.settings.useWideViewPort = true
|
val stringTextColor: String = if (textColor != null) {
|
||||||
binding.webcontent.settings.loadWithOverviewMode = true
|
String.format("#%06X", 0xFFFFFF and textColor)
|
||||||
binding.webcontent.settings.javaScriptEnabled = false
|
} else {
|
||||||
|
"#000000"
|
||||||
|
}
|
||||||
|
|
||||||
binding.webcontent.webViewClient = object : WebViewClient() {
|
val stringBackgroundColor = if (backgroundColor != null) {
|
||||||
|
String.format("#%06X", 0xFFFFFF and backgroundColor)
|
||||||
|
} else {
|
||||||
|
"#FFFFFF"
|
||||||
|
}
|
||||||
|
|
||||||
|
rootView!!.webcontent.settings.useWideViewPort = true
|
||||||
|
rootView!!.webcontent.settings.loadWithOverviewMode = true
|
||||||
|
rootView!!.webcontent.settings.javaScriptEnabled = false
|
||||||
|
|
||||||
|
rootView!!.webcontent.webViewClient = object : WebViewClient() {
|
||||||
override fun shouldOverrideUrlLoading(view: WebView?, url : String): Boolean {
|
override fun shouldOverrideUrlLoading(view: WebView?, url : String): Boolean {
|
||||||
if (binding.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
|
if (rootView!!.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
|
||||||
requireContext().startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
|
rootView!!.context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun shouldInterceptRequest(view: WebView?, url: String): WebResourceResponse? {
|
override fun shouldInterceptRequest(view: WebView?, url: String): WebResourceResponse? {
|
||||||
val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL)
|
val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL)
|
||||||
if (url.lowercase(Locale.US).contains(".jpg") || url.lowercase(Locale.US).contains(".jpeg")) {
|
if (url.toLowerCase().contains(".jpg") || url.toLowerCase().contains(".jpeg")) {
|
||||||
try {
|
try {
|
||||||
val image = Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get()
|
val image = Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get()
|
||||||
return WebResourceResponse("image/jpg", "UTF-8", getBitmapInputStream(image, Bitmap.CompressFormat.JPEG))
|
return WebResourceResponse("image/jpg", "UTF-8", getBitmapInputStream(image, Bitmap.CompressFormat.JPEG))
|
||||||
}catch ( e : ExecutionException) {}
|
}catch ( e : ExecutionException) {}
|
||||||
}
|
}
|
||||||
else if (url.lowercase(Locale.US).contains(".png")) {
|
else if (url.toLowerCase().contains(".png")) {
|
||||||
try {
|
try {
|
||||||
val image = Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get()
|
val image = Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get()
|
||||||
return WebResourceResponse("image/jpg", "UTF-8", getBitmapInputStream(image, Bitmap.CompressFormat.PNG))
|
return WebResourceResponse("image/jpg", "UTF-8", getBitmapInputStream(image, Bitmap.CompressFormat.PNG))
|
||||||
}catch ( e : ExecutionException) {}
|
}catch ( e : ExecutionException) {}
|
||||||
}
|
}
|
||||||
else if (url.lowercase(Locale.US).contains(".webp")) {
|
else if (url.toLowerCase().contains(".webp")) {
|
||||||
try {
|
try {
|
||||||
val image = Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get()
|
val image = Glide.with(view).asBitmap().apply(glideOptions).load(url).submit().get()
|
||||||
return WebResourceResponse("image/jpg", "UTF-8", getBitmapInputStream(image, Bitmap.CompressFormat.WEBP))
|
return WebResourceResponse("image/jpg", "UTF-8", getBitmapInputStream(image, Bitmap.CompressFormat.WEBP))
|
||||||
@ -428,10 +455,14 @@ class ArticleFragment : Fragment() {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
binding.webcontent.setOnTouchListener { _, event -> gestureDetector.onTouchEvent(event)}
|
rootView!!.webcontent.setOnTouchListener { _, event -> gestureDetector.onTouchEvent(event)}
|
||||||
|
|
||||||
binding.webcontent.settings.layoutAlgorithm =
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||||
WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING
|
rootView!!.webcontent.settings.layoutAlgorithm =
|
||||||
|
WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING
|
||||||
|
} else {
|
||||||
|
rootView!!.webcontent.settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.SINGLE_COLUMN
|
||||||
|
}
|
||||||
|
|
||||||
var baseUrl: String? = null
|
var baseUrl: String? = null
|
||||||
|
|
||||||
@ -459,7 +490,7 @@ class ArticleFragment : Fragment() {
|
|||||||
""
|
""
|
||||||
}
|
}
|
||||||
|
|
||||||
binding.webcontent.loadDataWithBaseURL(
|
rootView!!.webcontent.loadDataWithBaseURL(
|
||||||
baseUrl,
|
baseUrl,
|
||||||
"""<html>
|
"""<html>
|
||||||
|<head>
|
|<head>
|
||||||
@ -511,44 +542,41 @@ class ArticleFragment : Fragment() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun scrollDown() {
|
|
||||||
val height = binding.nestedScrollView.measuredHeight
|
|
||||||
binding.nestedScrollView.smoothScrollBy(0, height/2)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun scrollUp() {
|
|
||||||
val height = binding.nestedScrollView.measuredHeight
|
|
||||||
binding.nestedScrollView.smoothScrollBy(0, -height/2)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun openInBrowserAfterFailing(customTabsIntent: CustomTabsIntent) {
|
private fun openInBrowserAfterFailing(customTabsIntent: CustomTabsIntent) {
|
||||||
binding.progressBar.visibility = View.GONE
|
rootView!!.progressBar.visibility = View.GONE
|
||||||
requireActivity().openItemUrlInternalBrowser(
|
activity!!.openItemUrl(
|
||||||
url,
|
allItems,
|
||||||
customTabsIntent,
|
pageNumber.toInt(),
|
||||||
requireActivity()
|
url,
|
||||||
|
customTabsIntent,
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
activity!!
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
private const val ARG_POSITION = "position"
|
||||||
private const val ARG_ITEMS = "items"
|
private const val ARG_ITEMS = "items"
|
||||||
|
|
||||||
fun newInstance(
|
fun newInstance(
|
||||||
item: Item
|
position: Int,
|
||||||
|
allItems: ArrayList<Item>
|
||||||
): ArticleFragment {
|
): ArticleFragment {
|
||||||
val fragment = ArticleFragment()
|
val fragment = ArticleFragment()
|
||||||
val args = Bundle()
|
val args = Bundle()
|
||||||
args.putParcelable(ARG_ITEMS, item)
|
args.putInt(ARG_POSITION, position)
|
||||||
|
args.putParcelableArrayList(ARG_ITEMS, allItems)
|
||||||
fragment.arguments = args
|
fragment.arguments = args
|
||||||
return fragment
|
return fragment
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun performClick(): Boolean {
|
fun performClick(): Boolean {
|
||||||
if (binding.webcontent.hitTestResult.type == WebView.HitTestResult.IMAGE_TYPE ||
|
if (rootView!!.webcontent.hitTestResult.type == WebView.HitTestResult.IMAGE_TYPE ||
|
||||||
binding.webcontent.hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
|
rootView!!.webcontent.hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
|
||||||
|
|
||||||
val position : Int = allImages.indexOf(binding.webcontent.hitTestResult.extra)
|
val position : Int = allImages.indexOf(rootView!!.webcontent.hitTestResult.extra)
|
||||||
|
|
||||||
val intent = Intent(activity, ImageActivity::class.java)
|
val intent = Intent(activity, ImageActivity::class.java)
|
||||||
intent.putExtra("allImages", allImages)
|
intent.putExtra("allImages", allImages)
|
||||||
|
@ -4,43 +4,35 @@ import android.os.Bundle
|
|||||||
import android.view.*
|
import android.view.*
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import apps.amine.bou.readerforselfoss.R
|
import apps.amine.bou.readerforselfoss.R
|
||||||
import apps.amine.bou.readerforselfoss.databinding.FragmentImageBinding
|
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.Glide
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
import com.bumptech.glide.request.RequestOptions
|
import com.bumptech.glide.request.RequestOptions
|
||||||
|
import kotlinx.android.synthetic.main.fragment_image.view.*
|
||||||
|
|
||||||
class ImageFragment : Fragment() {
|
class ImageFragment : Fragment() {
|
||||||
|
|
||||||
private lateinit var imageUrl : String
|
private lateinit var imageUrl : String
|
||||||
private val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL)
|
private val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL)
|
||||||
private var _binding: FragmentImageBinding? = null
|
|
||||||
private val binding get() = _binding
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
imageUrl = requireArguments().getString("imageUrl")!!
|
imageUrl = arguments!!.getString("imageUrl")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
_binding = FragmentImageBinding.inflate(inflater, container, false)
|
val view : View = inflater.inflate(R.layout.fragment_image, container, false)
|
||||||
val view = binding?.root
|
|
||||||
|
|
||||||
binding!!.photoView.visibility = View.VISIBLE
|
view.photoView.visibility = View.VISIBLE
|
||||||
Glide.with(activity)
|
Glide.with(activity)
|
||||||
.asBitmap()
|
.asBitmap()
|
||||||
.apply(glideOptions)
|
.apply(glideOptions)
|
||||||
.load(imageUrl)
|
.load(imageUrl)
|
||||||
.into(binding!!.photoView)
|
.into(view.photoView)
|
||||||
|
|
||||||
return view
|
return view
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDestroyView() {
|
|
||||||
super.onDestroyView()
|
|
||||||
_binding = null
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private const val ARG_IMAGE = "imageUrl"
|
private const val ARG_IMAGE = "imageUrl"
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity
|
|||||||
@Dao
|
@Dao
|
||||||
interface ActionsDao {
|
interface ActionsDao {
|
||||||
@Query("SELECT * FROM actions order by id asc")
|
@Query("SELECT * FROM actions order by id asc")
|
||||||
suspend fun actions(): List<ActionEntity>
|
fun actions(): List<ActionEntity>
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
fun insertAllActions(vararg actions: ActionEntity)
|
fun insertAllActions(vararg actions: ActionEntity)
|
||||||
|
@ -13,17 +13,17 @@ import androidx.room.Update
|
|||||||
@Dao
|
@Dao
|
||||||
interface ItemsDao {
|
interface ItemsDao {
|
||||||
@Query("SELECT * FROM items order by id desc")
|
@Query("SELECT * FROM items order by id desc")
|
||||||
suspend fun items(): List<ItemEntity>
|
fun items(): List<ItemEntity>
|
||||||
|
|
||||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||||
suspend fun insertAllItems(vararg items: ItemEntity)
|
fun insertAllItems(vararg items: ItemEntity)
|
||||||
|
|
||||||
@Query("DELETE FROM items")
|
@Query("DELETE FROM items")
|
||||||
suspend fun deleteAllItems()
|
fun deleteAllItems()
|
||||||
|
|
||||||
@Delete
|
@Delete
|
||||||
suspend fun delete(item: ItemEntity)
|
fun delete(item: ItemEntity)
|
||||||
|
|
||||||
@Update
|
@Update
|
||||||
suspend fun updateItem(item: ItemEntity)
|
fun updateItem(item: ItemEntity)
|
||||||
}
|
}
|
@ -10,7 +10,7 @@ import apps.amine.bou.readerforselfoss.persistence.entities.ItemEntity
|
|||||||
import apps.amine.bou.readerforselfoss.persistence.entities.SourceEntity
|
import apps.amine.bou.readerforselfoss.persistence.entities.SourceEntity
|
||||||
import apps.amine.bou.readerforselfoss.persistence.entities.TagEntity
|
import apps.amine.bou.readerforselfoss.persistence.entities.TagEntity
|
||||||
|
|
||||||
@Database(entities = [TagEntity::class, SourceEntity::class, ItemEntity::class, ActionEntity::class], version = 4)
|
@Database(entities = [TagEntity::class, SourceEntity::class, ItemEntity::class, ActionEntity::class], version = 3)
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
abstract fun drawerDataDao(): DrawerDataDao
|
abstract fun drawerDataDao(): DrawerDataDao
|
||||||
|
|
||||||
|
@ -14,21 +14,3 @@ val MIGRATION_2_3: Migration = object : Migration(2, 3) {
|
|||||||
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`))")
|
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`))")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val MIGRATION_3_4: Migration = object : Migration(3, 4) {
|
|
||||||
override fun migrate(database: SupportSQLiteDatabase) {
|
|
||||||
// @see https://stackoverflow.com/questions/57392015/how-to-migrate-not-null-table-column-into-null-in-android-room-database
|
|
||||||
// Create the new table
|
|
||||||
database.execSQL("CREATE TABLE IF NOT EXISTS `itemstmp` (`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, `icon` TEXT, `link` TEXT NOT NULL, `sourcetitle` TEXT NOT NULL, `tags` TEXT NOT NULL, PRIMARY KEY(`id`))")
|
|
||||||
|
|
||||||
// Copy the data
|
|
||||||
database.execSQL(
|
|
||||||
"INSERT INTO itemstmp (`id`, `datetime`, `title`, `content`, `unread`, `starred`, `thumbnail`, `icon`, `link`, `sourcetitle`, `tags`) SELECT `id`, `datetime`, `title`, `content`, `unread`, `starred`, `thumbnail`, `icon`, `link`, `sourcetitle`, `tags` FROM items")
|
|
||||||
|
|
||||||
// Remove the old table
|
|
||||||
database.execSQL("DROP TABLE items")
|
|
||||||
|
|
||||||
// Change the table name to the correct one
|
|
||||||
database.execSQL("ALTER TABLE itemstmp RENAME TO items")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -0,0 +1,142 @@
|
|||||||
|
package apps.amine.bou.readerforselfoss.settings;
|
||||||
|
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.PreferenceActivity;
|
||||||
|
import androidx.annotation.LayoutRes;
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
import com.google.android.material.appbar.AppBarLayout;
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import androidx.appcompat.app.AppCompatDelegate;
|
||||||
|
import androidx.appcompat.widget.Toolbar;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
|
||||||
|
import com.ftinc.scoop.Scoop;
|
||||||
|
|
||||||
|
import apps.amine.bou.readerforselfoss.R;
|
||||||
|
import apps.amine.bou.readerforselfoss.themes.AppColors;
|
||||||
|
import apps.amine.bou.readerforselfoss.themes.Toppings;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link PreferenceActivity} which implements and proxies the necessary calls
|
||||||
|
* to be used with AppCompat.
|
||||||
|
*/
|
||||||
|
public abstract class AppCompatPreferenceActivity extends PreferenceActivity {
|
||||||
|
|
||||||
|
private AppCompatDelegate mDelegate;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
new AppColors(this);
|
||||||
|
|
||||||
|
getDelegate().installViewFactory();
|
||||||
|
getDelegate().onCreate(savedInstanceState);
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostCreate(Bundle savedInstanceState) {
|
||||||
|
super.onPostCreate(savedInstanceState);
|
||||||
|
|
||||||
|
LinearLayout root = (LinearLayout) findViewById(android.R.id.list).getParent().getParent().getParent();
|
||||||
|
AppBarLayout bar = (AppBarLayout) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
|
||||||
|
Toolbar toolbar = bar.findViewById(R.id.toolbar);
|
||||||
|
|
||||||
|
Scoop scoop = Scoop.getInstance();
|
||||||
|
scoop.bind(this, Toppings.PRIMARY.getValue(), toolbar);
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
setSupportActionBar(toolbar);
|
||||||
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
getSupportActionBar().setDisplayShowHomeEnabled(true);
|
||||||
|
|
||||||
|
root.addView(bar, 0);
|
||||||
|
|
||||||
|
getDelegate().onPostCreate(savedInstanceState);
|
||||||
|
}
|
||||||
|
|
||||||
|
ActionBar getSupportActionBar() {
|
||||||
|
return getDelegate().getSupportActionBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSupportActionBar(@Nullable Toolbar toolbar) {
|
||||||
|
getDelegate().setSupportActionBar(toolbar);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public MenuInflater getMenuInflater() {
|
||||||
|
return getDelegate().getMenuInflater();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setContentView(@LayoutRes int layoutResID) {
|
||||||
|
getDelegate().setContentView(layoutResID);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setContentView(View view) {
|
||||||
|
getDelegate().setContentView(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setContentView(View view, ViewGroup.LayoutParams params) {
|
||||||
|
getDelegate().setContentView(view, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addContentView(View view, ViewGroup.LayoutParams params) {
|
||||||
|
getDelegate().addContentView(view, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPostResume() {
|
||||||
|
super.onPostResume();
|
||||||
|
getDelegate().onPostResume();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onTitleChanged(CharSequence title, int color) {
|
||||||
|
super.onTitleChanged(title, color);
|
||||||
|
getDelegate().setTitle(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onConfigurationChanged(Configuration newConfig) {
|
||||||
|
super.onConfigurationChanged(newConfig);
|
||||||
|
getDelegate().onConfigurationChanged(newConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
getDelegate().onStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDestroy() {
|
||||||
|
super.onDestroy();
|
||||||
|
getDelegate().onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidateOptionsMenu() {
|
||||||
|
getDelegate().invalidateOptionsMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
private AppCompatDelegate getDelegate() {
|
||||||
|
if (mDelegate == null) {
|
||||||
|
mDelegate = AppCompatDelegate.create(this, null);
|
||||||
|
}
|
||||||
|
return mDelegate;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,345 @@
|
|||||||
|
package apps.amine.bou.readerforselfoss.settings;
|
||||||
|
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
|
import android.content.ClipData;
|
||||||
|
import android.content.ClipboardManager;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.content.res.Resources;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.preference.EditTextPreference;
|
||||||
|
import android.preference.Preference;
|
||||||
|
import android.preference.Preference.OnPreferenceChangeListener;
|
||||||
|
import android.preference.Preference.OnPreferenceClickListener;
|
||||||
|
import android.preference.PreferenceActivity;
|
||||||
|
import android.preference.PreferenceFragment;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
|
import android.preference.SwitchPreference;
|
||||||
|
import androidx.appcompat.app.ActionBar;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.InputFilter;
|
||||||
|
import android.text.Spanned;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import apps.amine.bou.readerforselfoss.R;
|
||||||
|
import apps.amine.bou.readerforselfoss.themes.AppColors;
|
||||||
|
import apps.amine.bou.readerforselfoss.utils.Config;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link PreferenceActivity} that presents a set of application settings. On
|
||||||
|
* handset devices, settings are presented as a single list. On tablets,
|
||||||
|
* settings are split by category, with category headers shown to the left of
|
||||||
|
* the list of settings.
|
||||||
|
* <p>
|
||||||
|
* See <a href="http://developer.android.com/design/patterns/settings.html">
|
||||||
|
* Android Design: Settings</a> for design guidelines and the <a
|
||||||
|
* href="http://developer.android.com/guide/topics/ui/settings.html">Settings
|
||||||
|
* API Guide</a> for more information on developing a Settings UI.
|
||||||
|
*/
|
||||||
|
public class SettingsActivity extends AppCompatPreferenceActivity {
|
||||||
|
/**
|
||||||
|
* A preference value change listener that updates the preference's summary
|
||||||
|
* to reflect its new value.
|
||||||
|
*/
|
||||||
|
private static Preference.OnPreferenceChangeListener sBindPreferenceSummaryToValueListener = new Preference.OnPreferenceChangeListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceChange(Preference preference, Object value) {
|
||||||
|
String stringValue = value.toString();
|
||||||
|
preference.setSummary(stringValue);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to determine if the device has an extra-large screen. For
|
||||||
|
* example, 10" tablets are extra-large.
|
||||||
|
*/
|
||||||
|
private static boolean isXLargeTablet(Context context) {
|
||||||
|
return (context.getResources().getConfiguration().screenLayout
|
||||||
|
& Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Binds a preference's summary to its value. More specifically, when the
|
||||||
|
* preference's value is changed, its summary (line of text below the
|
||||||
|
* preference title) is updated to reflect the value. The summary is also
|
||||||
|
* immediately updated upon calling this method. The exact display format is
|
||||||
|
* dependent on the type of preference.
|
||||||
|
*
|
||||||
|
* @see #sBindPreferenceSummaryToValueListener
|
||||||
|
*/
|
||||||
|
private static void bindPreferenceSummaryToValue(Preference preference) {
|
||||||
|
// Set the listener to watch for value changes.
|
||||||
|
preference.setOnPreferenceChangeListener(sBindPreferenceSummaryToValueListener);
|
||||||
|
|
||||||
|
// Trigger the listener immediately with the preference's
|
||||||
|
// current value.
|
||||||
|
sBindPreferenceSummaryToValueListener.onPreferenceChange(preference,
|
||||||
|
PreferenceManager
|
||||||
|
.getDefaultSharedPreferences(preference.getContext())
|
||||||
|
.getString(preference.getKey(), ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
new AppColors(this);
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setupActionBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up the {@link android.app.ActionBar}, if the API is available.
|
||||||
|
*/
|
||||||
|
private void setupActionBar() {
|
||||||
|
ActionBar actionBar = getSupportActionBar();
|
||||||
|
if (actionBar != null) {
|
||||||
|
// Show the Up button in the action bar.
|
||||||
|
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean onIsMultiPane() {
|
||||||
|
return isXLargeTablet(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritDoc}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||||
|
public void onBuildHeaders(List<Header> target) {
|
||||||
|
loadHeadersFromResource(R.xml.pref_headers, target);
|
||||||
|
|
||||||
|
AppColors appColors = new AppColors(this);
|
||||||
|
if (appColors != null && appColors.isDarkTheme()) {
|
||||||
|
for (Header header : target) {
|
||||||
|
tryLoadIconDark(header);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void tryLoadIconDark(Header header){
|
||||||
|
try{
|
||||||
|
if (header.fragmentArguments != null) {
|
||||||
|
String iconDark = header.fragmentArguments.getString("iconDark");
|
||||||
|
int iconDarkId = getResources().getIdentifier(iconDark, "drawable", getPackageName());
|
||||||
|
if (iconDarkId != 0) {
|
||||||
|
header.iconRes = iconDarkId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e("SettingsActivity", "Can not load dark icon", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method stops fragment injection in malicious applications.
|
||||||
|
* Make sure to deny any unknown fragments here.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected boolean isValidFragment(String fragmentName) {
|
||||||
|
return PreferenceFragment.class.getName().equals(fragmentName)
|
||||||
|
|| GeneralPreferenceFragment.class.getName().equals(fragmentName)
|
||||||
|
|| ArticleViewerPreferenceFragment.class.getName().equals(fragmentName)
|
||||||
|
|| OfflinePreferenceFragment.class.getName().equals(fragmentName)
|
||||||
|
|| ExperimentalPreferenceFragment.class.getName().equals(fragmentName)
|
||||||
|
|| LinksPreferenceFragment.class.getName().equals(fragmentName)
|
||||||
|
|| ThemePreferenceFragment.class.getName().equals(fragmentName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This fragment shows general preferences only. It is used when the
|
||||||
|
* activity is showing a two-pane settings UI.
|
||||||
|
*/
|
||||||
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||||
|
public static class GeneralPreferenceFragment extends PreferenceFragment {
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
addPreferencesFromResource(R.xml.pref_general);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
|
||||||
|
EditTextPreference itemsNumber = (EditTextPreference) findPreference("prefer_api_items_number");
|
||||||
|
itemsNumber.getEditText().setFilters(new InputFilter[]{
|
||||||
|
new InputFilter() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
|
||||||
|
try {
|
||||||
|
int input = Integer.parseInt(dest.toString() + source.toString());
|
||||||
|
if (input <= 200 && input > 0)
|
||||||
|
return null;
|
||||||
|
} catch (NumberFormatException nfe) {
|
||||||
|
Toast.makeText(getActivity(), R.string.items_number_should_be_number, Toast.LENGTH_LONG).show();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||||
|
public static class ArticleViewerPreferenceFragment extends PreferenceFragment {
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
addPreferencesFromResource(R.xml.pref_viewer);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
|
||||||
|
final EditTextPreference fontSize = (EditTextPreference) findPreference("reader_font_size");
|
||||||
|
fontSize.getEditText().addTextChangedListener(new TextWatcher() {
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable editable) {
|
||||||
|
try {
|
||||||
|
fontSize.getEditText().setTextSize(Integer.parseInt(editable.toString()));
|
||||||
|
} catch (NumberFormatException e) {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
fontSize.getEditText().setFilters(new InputFilter[]{
|
||||||
|
new InputFilter() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
|
||||||
|
try {
|
||||||
|
int input = Integer.parseInt(dest.toString() + source.toString());
|
||||||
|
if (input > 0)
|
||||||
|
return null;
|
||||||
|
} catch (NumberFormatException nfe) {}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This fragment shows general preferences only. It is used when the
|
||||||
|
* activity is showing a two-pane settings UI.
|
||||||
|
*/
|
||||||
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||||
|
public static class LinksPreferenceFragment extends PreferenceFragment {
|
||||||
|
public void openUrl(Uri uri) {
|
||||||
|
Intent browserIntent = new Intent(Intent.ACTION_VIEW, uri);
|
||||||
|
startActivity(browserIntent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
addPreferencesFromResource(R.xml.pref_links);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
|
||||||
|
findPreference("trackerLink").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
|
openUrl(Uri.parse(Config.trackerUrl));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
findPreference("sourceLink").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
|
openUrl(Uri.parse(Config.sourceUrl));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
findPreference("translation").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
|
openUrl(Uri.parse(Config.translationUrl));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||||
|
public static class ThemePreferenceFragment extends PreferenceFragment {
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
addPreferencesFromResource(R.xml.pref_theme);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
int id = item.getItemId();
|
||||||
|
if (id == R.id.clear) {
|
||||||
|
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||||
|
SharedPreferences.Editor editor = pref.edit();
|
||||||
|
editor.remove("color_primary");
|
||||||
|
editor.remove("color_primary_dark");
|
||||||
|
editor.remove("color_accent");
|
||||||
|
editor.remove("color_accent_dark");
|
||||||
|
editor.remove("dark_theme");
|
||||||
|
editor.apply();
|
||||||
|
getActivity().recreate();
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
inflater.inflate(R.menu.settings_theme, menu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@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) {
|
||||||
|
super.onBackPressed();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
@ -1,221 +0,0 @@
|
|||||||
package apps.amine.bou.readerforselfoss.settings
|
|
||||||
|
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.text.*
|
|
||||||
import androidx.preference.EditTextPreference
|
|
||||||
import androidx.preference.PreferenceManager
|
|
||||||
import android.view.*
|
|
||||||
import android.widget.Toast
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.core.widget.addTextChangedListener
|
|
||||||
import androidx.preference.Preference
|
|
||||||
import androidx.preference.PreferenceFragmentCompat
|
|
||||||
import apps.amine.bou.readerforselfoss.R
|
|
||||||
import apps.amine.bou.readerforselfoss.databinding.ActivitySettingsBinding
|
|
||||||
import apps.amine.bou.readerforselfoss.themes.Toppings
|
|
||||||
import apps.amine.bou.readerforselfoss.utils.Config
|
|
||||||
import com.ftinc.scoop.Scoop
|
|
||||||
import java.lang.NumberFormatException
|
|
||||||
|
|
||||||
private const val TITLE_TAG = "settingsActivityTitle"
|
|
||||||
|
|
||||||
class SettingsActivity : AppCompatActivity(),
|
|
||||||
PreferenceFragmentCompat.OnPreferenceStartFragmentCallback {
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
if (PreferenceManager.getDefaultSharedPreferences(this).getBoolean("dark_theme", false)) {
|
|
||||||
setTheme(R.style.NoBarDark)
|
|
||||||
}
|
|
||||||
val binding = ActivitySettingsBinding.inflate(layoutInflater)
|
|
||||||
|
|
||||||
val scoop = Scoop.getInstance()
|
|
||||||
scoop.bind(this, Toppings.PRIMARY.value, binding.toolbar)
|
|
||||||
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
|
|
||||||
|
|
||||||
setContentView(binding.root)
|
|
||||||
if (savedInstanceState == null) {
|
|
||||||
supportFragmentManager
|
|
||||||
.beginTransaction()
|
|
||||||
.replace(R.id.settings, MainPreferenceFragment())
|
|
||||||
.commit()
|
|
||||||
} else {
|
|
||||||
title = savedInstanceState.getCharSequence(TITLE_TAG)
|
|
||||||
}
|
|
||||||
supportFragmentManager.addOnBackStackChangedListener {
|
|
||||||
if (supportFragmentManager.backStackEntryCount == 0) {
|
|
||||||
setTitle(R.string.title_activity_settings)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
setSupportActionBar(binding.toolbar)
|
|
||||||
|
|
||||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
|
||||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
|
||||||
supportActionBar?.title = title
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSaveInstanceState(outState: Bundle) {
|
|
||||||
super.onSaveInstanceState(outState)
|
|
||||||
// Save current activity title so we can set it again after a configuration change
|
|
||||||
outState.putCharSequence(TITLE_TAG, title)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onSupportNavigateUp(): Boolean {
|
|
||||||
if (supportFragmentManager.popBackStackImmediate()) {
|
|
||||||
supportActionBar?.title = getText(R.string.title_activity_settings)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return super.onSupportNavigateUp()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onPreferenceStartFragment(
|
|
||||||
caller: PreferenceFragmentCompat,
|
|
||||||
pref: Preference
|
|
||||||
): Boolean {
|
|
||||||
// Instantiate the new Fragment
|
|
||||||
val args = pref.extras
|
|
||||||
val fragment = supportFragmentManager.fragmentFactory.instantiate(
|
|
||||||
classLoader,
|
|
||||||
pref.fragment
|
|
||||||
).apply {
|
|
||||||
arguments = args
|
|
||||||
setTargetFragment(caller, 0)
|
|
||||||
}
|
|
||||||
// Replace the existing Fragment with the new Fragment
|
|
||||||
supportFragmentManager.beginTransaction()
|
|
||||||
.replace(R.id.settings, fragment)
|
|
||||||
.addToBackStack(null)
|
|
||||||
.commit()
|
|
||||||
title = pref.title
|
|
||||||
supportActionBar?.title = title
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
class MainPreferenceFragment : PreferenceFragmentCompat() {
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
|
||||||
setPreferencesFromResource(R.xml.pref_main, rootKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class GeneralPreferenceFragment : PreferenceFragmentCompat() {
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
|
||||||
setPreferencesFromResource(R.xml.pref_general, rootKey)
|
|
||||||
|
|
||||||
val editTextPreference = preferenceManager.findPreference<EditTextPreference>("prefer_api_items_number")
|
|
||||||
editTextPreference?.setOnBindEditTextListener { editText ->
|
|
||||||
editText.inputType = InputType.TYPE_CLASS_NUMBER
|
|
||||||
editText.filters = arrayOf(
|
|
||||||
InputFilter { source, _, _, dest, _, _ ->
|
|
||||||
try {
|
|
||||||
val input: Int = (dest.toString() + source.toString()).toInt()
|
|
||||||
if (input in 1..200) return@InputFilter null
|
|
||||||
} catch (nfe: NumberFormatException) {
|
|
||||||
Toast.makeText(activity, R.string.items_number_should_be_number, Toast.LENGTH_LONG).show()
|
|
||||||
}
|
|
||||||
""
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ArticleViewerPreferenceFragment : PreferenceFragmentCompat() {
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
|
||||||
setPreferencesFromResource(R.xml.pref_viewer, rootKey)
|
|
||||||
|
|
||||||
val fontSize = preferenceManager.findPreference<EditTextPreference>("reader_font_size")
|
|
||||||
fontSize?.setOnBindEditTextListener { editText ->
|
|
||||||
editText.inputType = InputType.TYPE_CLASS_NUMBER
|
|
||||||
editText.addTextChangedListener { object : TextWatcher {
|
|
||||||
override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
|
||||||
override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) {}
|
|
||||||
override fun afterTextChanged(editable: Editable) {
|
|
||||||
try {
|
|
||||||
editText.textSize = editable.toString().toInt().toFloat()
|
|
||||||
} catch (e: NumberFormatException) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} }
|
|
||||||
editText.filters = arrayOf(
|
|
||||||
InputFilter { source, _, _, dest, _, _ ->
|
|
||||||
try {
|
|
||||||
val input = (dest.toString() + source.toString()).toInt()
|
|
||||||
if (input > 0) return@InputFilter null
|
|
||||||
} catch (nfe: NumberFormatException) {
|
|
||||||
}
|
|
||||||
""
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class OfflinePreferenceFragment : PreferenceFragmentCompat() {
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
|
||||||
setPreferencesFromResource(R.xml.pref_offline, rootKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ThemePreferenceFragment : PreferenceFragmentCompat() {
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
|
||||||
setPreferencesFromResource(R.xml.pref_theme, rootKey)
|
|
||||||
setHasOptionsMenu(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
|
||||||
super.onCreateOptionsMenu(menu, inflater)
|
|
||||||
inflater.inflate(R.menu.settings_theme, menu)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
||||||
val id = item.itemId
|
|
||||||
if (id == R.id.clear) {
|
|
||||||
val pref = PreferenceManager.getDefaultSharedPreferences(activity)
|
|
||||||
val editor = pref.edit()
|
|
||||||
editor.remove("color_primary")
|
|
||||||
editor.remove("color_primary_dark")
|
|
||||||
editor.remove("color_accent")
|
|
||||||
editor.remove("color_accent_dark")
|
|
||||||
editor.remove("dark_theme")
|
|
||||||
editor.apply()
|
|
||||||
requireActivity().recreate()
|
|
||||||
}
|
|
||||||
return super.onOptionsItemSelected(item)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class LinksPreferenceFragment : PreferenceFragmentCompat() {
|
|
||||||
private fun openUrl(uri: Uri?) {
|
|
||||||
val browserIntent = Intent(Intent.ACTION_VIEW, uri)
|
|
||||||
startActivity(browserIntent)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
|
||||||
setPreferencesFromResource(R.xml.pref_links, rootKey)
|
|
||||||
|
|
||||||
preferenceManager.findPreference<Preference>("trackerLink")?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
|
||||||
openUrl(Uri.parse(Config.trackerUrl))
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
preferenceManager.findPreference<Preference>("sourceLink")?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
|
||||||
openUrl(Uri.parse(Config.sourceUrl))
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
preferenceManager.findPreference<Preference>("translation")?.onPreferenceClickListener = Preference.OnPreferenceClickListener {
|
|
||||||
openUrl(Uri.parse(Config.translationUrl))
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class ExperimentalPreferenceFragment : PreferenceFragmentCompat() {
|
|
||||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
|
||||||
setPreferencesFromResource(R.xml.pref_experimental, rootKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +1,14 @@
|
|||||||
package apps.amine.bou.readerforselfoss.themes
|
package apps.amine.bou.readerforselfoss.themes
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.preference.PreferenceManager
|
||||||
import androidx.annotation.ColorInt
|
import androidx.annotation.ColorInt
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.appcompat.view.ContextThemeWrapper
|
||||||
|
import android.util.TypedValue
|
||||||
import apps.amine.bou.readerforselfoss.R
|
import apps.amine.bou.readerforselfoss.R
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
|
||||||
class AppColors(a: Activity) {
|
class AppColors(a: Activity) {
|
||||||
|
|
||||||
@ -11,8 +16,8 @@ class AppColors(a: Activity) {
|
|||||||
@ColorInt val colorPrimaryDark: Int
|
@ColorInt val colorPrimaryDark: Int
|
||||||
@ColorInt val colorAccent: Int
|
@ColorInt val colorAccent: Int
|
||||||
@ColorInt val colorAccentDark: Int
|
@ColorInt val colorAccentDark: Int
|
||||||
|
@ColorInt val cardBackgroundColor: Int
|
||||||
@ColorInt val colorBackground: Int
|
@ColorInt val colorBackground: Int
|
||||||
@ColorInt val textColor: Int
|
|
||||||
val isDarkTheme: Boolean
|
val isDarkTheme: Boolean
|
||||||
|
|
||||||
init {
|
init {
|
||||||
@ -49,13 +54,16 @@ class AppColors(a: Activity) {
|
|||||||
R.color.darkBackground
|
R.color.darkBackground
|
||||||
} else {
|
} else {
|
||||||
a.setTheme(R.style.NoBar)
|
a.setTheme(R.style.NoBar)
|
||||||
R.color.grey_50
|
android.R.color.background_light
|
||||||
}
|
}
|
||||||
|
|
||||||
textColor = if (isDarkTheme) {
|
val wrapper = Context::class.java
|
||||||
R.color.white
|
val method = wrapper!!.getMethod("getThemeResId")
|
||||||
} else {
|
method.isAccessible = true
|
||||||
R.color.grey_900
|
|
||||||
}
|
val typedCardBackground = TypedValue()
|
||||||
|
a.theme.resolveAttribute(R.attr.cardBackgroundColor, typedCardBackground, true)
|
||||||
|
|
||||||
|
cardBackgroundColor = typedCardBackground.data
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
package apps.amine.bou.readerforselfoss.transformers
|
||||||
|
|
||||||
|
import androidx.viewpager.widget.ViewPager
|
||||||
|
import android.view.View
|
||||||
|
|
||||||
|
class DepthPageTransformer : ViewPager.PageTransformer {
|
||||||
|
|
||||||
|
override fun transformPage(view: View, position: Float) {
|
||||||
|
val pageWidth = view.width
|
||||||
|
|
||||||
|
when {
|
||||||
|
position < -1 -> // [-Infinity,-1)
|
||||||
|
// This page is way off-screen to the left.
|
||||||
|
view.alpha = 0F
|
||||||
|
position <= 0 -> { // [-1,0]
|
||||||
|
// Use the default slide transition when moving to the left page
|
||||||
|
view.alpha = 1F
|
||||||
|
view.translationX = 0F
|
||||||
|
view.scaleX = 1F
|
||||||
|
view.scaleY = 1F
|
||||||
|
}
|
||||||
|
position <= 1 -> { // (0,1]
|
||||||
|
// Fade the page out.
|
||||||
|
view.alpha = 1 - position
|
||||||
|
|
||||||
|
// Counteract the default slide transition
|
||||||
|
view.translationX = pageWidth * -position
|
||||||
|
|
||||||
|
// Scale the page down (between MIN_SCALE and 1)
|
||||||
|
val scaleFactor = MIN_SCALE + (1 - MIN_SCALE) * (1 - Math.abs(position))
|
||||||
|
view.scaleX = scaleFactor
|
||||||
|
view.scaleY = scaleFactor
|
||||||
|
}
|
||||||
|
else -> // (1,+Infinity]
|
||||||
|
// This page is way off-screen to the right.
|
||||||
|
view.alpha = 0F
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val MIN_SCALE = 0.75f
|
||||||
|
}
|
||||||
|
}
|
@ -13,7 +13,7 @@ fun String.longHash(): Long {
|
|||||||
val chars = this.toCharArray()
|
val chars = this.toCharArray()
|
||||||
|
|
||||||
for (i in 0 until l) {
|
for (i in 0 until l) {
|
||||||
h = 31 * h + chars[i].code.toLong()
|
h = 31 * h + chars[i].toLong()
|
||||||
}
|
}
|
||||||
return h
|
return h
|
||||||
}
|
}
|
||||||
|
@ -4,27 +4,26 @@ import android.app.Activity
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.SharedPreferences
|
import android.content.SharedPreferences
|
||||||
import androidx.preference.PreferenceManager
|
|
||||||
import apps.amine.bou.readerforselfoss.LoginActivity
|
import apps.amine.bou.readerforselfoss.LoginActivity
|
||||||
|
|
||||||
class Config(c: Context) {
|
class Config(c: Context) {
|
||||||
|
|
||||||
val settings: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(c)
|
val settings: SharedPreferences = c.getSharedPreferences(settingsName, Context.MODE_PRIVATE)
|
||||||
|
|
||||||
val baseUrl: String
|
val baseUrl: String
|
||||||
get() = settings.getString("url", "")!!
|
get() = settings.getString("url", "")
|
||||||
|
|
||||||
val userLogin: String
|
val userLogin: String
|
||||||
get() = settings.getString("login", "")!!
|
get() = settings.getString("login", "")
|
||||||
|
|
||||||
val userPassword: String
|
val userPassword: String
|
||||||
get() = settings.getString("password", "")!!
|
get() = settings.getString("password", "")
|
||||||
|
|
||||||
val httpUserLogin: String
|
val httpUserLogin: String
|
||||||
get() = settings.getString("httpUserName", "")!!
|
get() = settings.getString("httpUserName", "")
|
||||||
|
|
||||||
val httpUserPassword: String
|
val httpUserPassword: String
|
||||||
get() = settings.getString("httpPassword", "")!!
|
get() = settings.getString("httpPassword", "")
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val settingsName = "paramsselfoss"
|
const val settingsName = "paramsselfoss"
|
||||||
@ -41,17 +40,16 @@ class Config(c: Context) {
|
|||||||
|
|
||||||
const val newItemsChannelId = "new-items-channel-id"
|
const val newItemsChannelId = "new-items-channel-id"
|
||||||
|
|
||||||
var apiVersion = 0
|
|
||||||
|
|
||||||
/* Execute logout and clear all settings to default */
|
|
||||||
fun logoutAndRedirect(
|
fun logoutAndRedirect(
|
||||||
c: Context,
|
c: Context,
|
||||||
callingActivity: Activity,
|
callingActivity: Activity,
|
||||||
editor: SharedPreferences.Editor,
|
editor: SharedPreferences.Editor,
|
||||||
baseUrlFail: Boolean = false
|
baseUrlFail: Boolean = false
|
||||||
): Boolean {
|
): Boolean {
|
||||||
val settings = PreferenceManager.getDefaultSharedPreferences(c)
|
editor.remove("url")
|
||||||
settings.edit().clear().commit()
|
editor.remove("login")
|
||||||
|
editor.remove("password")
|
||||||
|
editor.apply()
|
||||||
val intent = Intent(c, LoginActivity::class.java)
|
val intent = Intent(c, LoginActivity::class.java)
|
||||||
if (baseUrlFail) {
|
if (baseUrlFail) {
|
||||||
intent.putExtra("baseUrlFail", baseUrlFail)
|
intent.putExtra("baseUrlFail", baseUrlFail)
|
||||||
|
@ -1,31 +0,0 @@
|
|||||||
package apps.amine.bou.readerforselfoss.utils
|
|
||||||
|
|
||||||
import android.text.format.DateUtils
|
|
||||||
import java.time.Instant
|
|
||||||
import java.time.LocalDateTime
|
|
||||||
import java.time.OffsetDateTime
|
|
||||||
import java.time.ZoneOffset
|
|
||||||
import java.time.format.DateTimeFormatter
|
|
||||||
|
|
||||||
fun parseDate(dateString: String): Instant {
|
|
||||||
|
|
||||||
val FORMATTERV1 = "yyyy-MM-dd HH:mm:ss"
|
|
||||||
|
|
||||||
return if (Config.apiVersion >= 4) {
|
|
||||||
OffsetDateTime.parse(dateString).toInstant()
|
|
||||||
} else {
|
|
||||||
LocalDateTime.parse(dateString, DateTimeFormatter.ofPattern(FORMATTERV1)).toInstant(ZoneOffset.UTC)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun parseRelativeDate(dateString: String): String {
|
|
||||||
|
|
||||||
val date = parseDate(dateString)
|
|
||||||
|
|
||||||
return " " + DateUtils.getRelativeTimeSpanString(
|
|
||||||
date.toEpochMilli(),
|
|
||||||
Instant.now().toEpochMilli(),
|
|
||||||
DateUtils.MINUTE_IN_MILLIS,
|
|
||||||
DateUtils.FORMAT_ABBREV_RELATIVE
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,12 +1,16 @@
|
|||||||
package apps.amine.bou.readerforselfoss.utils
|
package apps.amine.bou.readerforselfoss.utils
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
|
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 apps.amine.bou.readerforselfoss.api.selfoss.SelfossTagType
|
||||||
|
import java.text.ParseException
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
fun String.toTextDrawableString(c: Context): String {
|
fun String.toTextDrawableString(c: Context): String {
|
||||||
val textDrawable = StringBuilder()
|
val textDrawable = StringBuilder()
|
||||||
for (s in this.split(" ".toRegex()).filter { it.isNotEmpty() }.toTypedArray()) {
|
for (s in this.split(" ".toRegex()).filter { !it.isEmpty() }.toTypedArray()) {
|
||||||
try {
|
try {
|
||||||
textDrawable.append(s[0])
|
textDrawable.append(s[0])
|
||||||
} catch (e: StringIndexOutOfBoundsException) {
|
} catch (e: StringIndexOutOfBoundsException) {
|
||||||
@ -16,9 +20,19 @@ fun String.toTextDrawableString(c: Context): String {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun Item.sourceAndDateText(): String {
|
fun Item.sourceAndDateText(): String {
|
||||||
val formattedDate = parseRelativeDate(this.datetime)
|
val formattedDate: String = try {
|
||||||
|
" " + DateUtils.getRelativeTimeSpanString(
|
||||||
|
SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(this.datetime).time,
|
||||||
|
Date().time,
|
||||||
|
DateUtils.MINUTE_IN_MILLIS,
|
||||||
|
DateUtils.FORMAT_ABBREV_RELATIVE
|
||||||
|
)
|
||||||
|
} catch (e: ParseException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
""
|
||||||
|
}
|
||||||
|
|
||||||
return this.getSourceTitle() + formattedDate
|
return this.sourcetitle + formattedDate
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Item.toggleStar(): Item {
|
fun Item.toggleStar(): Item {
|
||||||
|
@ -7,7 +7,6 @@ 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.os.Build
|
|
||||||
import android.text.Spannable
|
import android.text.Spannable
|
||||||
import android.text.style.ClickableSpan
|
import android.text.style.ClickableSpan
|
||||||
import androidx.browser.customtabs.CustomTabsIntent
|
import androidx.browser.customtabs.CustomTabsIntent
|
||||||
@ -21,23 +20,17 @@ 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 okhttp3.HttpUrl.Companion.toHttpUrlOrNull
|
|
||||||
|
|
||||||
fun Context.buildCustomTabsIntent(): CustomTabsIntent {
|
fun Context.buildCustomTabsIntent(): CustomTabsIntent {
|
||||||
|
|
||||||
val actionIntent = Intent(Intent.ACTION_SEND)
|
val actionIntent = Intent(Intent.ACTION_SEND)
|
||||||
actionIntent.type = "text/plain"
|
actionIntent.type = "text/plain"
|
||||||
val pflags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
||||||
PendingIntent.FLAG_IMMUTABLE
|
|
||||||
} else {
|
|
||||||
0
|
|
||||||
}
|
|
||||||
val createPendingShareIntent: PendingIntent = PendingIntent.getActivity(
|
val createPendingShareIntent: PendingIntent = PendingIntent.getActivity(
|
||||||
this,
|
this,
|
||||||
0,
|
0,
|
||||||
actionIntent,
|
actionIntent,
|
||||||
pflags
|
0
|
||||||
)
|
)
|
||||||
|
|
||||||
val intentBuilder = CustomTabsIntent.Builder()
|
val intentBuilder = CustomTabsIntent.Builder()
|
||||||
|
|
||||||
@ -81,35 +74,23 @@ fun Context.openItemUrlInternally(
|
|||||||
) {
|
) {
|
||||||
if (articleViewer) {
|
if (articleViewer) {
|
||||||
ReaderActivity.allItems = allItems
|
ReaderActivity.allItems = allItems
|
||||||
SharedItems.position = currentItem
|
|
||||||
val intent = Intent(this, ReaderActivity::class.java)
|
val intent = Intent(this, ReaderActivity::class.java)
|
||||||
intent.putExtra("currentItem", currentItem)
|
intent.putExtra("currentItem", currentItem)
|
||||||
app.startActivity(intent)
|
app.startActivity(intent)
|
||||||
} else {
|
} else {
|
||||||
this.openItemUrlInternalBrowser(
|
try {
|
||||||
linkDecoded,
|
CustomTabActivityHelper.openCustomTab(
|
||||||
customTabsIntent,
|
|
||||||
app)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun Context.openItemUrlInternalBrowser(
|
|
||||||
linkDecoded: String,
|
|
||||||
customTabsIntent: CustomTabsIntent,
|
|
||||||
app: Activity
|
|
||||||
) {
|
|
||||||
try {
|
|
||||||
CustomTabActivityHelper.openCustomTab(
|
|
||||||
app,
|
app,
|
||||||
customTabsIntent,
|
customTabsIntent,
|
||||||
Uri.parse(linkDecoded)
|
Uri.parse(linkDecoded)
|
||||||
) { _, uri ->
|
) { _, uri ->
|
||||||
val intent = Intent(Intent.ACTION_VIEW, uri)
|
val intent = Intent(Intent.ACTION_VIEW, uri)
|
||||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
openInBrowser(linkDecoded, app)
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
|
||||||
openInBrowser(linkDecoded, app)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +113,7 @@ fun Context.openItemUrl(
|
|||||||
} else {
|
} else {
|
||||||
if (!internalBrowser) {
|
if (!internalBrowser) {
|
||||||
openInBrowser(linkDecoded, app)
|
openInBrowser(linkDecoded, app)
|
||||||
} else if (articleViewer) {
|
} else {
|
||||||
this.openItemUrlInternally(
|
this.openItemUrlInternally(
|
||||||
allItems,
|
allItems,
|
||||||
currentItem,
|
currentItem,
|
||||||
@ -141,12 +122,6 @@ fun Context.openItemUrl(
|
|||||||
articleViewer,
|
articleViewer,
|
||||||
app
|
app
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
this.openItemUrlInternalBrowser(
|
|
||||||
linkDecoded,
|
|
||||||
customTabsIntent,
|
|
||||||
app
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -162,13 +137,13 @@ private fun openInBrowser(linkDecoded: String, app: Activity) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun String.isUrlValid(): Boolean =
|
fun String.isUrlValid(): Boolean =
|
||||||
this.toHttpUrlOrNull() != null && Patterns.WEB_URL.matcher(this).matches()
|
HttpUrl.parse(this) != null && Patterns.WEB_URL.matcher(this).matches()
|
||||||
|
|
||||||
fun String.isBaseUrlValid(ctx: Context): Boolean {
|
fun String.isBaseUrlValid(ctx: Context): Boolean {
|
||||||
val baseUrl = this.toHttpUrlOrNull()
|
val baseUrl = HttpUrl.parse(this)
|
||||||
var existsAndEndsWithSlash = false
|
var existsAndEndsWithSlash = false
|
||||||
if (baseUrl != null) {
|
if (baseUrl != null) {
|
||||||
val pathSegments = baseUrl.pathSegments
|
val pathSegments = baseUrl.pathSegments()
|
||||||
existsAndEndsWithSlash = "" == pathSegments[pathSegments.size - 1]
|
existsAndEndsWithSlash = "" == pathSegments[pathSegments.size - 1]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,404 +0,0 @@
|
|||||||
package apps.amine.bou.readerforselfoss.utils
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.widget.Toast
|
|
||||||
import apps.amine.bou.readerforselfoss.R
|
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
|
||||||
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
|
||||||
import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity
|
|
||||||
import apps.amine.bou.readerforselfoss.utils.persistence.toEntity
|
|
||||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
|
||||||
import apps.amine.bou.readerforselfoss.utils.persistence.toView
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import retrofit2.Call
|
|
||||||
import retrofit2.Callback
|
|
||||||
import retrofit2.Response
|
|
||||||
import java.text.SimpleDateFormat
|
|
||||||
import kotlin.concurrent.thread
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Singleton class that contains the articles fetched from Selfoss, it allows sharing the items list
|
|
||||||
* between Activities and Fragments
|
|
||||||
*/
|
|
||||||
object SharedItems {
|
|
||||||
var items: ArrayList<Item> = arrayListOf<Item>()
|
|
||||||
get() {
|
|
||||||
return ArrayList(field)
|
|
||||||
}
|
|
||||||
set(value) {
|
|
||||||
field = ArrayList(value)
|
|
||||||
}
|
|
||||||
var focusedItems: ArrayList<Item> = arrayListOf<Item>()
|
|
||||||
get() {
|
|
||||||
return ArrayList(field)
|
|
||||||
}
|
|
||||||
set(value) {
|
|
||||||
field = ArrayList(value)
|
|
||||||
}
|
|
||||||
var position = 0
|
|
||||||
set(value) {
|
|
||||||
field = when {
|
|
||||||
value < 0 -> 0
|
|
||||||
value > items.size -> items.size
|
|
||||||
else -> value
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var displayedItems: String = "unread"
|
|
||||||
set(value) {
|
|
||||||
field = when (value) {
|
|
||||||
"all" -> "all"
|
|
||||||
"unread" -> "unread"
|
|
||||||
"read" -> "read"
|
|
||||||
"starred" -> "starred"
|
|
||||||
else -> "all"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var searchFilter: String? = null
|
|
||||||
var sourceIDFilter: Long? = null
|
|
||||||
var sourceFilter: String? = null
|
|
||||||
var tagFilter: String? = null
|
|
||||||
var itemsCaching = false
|
|
||||||
|
|
||||||
var fetchedUnread = false
|
|
||||||
var fetchedAll = false
|
|
||||||
var fetchedStarred = false
|
|
||||||
|
|
||||||
var badgeUnread = -1
|
|
||||||
var badgeAll = -1
|
|
||||||
var badgeStarred = -1
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add new items to the SharedItems list
|
|
||||||
*
|
|
||||||
* The new items are considered more updated than the ones already in the list.
|
|
||||||
* The old items present in the new list are discarded and replaced by the new ones.
|
|
||||||
* Items are compared according to the selfoss id, which should always be unique.
|
|
||||||
*/
|
|
||||||
fun appendNewItems(newItems: ArrayList<Item>) {
|
|
||||||
var tmpItems = items
|
|
||||||
if (tmpItems != newItems) {
|
|
||||||
tmpItems = tmpItems.filter { item -> newItems.find { it.id == item.id } == null } as ArrayList<Item>
|
|
||||||
tmpItems.addAll(newItems)
|
|
||||||
items = tmpItems
|
|
||||||
|
|
||||||
sortItems()
|
|
||||||
getFocusedItems()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun refreshFocusedItems(newItems: ArrayList<Item>) {
|
|
||||||
val tmpItems = items
|
|
||||||
tmpItems.removeAll(focusedItems)
|
|
||||||
|
|
||||||
appendNewItems(newItems)
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun clearDBItems(db: AppDatabase) {
|
|
||||||
db.itemsDao().deleteAllItems()
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun updateDatabase(db: AppDatabase) {
|
|
||||||
if (itemsCaching) {
|
|
||||||
if (items.isEmpty()) {
|
|
||||||
getFromDB(db)
|
|
||||||
}
|
|
||||||
db.itemsDao().deleteAllItems()
|
|
||||||
db.itemsDao().insertAllItems(*(items.map { it.toEntity() }).toTypedArray())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun filter() {
|
|
||||||
fun filterSearch(item: Item): Boolean {
|
|
||||||
return if (!searchFilter.isEmptyOrNullOrNullString()) {
|
|
||||||
var matched = item.title.contains(searchFilter.toString(), true)
|
|
||||||
matched = matched || item.content.contains(searchFilter.toString(), true)
|
|
||||||
matched = matched || item.sourcetitle.contains(searchFilter.toString(), true)
|
|
||||||
matched
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var tmpItems = focusedItems
|
|
||||||
if (tagFilter != null) {
|
|
||||||
tmpItems = tmpItems.filter { it.tags.tags.contains(tagFilter.toString()) } as ArrayList<Item>
|
|
||||||
}
|
|
||||||
if (searchFilter != null) {
|
|
||||||
tmpItems = tmpItems.filter { filterSearch(it) } as ArrayList<Item>
|
|
||||||
}
|
|
||||||
if (sourceFilter != null) {
|
|
||||||
tmpItems = tmpItems.filter { it.sourcetitle == sourceFilter } as ArrayList<Item>
|
|
||||||
}
|
|
||||||
focusedItems = tmpItems
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getFocusedItems() {
|
|
||||||
when (displayedItems) {
|
|
||||||
"all" -> getAll()
|
|
||||||
"unread" -> getUnRead()
|
|
||||||
"read" -> getRead()
|
|
||||||
"starred" -> getStarred()
|
|
||||||
else -> getUnRead()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getUnRead() {
|
|
||||||
displayedItems = "unread"
|
|
||||||
focusedItems = items.filter { item -> item.unread } as ArrayList<Item>
|
|
||||||
filter()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getRead() {
|
|
||||||
displayedItems = "read"
|
|
||||||
focusedItems = items.filter { item -> !item.unread } as ArrayList<Item>
|
|
||||||
filter()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getStarred() {
|
|
||||||
displayedItems = "starred"
|
|
||||||
focusedItems = items.filter { item -> item.starred } as ArrayList<Item>
|
|
||||||
filter()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getAll() {
|
|
||||||
displayedItems = "all"
|
|
||||||
focusedItems = items
|
|
||||||
filter()
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun getFromDB(db: AppDatabase) {
|
|
||||||
if (itemsCaching) {
|
|
||||||
val dbItems = db.itemsDao().items().map { it.toView() } as ArrayList<Item>
|
|
||||||
appendNewItems(dbItems)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun removeItemAtIndex(index: Int) {
|
|
||||||
val i = focusedItems[index]
|
|
||||||
val tmpItems = focusedItems
|
|
||||||
tmpItems.remove(i)
|
|
||||||
focusedItems = tmpItems
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addItemAtIndex(newItem: Item, index: Int) {
|
|
||||||
val tmpItems = focusedItems
|
|
||||||
tmpItems.add(index, newItem)
|
|
||||||
focusedItems = tmpItems
|
|
||||||
}
|
|
||||||
|
|
||||||
fun readItem(app: Context, api: SelfossApi, db: AppDatabase, item: Item) {
|
|
||||||
if (items.contains(item)) {
|
|
||||||
position = items.indexOf(item)
|
|
||||||
readItemAtPosition(app, api, db)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun readItems(db: AppDatabase, ids: List<String>) {
|
|
||||||
for (id in ids) {
|
|
||||||
val match = items.filter { it -> it.id == id }
|
|
||||||
if (match.isNotEmpty() && match.size == 1) {
|
|
||||||
position = items.indexOf(match[0])
|
|
||||||
val tmpItems = items
|
|
||||||
tmpItems[position].unread = false
|
|
||||||
items = tmpItems
|
|
||||||
resetDBItem(db)
|
|
||||||
badgeUnread--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun readItemAtPosition(app: Context, api: SelfossApi, db: AppDatabase) {
|
|
||||||
val i = items[position]
|
|
||||||
|
|
||||||
if (app.isNetworkAccessible(null)) {
|
|
||||||
api.markItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
|
||||||
override fun onResponse(
|
|
||||||
call: Call<SuccessResponse>,
|
|
||||||
response: Response<SuccessResponse>
|
|
||||||
) {
|
|
||||||
|
|
||||||
val tmpItems = items
|
|
||||||
tmpItems[position].unread = false
|
|
||||||
items = tmpItems
|
|
||||||
|
|
||||||
resetDBItem(db)
|
|
||||||
getFocusedItems()
|
|
||||||
badgeUnread--
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
|
||||||
Toast.makeText(
|
|
||||||
app,
|
|
||||||
app.getString(R.string.cant_mark_read),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else if (itemsCaching) {
|
|
||||||
thread {
|
|
||||||
db.actionsDao().insertAllActions(ActionEntity(i.id, true, false, false, false))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (position > items.size) {
|
|
||||||
position -= 1
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun unreadItem(app: Context, api: SelfossApi, db: AppDatabase, item: Item) {
|
|
||||||
if (items.contains(item) && !item.unread) {
|
|
||||||
position = items.indexOf(item)
|
|
||||||
unreadItemAtPosition(app, api, db)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun unreadItemAtPosition(app: Context, api: SelfossApi, db: AppDatabase) {
|
|
||||||
val i = items[position]
|
|
||||||
|
|
||||||
if (app.isNetworkAccessible(null)) {
|
|
||||||
api.unmarkItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
|
||||||
override fun onResponse(
|
|
||||||
call: Call<SuccessResponse>,
|
|
||||||
response: Response<SuccessResponse>
|
|
||||||
) {
|
|
||||||
|
|
||||||
val tmpItems = items
|
|
||||||
tmpItems[position].unread = true
|
|
||||||
items = tmpItems
|
|
||||||
|
|
||||||
resetDBItem(db)
|
|
||||||
getFocusedItems()
|
|
||||||
badgeUnread++
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
|
||||||
Toast.makeText(
|
|
||||||
app,
|
|
||||||
app.getString(R.string.cant_mark_unread),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else if (itemsCaching) {
|
|
||||||
thread {
|
|
||||||
db.actionsDao().insertAllActions(ActionEntity(i.id, false, true, false, false))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun starItem(app: Context, api: SelfossApi, db: AppDatabase, item: Item) {
|
|
||||||
if (items.contains(item) && !item.starred) {
|
|
||||||
position = items.indexOf(item)
|
|
||||||
starItemAtPosition(app, api, db)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun starItemAtPosition(app: Context, api: SelfossApi, db: AppDatabase) {
|
|
||||||
val i = items[position]
|
|
||||||
|
|
||||||
if (app.isNetworkAccessible(null)) {
|
|
||||||
api.starrItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
|
||||||
override fun onResponse(
|
|
||||||
call: Call<SuccessResponse>,
|
|
||||||
response: Response<SuccessResponse>
|
|
||||||
) {
|
|
||||||
val tmpItems = items
|
|
||||||
tmpItems[position].starred = true
|
|
||||||
items = tmpItems
|
|
||||||
|
|
||||||
resetDBItem(db)
|
|
||||||
getFocusedItems()
|
|
||||||
badgeStarred++
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(
|
|
||||||
call: Call<SuccessResponse>,
|
|
||||||
t: Throwable
|
|
||||||
) {
|
|
||||||
Toast.makeText(
|
|
||||||
app,
|
|
||||||
app.getString(R.string.cant_mark_favortie),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
thread {
|
|
||||||
db.actionsDao().insertAllActions(ActionEntity(i.id, false, false, true, false))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun unstarItem(app: Context, api: SelfossApi, db: AppDatabase, item: Item) {
|
|
||||||
if (items.contains(item) && item.starred) {
|
|
||||||
position = items.indexOf(item)
|
|
||||||
unstarItemAtPosition(app, api, db)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun unstarItemAtPosition(app: Context, api: SelfossApi, db: AppDatabase) {
|
|
||||||
val i = items[position]
|
|
||||||
|
|
||||||
if (app.isNetworkAccessible(null)) {
|
|
||||||
api.unstarrItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
|
||||||
override fun onResponse(
|
|
||||||
call: Call<SuccessResponse>,
|
|
||||||
response: Response<SuccessResponse>
|
|
||||||
) {
|
|
||||||
val tmpItems = items
|
|
||||||
tmpItems[position].starred = false
|
|
||||||
items = tmpItems
|
|
||||||
|
|
||||||
resetDBItem(db)
|
|
||||||
getFocusedItems()
|
|
||||||
badgeStarred--
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onFailure(
|
|
||||||
call: Call<SuccessResponse>,
|
|
||||||
t: Throwable
|
|
||||||
) {
|
|
||||||
Toast.makeText(
|
|
||||||
app,
|
|
||||||
app.getString(R.string.cant_unmark_favortie),
|
|
||||||
Toast.LENGTH_SHORT
|
|
||||||
).show()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
thread {
|
|
||||||
db.actionsDao().insertAllActions(ActionEntity(i.id, false, false, false, true))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun resetDBItem(db: AppDatabase) {
|
|
||||||
if (itemsCaching) {
|
|
||||||
val i = items[position]
|
|
||||||
CoroutineScope(Dispatchers.IO).launch {
|
|
||||||
db.itemsDao().delete(i.toEntity())
|
|
||||||
db.itemsDao().insertAllItems(i.toEntity())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun unreadItemStatusAtIndex(position: Int): Boolean {
|
|
||||||
return focusedItems[position].unread
|
|
||||||
}
|
|
||||||
|
|
||||||
fun computeBadges() {
|
|
||||||
badgeUnread = items.filter { item -> item.unread }.size
|
|
||||||
badgeStarred = items.filter { item -> item.starred }.size
|
|
||||||
badgeAll = items.size
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun sortItems() {
|
|
||||||
val tmpItems = ArrayList(items.sortedByDescending { parseDate(it.datetime) })
|
|
||||||
items = tmpItems
|
|
||||||
}
|
|
||||||
}
|
|
@ -3,8 +3,7 @@ package apps.amine.bou.readerforselfoss.utils.network
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
import android.net.NetworkCapabilities
|
import android.net.NetworkInfo
|
||||||
import android.os.Build
|
|
||||||
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
|
||||||
@ -15,7 +14,9 @@ var view: View? = null
|
|||||||
lateinit var s: Snackbar
|
lateinit var s: Snackbar
|
||||||
|
|
||||||
fun Context.isNetworkAccessible(v: View?, overrideOffline: Boolean = false): Boolean {
|
fun Context.isNetworkAccessible(v: View?, overrideOffline: Boolean = false): Boolean {
|
||||||
val networkIsAccessible = isNetworkAvailable(this)
|
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)) {
|
if (v != null && (!networkIsAccessible || overrideOffline) && (!snackBarShown || v != view)) {
|
||||||
view = v
|
view = v
|
||||||
@ -41,24 +42,4 @@ fun Context.isNetworkAccessible(v: View?, overrideOffline: Boolean = false): Boo
|
|||||||
s.dismiss()
|
s.dismiss()
|
||||||
}
|
}
|
||||||
return if(overrideOffline) overrideOffline else networkIsAccessible
|
return if(overrideOffline) overrideOffline else networkIsAccessible
|
||||||
}
|
|
||||||
|
|
||||||
fun isNetworkAvailable(context: Context): Boolean {
|
|
||||||
val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
||||||
val network = connectivityManager.activeNetwork ?: return false
|
|
||||||
val networkCapabilities = connectivityManager.getNetworkCapabilities(network) ?: return false
|
|
||||||
|
|
||||||
return when {
|
|
||||||
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> true
|
|
||||||
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
|
|
||||||
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
|
|
||||||
networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
val network = connectivityManager.activeNetworkInfo ?: return false
|
|
||||||
return network.isConnectedOrConnecting
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -28,7 +28,7 @@ fun SourceEntity.toView(): Source =
|
|||||||
fun Source.toEntity(): SourceEntity =
|
fun Source.toEntity(): SourceEntity =
|
||||||
SourceEntity(
|
SourceEntity(
|
||||||
this.id,
|
this.id,
|
||||||
this.getTitleDecoded(),
|
this.title,
|
||||||
this.tags.tags,
|
this.tags.tags,
|
||||||
this.spout,
|
this.spout,
|
||||||
this.error,
|
this.error,
|
||||||
@ -68,6 +68,6 @@ fun Item.toEntity(): ItemEntity =
|
|||||||
this.thumbnail,
|
this.thumbnail,
|
||||||
this.icon,
|
this.icon,
|
||||||
this.link,
|
this.link,
|
||||||
this.getSourceTitle(),
|
this.sourcetitle,
|
||||||
this.tags.tags
|
this.tags.tags
|
||||||
)
|
)
|
@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<item android:state_selected="true"
|
|
||||||
android:color="@color/red"/>
|
|
||||||
|
|
||||||
<item android:state_selected="false"
|
|
||||||
android:color="?android:attr/textColorPrimary" />
|
|
||||||
</selector>
|
|
@ -1,5 +0,0 @@
|
|||||||
<vector android:height="24dp" android:tint="#FFFFFF"
|
|
||||||
android:viewportHeight="24" android:viewportWidth="24"
|
|
||||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<path android:fillColor="@android:color/white" android:pathData="M12,4.5C7,4.5 2.73,7.61 1,12c1.73,4.39 6,7.5 11,7.5s9.27,-3.11 11,-7.5c-1.73,-4.39 -6,-7.5 -11,-7.5zM12,17c-2.76,0 -5,-2.24 -5,-5s2.24,-5 5,-5 5,2.24 5,5 -2.24,5 -5,5zM12,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3 3,-1.34 3,-3 -1.34,-3 -3,-3z"/>
|
|
||||||
</vector>
|
|
5
app/src/main/res/drawable/ic_fiber_new_white_24dp.xml
Normal file
5
app/src/main/res/drawable/ic_fiber_new_white_24dp.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="M20,4L4,4c-1.11,0 -1.99,0.89 -1.99,2L2,18c0,1.11 0.89,2 2,2h16c1.11,0 2,-0.89 2,-2L22,6c0,-1.11 -0.89,-2 -2,-2zM8.5,15L7.3,15l-2.55,-3.5L4.75,15L3.5,15L3.5,9h1.25l2.5,3.5L7.25,9L8.5,9v6zM13.5,10.26L11,10.26v1.12h2.5v1.26L11,12.64v1.11h2.5L13.5,15h-4L9.5,9h4v1.26zM20.5,14c0,0.55 -0.45,1 -1,1h-4c-0.55,0 -1,-0.45 -1,-1L14.5,9h1.25v4.51h1.13L16.88,9.99h1.25v3.51h1.12L19.25,9h1.25v5z"/>
|
||||||
|
</vector>
|
5
app/src/main/res/drawable/ic_info_white_24dp.xml
Normal file
5
app/src/main/res/drawable/ic_info_white_24dp.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM13,17h-2v-6h2v6zM13,9h-2L11,7h2v2z"/>
|
||||||
|
</vector>
|
@ -1,5 +0,0 @@
|
|||||||
<vector android:height="54.751434dp" android:viewportHeight="18.756023"
|
|
||||||
android:viewportWidth="20.554007" android:width="60dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
|
||||||
<path android:fillColor="#FFFFFFFF"
|
|
||||||
android:pathData="m5.7968,14.6109c-2.7907,-2.7367 -4.4957,-4.7131 -5.018,-5.8165 -2.102,-4.4408 0.2424,-8.7943 4.7357,-8.7943 1.635,0 2.7056,0.425 3.9688,1.5755l0.7937,0.723 0.7937,-0.723c1.2631,-1.1505 2.3337,-1.5755 3.9688,-1.5755 4.4933,0 6.8377,4.3535 4.7357,8.7943 -0.5223,1.1035 -2.2274,3.0799 -5.018,5.8165 -2.3248,2.2798 -4.3409,4.1451 -4.4802,4.1451 -0.1393,0 -2.1554,-1.8653 -4.4802,-4.1451z" android:strokeWidth="0.0933392"/>
|
|
||||||
</vector>
|
|
9
app/src/main/res/drawable/ic_search_black_24dp.xml
Normal file
9
app/src/main/res/drawable/ic_search_black_24dp.xml
Normal file
@ -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.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
|
||||||
|
</vector>
|
5
app/src/main/res/drawable/ic_settings_white_24dp.xml
Normal file
5
app/src/main/res/drawable/ic_settings_white_24dp.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"/>
|
||||||
|
</vector>
|
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="M23.64,7c-0.45,-0.34 -4.93,-4 -11.64,-4 -1.5,0 -2.89,0.19 -4.15,0.48L18.18,13.8 23.64,7zM17.04,15.22L3.27,1.44 2,2.72l2.05,2.06C1.91,5.76 0.59,6.82 0.36,7l11.63,14.49 0.01,0.01 0.01,-0.01 3.9,-4.86 3.32,3.32 1.27,-1.27 -3.46,-3.46z"/>
|
||||||
|
</vector>
|
5
app/src/main/res/drawable/ic_widgets_white_24dp.xml
Normal file
5
app/src/main/res/drawable/ic_widgets_white_24dp.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||||
|
android:viewportHeight="24.0" android:viewportWidth="24.0"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="#FF000000" android:pathData="M13,13v8h8v-8h-8zM3,21h8v-8L3,13v8zM3,3v8h8L11,3L3,3zM16.66,1.69L11,7.34 16.66,13l5.66,-5.66 -5.66,-5.65z"/>
|
||||||
|
</vector>
|
@ -7,7 +7,7 @@
|
|||||||
tools:context="apps.amine.bou.readerforselfoss.AddSourceActivity">
|
tools:context="apps.amine.bou.readerforselfoss.AddSourceActivity">
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
@ -55,8 +55,7 @@
|
|||||||
android:layout_marginRight="16dp"
|
android:layout_marginRight="16dp"
|
||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
android:layout_marginLeft="16dp"
|
android:layout_marginLeft="16dp"/>
|
||||||
android:gravity="center_horizontal" />
|
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -68,9 +67,7 @@
|
|||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
android:inputType="text"
|
android:inputType="text"
|
||||||
android:hint="@string/add_source_hint_name"
|
android:hint="@string/add_source_hint_name"/>
|
||||||
android:textColorHint="?android:textColorPrimary"
|
|
||||||
android:autofillHints="false" />
|
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -79,12 +76,10 @@
|
|||||||
android:ems="10"
|
android:ems="10"
|
||||||
android:id="@+id/sourceUri"
|
android:id="@+id/sourceUri"
|
||||||
android:hint="@string/add_source_hint_url"
|
android:hint="@string/add_source_hint_url"
|
||||||
android:textColorHint="?android:textColorPrimary"
|
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/nameInput"
|
app:layout_constraintTop_toBottomOf="@+id/nameInput"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"/>
|
||||||
android:autofillHints="false" />
|
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -96,9 +91,7 @@
|
|||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/sourceUri"
|
app:layout_constraintTop_toBottomOf="@+id/sourceUri"
|
||||||
android:hint="@string/add_source_hint_tags"
|
android:hint="@string/add_source_hint_tags"
|
||||||
android:textColorHint="?android:textColorPrimary"
|
android:inputType="text"/>
|
||||||
android:inputType="text"
|
|
||||||
android:autofillHints="false" />
|
|
||||||
|
|
||||||
<Spinner
|
<Spinner
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -107,8 +100,7 @@
|
|||||||
app:layout_constraintTop_toBottomOf="@+id/tags"
|
app:layout_constraintTop_toBottomOf="@+id/tags"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
android:layout_height="40dp"
|
android:layout_height="40dp"/>
|
||||||
android:theme="@style/App.Spinner"/>
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:text="@string/add_source_save"
|
android:text="@string/add_source_save"
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.drawerlayout.widget.DrawerLayout
|
<RelativeLayout
|
||||||
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:id="@+id/drawerContainer"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:context="apps.amine.bou.readerforselfoss.HomeActivity"
|
tools:context="apps.amine.bou.readerforselfoss.HomeActivity"
|
||||||
@ -37,43 +36,51 @@
|
|||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
<FrameLayout
|
||||||
android:id="@+id/swipeRefreshLayout"
|
android:id="@+id/drawer_layout"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
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">
|
||||||
|
|
||||||
<LinearLayout
|
<androidx.swiperefreshlayout.widget.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">
|
||||||
android:orientation="vertical"
|
|
||||||
android:background="?android:attr/windowBackground">
|
|
||||||
|
|
||||||
<TextView
|
<LinearLayout
|
||||||
android:id="@+id/emptyText"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:gravity="center_horizontal"
|
android:orientation="vertical"
|
||||||
android:paddingTop="100dp"
|
android:background="?android:attr/windowBackground">
|
||||||
android:text="@string/nothing_here"
|
|
||||||
android:textAlignment="center"
|
|
||||||
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
|
|
||||||
android:background="@android:color/transparent"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<TextView
|
||||||
android:id="@+id/recyclerView"
|
android:id="@+id/emptyText"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:background="@android:color/transparent"
|
android:gravity="fill"
|
||||||
android:clipToPadding="false"
|
android:paddingTop="100dp"
|
||||||
android:paddingBottom="60dp"
|
android:text="@string/nothing_here"
|
||||||
android:scrollbars="vertical"
|
android:textAlignment="center"
|
||||||
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
|
||||||
tools:listitem="@layout/list_item"/>
|
android:background="@android:color/transparent"
|
||||||
</LinearLayout>
|
android:visibility="gone" />
|
||||||
|
|
||||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/recyclerView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@android:color/transparent"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:paddingBottom="60dp"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"
|
||||||
|
tools:listitem="@layout/list_item"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
@ -83,11 +90,4 @@
|
|||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="60dp"/>
|
android:layout_height="60dp"/>
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
</RelativeLayout>
|
||||||
<com.mikepenz.materialdrawer.widget.MaterialDrawerSliderView
|
|
||||||
android:id="@+id/mainDrawer"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:layout_gravity="start"
|
|
||||||
android:fitsSystemWindows="true" />
|
|
||||||
</androidx.drawerlayout.widget.DrawerLayout>
|
|
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<androidx.viewpager2.widget.ViewPager2
|
<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"
|
||||||
|
@ -45,69 +45,98 @@
|
|||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<EditText
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/urlView"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:hint="@string/prompt_url"
|
android:id="@+id/urlLayout"
|
||||||
android:imeOptions="actionUnspecified"
|
>
|
||||||
android:importantForAutofill="no"
|
|
||||||
android:inputType="textUri"
|
|
||||||
android:maxLines="1" />
|
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
<EditText
|
||||||
|
android:id="@+id/urlView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/prompt_url"
|
||||||
|
android:imeOptions="actionUnspecified"
|
||||||
|
android:inputType="textUri"
|
||||||
|
android:maxLines="1" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<Switch
|
||||||
android:text="@string/withLoginSwitch"
|
android:text="@string/withLoginSwitch"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="0dp"
|
||||||
android:id="@+id/withLogin"
|
android:id="@+id/withLogin"
|
||||||
android:layout_weight="1"/>
|
android:layout_weight="1"/>
|
||||||
|
|
||||||
<EditText
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/loginView"
|
android:id="@+id/loginLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:autofillHints="username"
|
android:visibility="gone">
|
||||||
android:hint="@string/prompt_login"
|
|
||||||
android:inputType="text"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<EditText
|
<AutoCompleteTextView
|
||||||
android:id="@+id/passwordView"
|
android:id="@+id/loginView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/prompt_login"
|
||||||
|
android:inputType="text"
|
||||||
|
android:maxLines="1" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/passwordLayout"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:autofillHints="password"
|
android:visibility="gone">
|
||||||
android:hint="@string/prompt_password"
|
|
||||||
android:inputType="textPassword"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
<EditText
|
||||||
|
android:id="@+id/passwordView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/prompt_password"
|
||||||
|
android:inputType="textPassword"
|
||||||
|
android:maxLines="1" />
|
||||||
|
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<Switch
|
||||||
android:id="@+id/withHttpLogin"
|
android:id="@+id/withHttpLogin"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:text="@string/withHttpLoginSwitch" />
|
android:text="@string/withHttpLoginSwitch" />
|
||||||
|
|
||||||
<EditText
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
android:id="@+id/httpLoginView"
|
android:id="@+id/httpLoginInput"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:autofillHints="username"
|
android:visibility="gone">
|
||||||
android:hint="@string/prompt_http_login"
|
|
||||||
android:inputType="text"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/httpPasswordView"
|
android:inputType="text"
|
||||||
|
android:id="@+id/httpLoginView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/prompt_http_login" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<com.google.android.material.textfield.TextInputLayout
|
||||||
|
android:id="@+id/httpPasswordInput"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="match_parent"
|
||||||
android:autofillHints="password"
|
android:visibility="gone">
|
||||||
android:hint="@string/prompt_http_password"
|
|
||||||
android:inputType="textPassword"
|
|
||||||
android:visibility="gone" />
|
|
||||||
|
|
||||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
<EditText
|
||||||
|
android:id="@+id/httpPasswordView"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/prompt_http_password"
|
||||||
|
android:inputType="textPassword" />
|
||||||
|
</com.google.android.material.textfield.TextInputLayout>
|
||||||
|
|
||||||
|
<Switch
|
||||||
android:id="@+id/withSelfhostedCert"
|
android:id="@+id/withSelfhostedCert"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@ -126,8 +155,8 @@
|
|||||||
style="?android:textAppearanceSmall"
|
style="?android:textAppearanceSmall"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginTop="16dp"
|
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
android:text="@string/action_sign_in"
|
android:text="@string/action_sign_in"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
<androidx.viewpager2.widget.ViewPager2
|
<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"
|
||||||
@ -33,7 +33,7 @@
|
|||||||
app:layout_constraintTop_toBottomOf="@+id/appBarLayout" />
|
app:layout_constraintTop_toBottomOf="@+id/appBarLayout" />
|
||||||
|
|
||||||
|
|
||||||
<me.relex.circleindicator.CircleIndicator3
|
<me.relex.circleindicator.CircleIndicator
|
||||||
android:id="@+id/indicator"
|
android:id="@+id/indicator"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="20dp"
|
android:layout_height="20dp"
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:id="@+id/layout"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<com.google.android.material.appbar.AppBarLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<androidx.appcompat.widget.Toolbar
|
|
||||||
android:id="@+id/toolbar"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="?attr/actionBarSize"
|
|
||||||
android:theme="@style/ToolBarStyle" />
|
|
||||||
</com.google.android.material.appbar.AppBarLayout>
|
|
||||||
|
|
||||||
<FrameLayout
|
|
||||||
android:id="@+id/settings"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent" />
|
|
||||||
</LinearLayout>
|
|
@ -31,13 +31,14 @@
|
|||||||
android:id="@+id/fab"
|
android:id="@+id/fab"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="end|bottom|end"
|
android:layout_gravity="end|bottom|right"
|
||||||
app:srcCompat="@drawable/ic_add_white_24dp"
|
android:src="@drawable/ic_add_white_24dp"
|
||||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||||
android:paddingTop="@dimen/activity_vertical_margin"
|
android:paddingTop="@dimen/activity_vertical_margin"
|
||||||
android:layout_alignParentBottom="true"
|
android:layout_alignParentBottom="true"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
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:contentDescription="@string/add_source" />
|
android:layout_marginRight="16dp"/>
|
||||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
@ -16,8 +16,7 @@
|
|||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
card_view:cardElevation="2dp"
|
card_view:cardElevation="2dp"
|
||||||
card_view:cardUseCompatPadding="true"
|
card_view:cardUseCompatPadding="true"
|
||||||
card_view:layout_constraintBottom_toBottomOf="parent"
|
card_view:layout_constraintBottom_toBottomOf="parent">
|
||||||
app:cardBackgroundColor="?cardBackgroundColor">
|
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@ -66,7 +65,6 @@
|
|||||||
android:gravity="start"
|
android:gravity="start"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:textColor="?android:textColorPrimary"
|
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
app:layout_constraintLeft_toRightOf="@+id/sourceImage"
|
app:layout_constraintLeft_toRightOf="@+id/sourceImage"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
@ -81,7 +79,6 @@
|
|||||||
android:gravity="start"
|
android:gravity="start"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:textColor="?android:textColorPrimary"
|
|
||||||
app:layout_constraintLeft_toLeftOf="@+id/title"
|
app:layout_constraintLeft_toLeftOf="@+id/title"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/title"
|
app:layout_constraintTop_toBottomOf="@+id/title"
|
||||||
tools:text="Google Actualité Il y a 5h" />
|
tools:text="Google Actualité Il y a 5h" />
|
||||||
@ -95,22 +92,20 @@
|
|||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
app:layout_constraintTop_toBottomOf="@+id/sourceTitleAndDate">
|
app:layout_constraintTop_toBottomOf="@+id/sourceTitleAndDate">
|
||||||
|
|
||||||
<ImageButton
|
<com.like.LikeButton
|
||||||
android:id="@+id/favButton"
|
android:id="@+id/favButton"
|
||||||
android:layout_width="35dp"
|
android:layout_width="35dp"
|
||||||
android:layout_height="35dp"
|
android:layout_height="35dp"
|
||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
android:layout_alignParentRight="true"
|
android:layout_alignParentRight="true"
|
||||||
|
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
android:layout_marginEnd="8dp"
|
android:layout_marginEnd="8dp"
|
||||||
android:layout_marginRight="8dp"
|
android:layout_marginRight="8dp"
|
||||||
android:adjustViewBounds="true"
|
|
||||||
android:background="@android:color/transparent"
|
|
||||||
android:elevation="5dp"
|
android:elevation="5dp"
|
||||||
android:padding="4dp"
|
android:padding="4dp"
|
||||||
android:scaleType="centerCrop"
|
app:icon_size="22dp"
|
||||||
app:srcCompat="@drawable/ic_menu_heart_60dp"
|
app:icon_type="heart" />
|
||||||
app:tint="@color/ic_menu_heart_color" />
|
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/shareBtn"
|
android:id="@+id/shareBtn"
|
||||||
@ -126,8 +121,8 @@
|
|||||||
android:elevation="5dp"
|
android:elevation="5dp"
|
||||||
android:padding="4dp"
|
android:padding="4dp"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
app:srcCompat="@drawable/ic_share_black_24dp"
|
android:src="@drawable/ic_share_black_24dp"
|
||||||
app:tint="?android:attr/textColorPrimary" />
|
android:tint="?android:attr/textColorPrimary" />
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/browserBtn"
|
android:id="@+id/browserBtn"
|
||||||
@ -143,8 +138,8 @@
|
|||||||
android:elevation="5dp"
|
android:elevation="5dp"
|
||||||
android:padding="4dp"
|
android:padding="4dp"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
app:srcCompat="@drawable/ic_open_in_browser_black_24dp"
|
android:src="@drawable/ic_open_in_browser_black_24dp"
|
||||||
app:tint="?android:attr/textColorPrimary" />
|
android:tint="?android:attr/textColorPrimary" />
|
||||||
|
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
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"
|
||||||
android:background="?android:colorBackground"
|
|
||||||
android:descendantFocusability="blocksDescendants">
|
android:descendantFocusability="blocksDescendants">
|
||||||
|
|
||||||
<androidx.core.widget.NestedScrollView
|
<androidx.core.widget.NestedScrollView
|
||||||
@ -34,7 +33,7 @@
|
|||||||
android:layout_marginLeft="16dp"
|
android:layout_marginLeft="16dp"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:layout_marginRight="16dp"
|
android:layout_marginRight="16dp"
|
||||||
android:textColor="?android:textColorSecondary"
|
android:textAppearance="@style/TextAppearance.AppCompat.Small"
|
||||||
android:textSize="12sp"
|
android:textSize="12sp"
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
@ -66,7 +65,6 @@
|
|||||||
android:layout_marginRight="16dp"
|
android:layout_marginRight="16dp"
|
||||||
android:layout_marginTop="24dp"
|
android:layout_marginTop="24dp"
|
||||||
android:paddingBottom="48dp"
|
android:paddingBottom="48dp"
|
||||||
android:background="?android:colorBackground"
|
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
@ -12,8 +12,7 @@
|
|||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginTop="21dp"
|
android:layout_marginTop="21dp"
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
android:layout_marginLeft="8dp" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/title"
|
android:id="@+id/title"
|
||||||
@ -30,14 +29,11 @@
|
|||||||
android:textAllCaps="false"
|
android:textAllCaps="false"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
android:textColor="?android:textColorPrimary"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
app:layout_constraintStart_toEndOf="@+id/itemImage"
|
app:layout_constraintStart_toEndOf="@+id/itemImage"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="Titre"
|
tools:text="Titre" />
|
||||||
android:layout_marginLeft="8dp"
|
|
||||||
android:layout_marginRight="16dp" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/sourceTitleAndDate"
|
android:id="@+id/sourceTitleAndDate"
|
||||||
@ -50,13 +46,10 @@
|
|||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:textAlignment="viewStart"
|
android:textAlignment="viewStart"
|
||||||
android:textSize="14sp"
|
android:textSize="14sp"
|
||||||
android:textColor="?android:textColorPrimary"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
app:layout_constraintHorizontal_bias="0.0"
|
||||||
app:layout_constraintStart_toEndOf="@+id/itemImage"
|
app:layout_constraintStart_toEndOf="@+id/itemImage"
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="Google Actualité Il y a 5h"
|
tools:text="Google Actualité Il y a 5h" />
|
||||||
android:layout_marginLeft="8dp"
|
|
||||||
android:layout_marginRight="16dp" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
15
app/src/main/res/layout/settings_toolbar.xml
Normal file
15
app/src/main/res/layout/settings_toolbar.xml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<androidx.appcompat.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize"
|
||||||
|
app:theme="@style/ToolBarStyle"
|
||||||
|
app:popupTheme="?attr/toolbarPopupTheme" />
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
@ -1,50 +1,55 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.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:orientation="vertical"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="48dp"
|
android:layout_height="48dp">
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
|
|
||||||
<ImageView
|
<ImageView
|
||||||
android:id="@+id/itemImage"
|
android:id="@+id/itemImage"
|
||||||
android:layout_width="36dp"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
android:layout_height="36dp"
|
|
||||||
android:importantForAccessibility="no"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
android:layout_marginLeft="16dp"
|
||||||
|
android:layout_width="36dp"
|
||||||
|
android:layout_height="36dp"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/sourceTitle"
|
android:layout_width="wrap_content"
|
||||||
android:layout_width="0dp"
|
|
||||||
android:layout_height="17dp"
|
android:layout_height="17dp"
|
||||||
android:layout_marginStart="8dp"
|
android:id="@+id/sourceTitle"
|
||||||
android:ellipsize="end"
|
|
||||||
android:gravity="start"
|
|
||||||
android:maxLines="1"
|
|
||||||
android:textAlignment="textStart"
|
|
||||||
android:textSize="13sp"
|
|
||||||
android:textColor="?android:textColorPrimary"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toStartOf="@+id/deleteBtn"
|
|
||||||
app:layout_constraintStart_toEndOf="@+id/itemImage"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
tools:text="source title" />
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintLeft_toRightOf="@+id/itemImage"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginLeft="16dp"
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:textAlignment="viewStart"
|
||||||
|
tools:text="source title "
|
||||||
|
android:gravity="start" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/deleteBtn"
|
android:id="@+id/deleteBtn"
|
||||||
style="@style/Widget.AppCompat.Button.Borderless"
|
|
||||||
android:layout_width="34dp"
|
|
||||||
android:layout_height="34dp"
|
|
||||||
android:layout_marginEnd="8dp"
|
|
||||||
android:background="@drawable/ic_remove_circle_outline_black_24dp"
|
android:background="@drawable/ic_remove_circle_outline_black_24dp"
|
||||||
|
style="@style/Widget.AppCompat.Button.Borderless"
|
||||||
android:backgroundTint="?android:textColorSecondary"
|
android:backgroundTint="?android:textColorSecondary"
|
||||||
android:elevation="4dp"
|
android:elevation="4dp"
|
||||||
android:contentDescription="@string/remove_source"
|
android:layout_marginTop="16dp"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
android:layout_marginBottom="16dp"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
android:layout_marginEnd="16dp"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
android:layout_marginRight="16dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
app:layout_constraintLeft_toRightOf="@+id/sourceTitle"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
app:layout_constraintHorizontal_bias="1.0"
|
||||||
|
android:layout_width="34dp"
|
||||||
|
android:layout_height="34dp"/>
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -15,8 +15,14 @@
|
|||||||
android:title="@string/reader_text_align_justify" />
|
android:title="@string/reader_text_align_justify" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/star"
|
android:id="@+id/unsave"
|
||||||
android:icon="@drawable/ic_menu_heart_60dp"
|
android:icon="@drawable/heart_on"
|
||||||
|
android:title="@string/remove_to_favs_reader"
|
||||||
|
android:visible="true"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
<item
|
||||||
|
android:id="@+id/save"
|
||||||
|
android:icon="@drawable/heart_off"
|
||||||
android:title="@string/add_to_favs_reader"
|
android:title="@string/add_to_favs_reader"
|
||||||
android:visible="true"
|
android:visible="true"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="ifRoom" />
|
||||||
|
@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/unread_action"
|
android:id="@+id/unread_action"
|
||||||
android:icon="@drawable/ic_baseline_white_eye_24dp"
|
android:icon="@drawable/ic_fiber_new_white_24dp"
|
||||||
android:title="@string/unmark"
|
android:title="@string/unmark"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
@ -17,6 +17,7 @@
|
|||||||
<item
|
<item
|
||||||
android:id="@+id/open_action"
|
android:id="@+id/open_action"
|
||||||
android:icon="@drawable/ic_open_in_browser_white_24dp"
|
android:icon="@drawable/ic_open_in_browser_white_24dp"
|
||||||
|
android:iconTint="@color/white"
|
||||||
android:title="@string/reader_action_open"
|
android:title="@string/reader_action_open"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
|
@ -139,8 +139,6 @@
|
|||||||
<string name="pref_switch_items_caching_off">Els articles no es guardaran a la memòria del dispositiu i l\'aplicació no es podrà utilitzar sense connexió.</string>
|
<string name="pref_switch_items_caching_off">Els articles no es guardaran a la memòria del dispositiu i l\'aplicació no es podrà utilitzar sense connexió.</string>
|
||||||
<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_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="pref_switch_items_caching">Guarda els elements per utilitzar-los sense connexió</string>
|
||||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
|
||||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
|
||||||
<string name="no_network_connectivity">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">Sincronitza els articles</string>
|
||||||
<string name="pref_switch_periodic_refresh_off">Els articles no se sincronitzaran en segon pla</string>
|
<string name="pref_switch_periodic_refresh_off">Els articles no se sincronitzaran en segon pla</string>
|
||||||
@ -162,8 +160,4 @@
|
|||||||
<string name="reader_text_align_left">Align left</string>
|
<string name="reader_text_align_left">Align left</string>
|
||||||
<string name="reader_text_align_justify">Justify</string>
|
<string name="reader_text_align_justify">Justify</string>
|
||||||
<string name="settings_reader_font">Reader font</string>
|
<string name="settings_reader_font">Reader font</string>
|
||||||
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
|
||||||
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
|
||||||
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
|
||||||
<string name="remove_source">Remove source</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -139,8 +139,6 @@
|
|||||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
|
||||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
|
||||||
<string name="no_network_connectivity">Nicht verbunden !</string>
|
<string name="no_network_connectivity">Nicht verbunden !</string>
|
||||||
<string name="pref_switch_periodic_refresh">Synchronisiere Artikel</string>
|
<string name="pref_switch_periodic_refresh">Synchronisiere Artikel</string>
|
||||||
<string name="pref_switch_periodic_refresh_off">Artikel werden nicht im Hintergrund synchronisiert</string>
|
<string name="pref_switch_periodic_refresh_off">Artikel werden nicht im Hintergrund synchronisiert</string>
|
||||||
@ -162,8 +160,4 @@
|
|||||||
<string name="reader_text_align_left">Align left</string>
|
<string name="reader_text_align_left">Align left</string>
|
||||||
<string name="reader_text_align_justify">Justify</string>
|
<string name="reader_text_align_justify">Justify</string>
|
||||||
<string name="settings_reader_font">Reader font</string>
|
<string name="settings_reader_font">Reader font</string>
|
||||||
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
|
||||||
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
|
||||||
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
|
||||||
<string name="remove_source">Remove source</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -139,8 +139,6 @@
|
|||||||
<string name="pref_switch_items_caching_off">Los artículos no se guardarán en la memoria del dispositivo y la aplicación no se podrá utilizar sin conexión.</string>
|
<string name="pref_switch_items_caching_off">Los artículos no se guardarán en la memoria del dispositivo y la aplicación no se podrá utilizar sin conexión.</string>
|
||||||
<string name="pref_switch_items_caching_on">Los artículos se guardarán en la memoria del dispositivo y se utilizarán para el uso sin conexión.</string>
|
<string name="pref_switch_items_caching_on">Los artículos se guardarán en la memoria del dispositivo y se utilizarán para el uso sin conexión.</string>
|
||||||
<string name="pref_switch_items_caching">Guardar elementos para uso sin conexión</string>
|
<string name="pref_switch_items_caching">Guardar elementos para uso sin conexión</string>
|
||||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
|
||||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
|
||||||
<string name="no_network_connectivity">Sin conexión!</string>
|
<string name="no_network_connectivity">Sin conexión!</string>
|
||||||
<string name="pref_switch_periodic_refresh">Sincronizar artículos</string>
|
<string name="pref_switch_periodic_refresh">Sincronizar artículos</string>
|
||||||
<string name="pref_switch_periodic_refresh_off">Los artículos no se sincronizarán en segundo plano</string>
|
<string name="pref_switch_periodic_refresh_off">Los artículos no se sincronizarán en segundo plano</string>
|
||||||
@ -162,8 +160,4 @@
|
|||||||
<string name="reader_text_align_left">Alinear a la izquierda</string>
|
<string name="reader_text_align_left">Alinear a la izquierda</string>
|
||||||
<string name="reader_text_align_justify">Justificado</string>
|
<string name="reader_text_align_justify">Justificado</string>
|
||||||
<string name="settings_reader_font">Modo lectura</string>
|
<string name="settings_reader_font">Modo lectura</string>
|
||||||
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
|
||||||
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
|
||||||
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
|
||||||
<string name="remove_source">Remove source</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -139,8 +139,6 @@
|
|||||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
|
||||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
|
||||||
<string name="no_network_connectivity">Not connected !</string>
|
<string name="no_network_connectivity">Not connected !</string>
|
||||||
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||||
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||||
@ -162,8 +160,4 @@
|
|||||||
<string name="reader_text_align_left">Align left</string>
|
<string name="reader_text_align_left">Align left</string>
|
||||||
<string name="reader_text_align_justify">Justify</string>
|
<string name="reader_text_align_justify">Justify</string>
|
||||||
<string name="settings_reader_font">Reader font</string>
|
<string name="settings_reader_font">Reader font</string>
|
||||||
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
|
||||||
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
|
||||||
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
|
||||||
<string name="remove_source">Remove source</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -139,8 +139,6 @@
|
|||||||
<string name="pref_switch_items_caching_off">Les articles ne seront pas enregistrés et l\'application ne sera pas utilisable hors ligne.</string>
|
<string name="pref_switch_items_caching_off">Les articles ne seront pas enregistrés et l\'application ne sera pas utilisable hors ligne.</string>
|
||||||
<string name="pref_switch_items_caching_on">Les articles seront enregistrés et l\'application sera utilisable hors ligne.</string>
|
<string name="pref_switch_items_caching_on">Les articles seront enregistrés et l\'application sera utilisable hors ligne.</string>
|
||||||
<string name="pref_switch_items_caching">Sauvegarder les articles pour une utilisation hors ligne</string>
|
<string name="pref_switch_items_caching">Sauvegarder les articles pour une utilisation hors ligne</string>
|
||||||
<string name="pref_switch_update_sources">Vérifier les nouvelles sources et tags</string>
|
|
||||||
<string name="pref_switch_update_sources_summary">Désactivez cette option si votre serveur reçoit trop de requêtes.</string>
|
|
||||||
<string name="no_network_connectivity">Hors connexion !</string>
|
<string name="no_network_connectivity">Hors connexion !</string>
|
||||||
<string name="pref_switch_periodic_refresh">Synchroniser les articles</string>
|
<string name="pref_switch_periodic_refresh">Synchroniser les articles</string>
|
||||||
<string name="pref_switch_periodic_refresh_off">Les articles ne seront pas synchronisés en arrière plan</string>
|
<string name="pref_switch_periodic_refresh_off">Les articles ne seront pas synchronisés en arrière plan</string>
|
||||||
@ -162,8 +160,4 @@
|
|||||||
<string name="reader_text_align_left">Aligner à gauche</string>
|
<string name="reader_text_align_left">Aligner à gauche</string>
|
||||||
<string name="reader_text_align_justify">Justifier le texte</string>
|
<string name="reader_text_align_justify">Justifier le texte</string>
|
||||||
<string name="settings_reader_font">Police du lecteur d\'articles</string>
|
<string name="settings_reader_font">Police du lecteur d\'articles</string>
|
||||||
<string name="reader_static_bar_title">Barre statique pour le visionneur d\'articles</string>
|
|
||||||
<string name="reader_static_bar_on">La barre sera affichée</string>
|
|
||||||
<string name="reader_static_bar_off">La barre sera affichée grâce au bouton</string>
|
|
||||||
<string name="remove_source">Remove source</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||||
<string name="app_name">"Lector para selfoss"</string>
|
<string name="app_name">"Lector para Selfoss"</string>
|
||||||
<string name="title_activity_login">"Conectar"</string>
|
<string name="title_activity_login">"Acceder"</string>
|
||||||
<string name="prompt_password">"Contrasinal"</string>
|
<string name="prompt_password">"Contrasinal"</string>
|
||||||
<string name="prompt_http_password">"Contrasinal HTTP"</string>
|
<string name="prompt_http_password">"Contrasinal HTTP"</string>
|
||||||
<string name="action_sign_in">"Ir"</string>
|
<string name="action_sign_in">"Ir"</string>
|
||||||
@ -9,7 +9,7 @@
|
|||||||
<string name="error_field_required">"Campo requirido"</string>
|
<string name="error_field_required">"Campo requirido"</string>
|
||||||
<string name="prompt_url">"URL"</string>
|
<string name="prompt_url">"URL"</string>
|
||||||
<string name="withLoginSwitch">"É preciso iniciar sesión?"</string>
|
<string name="withLoginSwitch">"É preciso iniciar sesión?"</string>
|
||||||
<string name="withHttpLoginSwitch">"É preciso iniciar sesión?"</string>
|
<string name="withHttpLoginSwitch">"É preciso iniciar sesión HTTP?"</string>
|
||||||
<string name="login_url_problem">"Ups! Pode que precises engadir un \"/\" o final da URL."</string>
|
<string name="login_url_problem">"Ups! Pode que precises engadir un \"/\" o final da URL."</string>
|
||||||
<string name="prompt_login">"Nome de usuario"</string>
|
<string name="prompt_login">"Nome de usuario"</string>
|
||||||
<string name="prompt_http_login">"Nome de usuario HTTP"</string>
|
<string name="prompt_http_login">"Nome de usuario HTTP"</string>
|
||||||
@ -139,8 +139,6 @@
|
|||||||
<string name="pref_switch_items_caching_off">Os artigos non se gardaran na memoria do dispositivo e non se poderá utilizar a aplicación sen conexión.</string>
|
<string name="pref_switch_items_caching_off">Os artigos non se gardaran na memoria do dispositivo e non se poderá utilizar a aplicación sen conexión.</string>
|
||||||
<string name="pref_switch_items_caching_on">Os artigos gardaranse na memoria do dispositivo e estarán dispoñibles sen conexión.</string>
|
<string name="pref_switch_items_caching_on">Os artigos gardaranse na memoria do dispositivo e estarán dispoñibles sen conexión.</string>
|
||||||
<string name="pref_switch_items_caching">Gardar elementos para uso sen conexión</string>
|
<string name="pref_switch_items_caching">Gardar elementos para uso sen conexión</string>
|
||||||
<string name="pref_switch_update_sources">Comproba novas fontes e etiquetas</string>
|
|
||||||
<string name="pref_switch_update_sources_summary">Deshabilita isto se o teu servidor está recibindo demasiadas peticións de base de datos.</string>
|
|
||||||
<string name="no_network_connectivity">Non conectado!</string>
|
<string name="no_network_connectivity">Non conectado!</string>
|
||||||
<string name="pref_switch_periodic_refresh">Sincronizar artigos</string>
|
<string name="pref_switch_periodic_refresh">Sincronizar artigos</string>
|
||||||
<string name="pref_switch_periodic_refresh_off">Os artigos non se sincronizarán coa aplicación de fondo</string>
|
<string name="pref_switch_periodic_refresh_off">Os artigos non se sincronizarán coa aplicación de fondo</string>
|
||||||
@ -162,8 +160,4 @@
|
|||||||
<string name="reader_text_align_left">Aliñar á esquerda</string>
|
<string name="reader_text_align_left">Aliñar á esquerda</string>
|
||||||
<string name="reader_text_align_justify">Xustificado</string>
|
<string name="reader_text_align_justify">Xustificado</string>
|
||||||
<string name="settings_reader_font">Modo lector</string>
|
<string name="settings_reader_font">Modo lector</string>
|
||||||
<string name="reader_static_bar_title">Barra inferior estática na vista de artigos</string>
|
|
||||||
<string name="reader_static_bar_on">A barra inferior mostrarase sempre</string>
|
|
||||||
<string name="reader_static_bar_off">A barra inferior pode mostrarse a través do botón flotante</string>
|
|
||||||
<string name="remove_source">Eliminar fonte</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -139,8 +139,6 @@
|
|||||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
|
||||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
|
||||||
<string name="no_network_connectivity">Not connected !</string>
|
<string name="no_network_connectivity">Not connected !</string>
|
||||||
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||||
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||||
@ -162,8 +160,4 @@
|
|||||||
<string name="reader_text_align_left">Align left</string>
|
<string name="reader_text_align_left">Align left</string>
|
||||||
<string name="reader_text_align_justify">Justify</string>
|
<string name="reader_text_align_justify">Justify</string>
|
||||||
<string name="settings_reader_font">Reader font</string>
|
<string name="settings_reader_font">Reader font</string>
|
||||||
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
|
||||||
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
|
||||||
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
|
||||||
<string name="remove_source">Remove source</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -139,8 +139,6 @@
|
|||||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
|
||||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
|
||||||
<string name="no_network_connectivity">Not connected !</string>
|
<string name="no_network_connectivity">Not connected !</string>
|
||||||
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||||
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||||
@ -162,8 +160,4 @@
|
|||||||
<string name="reader_text_align_left">Align left</string>
|
<string name="reader_text_align_left">Align left</string>
|
||||||
<string name="reader_text_align_justify">Justify</string>
|
<string name="reader_text_align_justify">Justify</string>
|
||||||
<string name="settings_reader_font">Reader font</string>
|
<string name="settings_reader_font">Reader font</string>
|
||||||
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
|
||||||
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
|
||||||
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
|
||||||
<string name="remove_source">Remove source</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -139,8 +139,6 @@
|
|||||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
|
||||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
|
||||||
<string name="no_network_connectivity">Not connected !</string>
|
<string name="no_network_connectivity">Not connected !</string>
|
||||||
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||||
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||||
@ -162,8 +160,4 @@
|
|||||||
<string name="reader_text_align_left">Align left</string>
|
<string name="reader_text_align_left">Align left</string>
|
||||||
<string name="reader_text_align_justify">Justify</string>
|
<string name="reader_text_align_justify">Justify</string>
|
||||||
<string name="settings_reader_font">Reader font</string>
|
<string name="settings_reader_font">Reader font</string>
|
||||||
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
|
||||||
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
|
||||||
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
|
||||||
<string name="remove_source">Remove source</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -139,8 +139,6 @@
|
|||||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
|
||||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
|
||||||
<string name="no_network_connectivity">Not connected !</string>
|
<string name="no_network_connectivity">Not connected !</string>
|
||||||
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||||
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||||
@ -162,8 +160,4 @@
|
|||||||
<string name="reader_text_align_left">Align left</string>
|
<string name="reader_text_align_left">Align left</string>
|
||||||
<string name="reader_text_align_justify">Justify</string>
|
<string name="reader_text_align_justify">Justify</string>
|
||||||
<string name="settings_reader_font">Reader font</string>
|
<string name="settings_reader_font">Reader font</string>
|
||||||
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
|
||||||
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
|
||||||
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
|
||||||
<string name="remove_source">Remove source</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -139,8 +139,6 @@
|
|||||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
|
||||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
|
||||||
<string name="no_network_connectivity">Not connected !</string>
|
<string name="no_network_connectivity">Not connected !</string>
|
||||||
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||||
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||||
@ -162,8 +160,4 @@
|
|||||||
<string name="reader_text_align_left">Align left</string>
|
<string name="reader_text_align_left">Align left</string>
|
||||||
<string name="reader_text_align_justify">Justify</string>
|
<string name="reader_text_align_justify">Justify</string>
|
||||||
<string name="settings_reader_font">Reader font</string>
|
<string name="settings_reader_font">Reader font</string>
|
||||||
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
|
||||||
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
|
||||||
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
|
||||||
<string name="remove_source">Remove source</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -139,8 +139,6 @@
|
|||||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
|
||||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
|
||||||
<string name="no_network_connectivity">Not connected !</string>
|
<string name="no_network_connectivity">Not connected !</string>
|
||||||
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||||
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||||
@ -162,8 +160,4 @@
|
|||||||
<string name="reader_text_align_left">Align left</string>
|
<string name="reader_text_align_left">Align left</string>
|
||||||
<string name="reader_text_align_justify">Justify</string>
|
<string name="reader_text_align_justify">Justify</string>
|
||||||
<string name="settings_reader_font">Reader font</string>
|
<string name="settings_reader_font">Reader font</string>
|
||||||
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
|
||||||
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
|
||||||
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
|
||||||
<string name="remove_source">Remove source</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,169 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
|
||||||
<string name="app_name">"Reader for Selfoss"</string>
|
|
||||||
<string name="title_activity_login">"පිවිසෙන්න"</string>
|
|
||||||
<string name="prompt_password">"මුර පදය"</string>
|
|
||||||
<string name="prompt_http_password">"HTTP Password"</string>
|
|
||||||
<string name="action_sign_in">"Go"</string>
|
|
||||||
<string name="error_invalid_password">"Password not long enough"</string>
|
|
||||||
<string name="error_field_required">"Field required"</string>
|
|
||||||
<string name="prompt_url">"Url"</string>
|
|
||||||
<string name="withLoginSwitch">"Login required ?"</string>
|
|
||||||
<string name="withHttpLoginSwitch">"HTTP Login required ?"</string>
|
|
||||||
<string name="login_url_problem">"Oops. You may need to add a \"/\" at the end of the url."</string>
|
|
||||||
<string name="prompt_login">"පරිශීලක නාමය"</string>
|
|
||||||
<string name="prompt_http_login">"HTTP Username"</string>
|
|
||||||
<string name="label_share">"Share"</string>
|
|
||||||
<string name="readAll">"Read all"</string>
|
|
||||||
<string name="action_disconnect">"Disconnect"</string>
|
|
||||||
<string name="title_activity_settings">"සැකසුම්"</string>
|
|
||||||
<string name="pref_header_general">"General"</string>
|
|
||||||
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
|
|
||||||
<string name="add_source_hint_url">"Link"</string>
|
|
||||||
<string name="add_source_hint_name">"නම"</string>
|
|
||||||
<string name="add_source">"Add a source"</string>
|
|
||||||
<string name="add_source_save">"සුරකින්න"</string>
|
|
||||||
<string name="wrong_infos">"Check your details again."</string>
|
|
||||||
<string name="all_posts_not_read">"All posts weren't read"</string>
|
|
||||||
<string name="all_posts_read">"All posts were read"</string>
|
|
||||||
<string name="cant_get_favs">"Can't get favorites"</string>
|
|
||||||
<string name="cant_get_new_elements">"Can't get new articles"</string>
|
|
||||||
<string name="cant_get_read">"Can't get read articles"</string>
|
|
||||||
<string name="nothing_here">"Nothing here"</string>
|
|
||||||
<string name="tab_new">"New"</string>
|
|
||||||
<string name="tab_read">"සියල්ල"</string>
|
|
||||||
<string name="tab_favs">"Favorites"</string>
|
|
||||||
<string name="action_about">"මේ ගැන"</string>
|
|
||||||
<string name="marked_as_read">"Item read"</string>
|
|
||||||
<string name="marked_as_unread">"Item unread"</string>
|
|
||||||
<string name="undo_string">"Undo"</string>
|
|
||||||
<string name="addStringNoUrl">"Log in to add sources."</string>
|
|
||||||
<string name="cant_get_sources">"Can't get sources list."</string>
|
|
||||||
<string name="cant_create_source">"Can't create source."</string>
|
|
||||||
<string name="cant_get_spouts">"Can't get spouts list."</string>
|
|
||||||
<string name="form_not_complete">"The form is not complete"</string>
|
|
||||||
<string name="pref_header_links">"Links"</string>
|
|
||||||
<string name="issue_tracker_link">"Issue Tracker"</string>
|
|
||||||
<string name="issue_tracker_summary">"Report a bug or ask for a new feature"</string>
|
|
||||||
<string name="warning_wrong_url">"WARNING"</string>
|
|
||||||
<string name="pref_switch_card_view_title">"Card View"</string>
|
|
||||||
<string name="cant_mark_favortie">"Can't mark article as favorite"</string>
|
|
||||||
<string name="cant_unmark_favortie">"Can't remove item from favorite"</string>
|
|
||||||
<string name="share">"Share"</string>
|
|
||||||
<string name="rating_prompt_title">"Enjoying the app ?"</string>
|
|
||||||
<string name="rating_prompt_yes">"Yes !"</string>
|
|
||||||
<string name="rating_prompt_no">"Not really …"</string>
|
|
||||||
<string name="rating_prompt_feedback_title">"Can you tell us why ?"</string>
|
|
||||||
<string name="rating_prompt_feedback_yes">"OK !"</string>
|
|
||||||
<string name="rating_prompt_feedback_no">"Not now."</string>
|
|
||||||
<string name="rating_prompt_rating_title">"Great ! Can you rate us on the Store ?"</string>
|
|
||||||
<string name="rating_prompt_rating_yes">"Sure !"</string>
|
|
||||||
<string name="rating_prompt_rating_no">"Not right now."</string>
|
|
||||||
<string name="rating_prompt_thanks">"Thanks, your feedback help enhance the app !"</string>
|
|
||||||
<string name="switch_unread_count">"Display the unread count as a badge for the bottom bar."</string>
|
|
||||||
<string name="switch_unread_count_title">"Display unread count"</string>
|
|
||||||
<string name="display_all_counts_title">"Display count for favorite and read"</string>
|
|
||||||
<string name="text_wrong_url">"You seem to be trying to use an invalid URL. Make sure it is correct, and if the problem persists, contact me (via the store contact link). Please note that the app needs you to be using Selfoss. You can't access RSS feeds without it."</string>
|
|
||||||
<string name="pref_general_internal_browser_title">"Open links inside the app"</string>
|
|
||||||
<string name="pref_general_internal_browser_on">"Articles will open inside the app"</string>
|
|
||||||
<string name="pref_general_internal_browser_off">"Articles will open with your default browser"</string>
|
|
||||||
<string name="prefer_article_viewer_title">"Use the article viewer"</string>
|
|
||||||
<string name="prefer_article_viewer_on">"Will use the article viewer instead of the internal browser"</string>
|
|
||||||
<string name="prefer_article_viewer_off">"Will use the internal browser instead of the article viewer"</string>
|
|
||||||
<string name="pref_general_category_links">"Link handling"</string>
|
|
||||||
<string name="pref_general_category_displaying">"Displaying"</string>
|
|
||||||
<string name="pref_switch_card_view_on">"The articles will be displayed as cards"</string>
|
|
||||||
<string name="pref_switch_card_view_off">"The articles will be displayed as a list"</string>
|
|
||||||
<string name="menu_home_refresh">"Update remote"</string>
|
|
||||||
<string name="refresh_success_response">"The remote is updated, you can now reload the articles list"</string>
|
|
||||||
<string name="refresh_failer_message">"The update didn't work, try again later, or check your selfoss logs."</string>
|
|
||||||
<string name="refresh_in_progress">"Refresh in progress"</string>
|
|
||||||
<string name="card_height_title">Full height cards</string>
|
|
||||||
<string name="card_height_on">Cards height will adjust to its content</string>
|
|
||||||
<string name="card_height_off">Card height will be fixed</string>
|
|
||||||
<string name="source_code">Source code</string>
|
|
||||||
<string name="cant_mark_read">Can\'t mark article as read</string>
|
|
||||||
<string name="cant_mark_unread">Can\'t mark article as unread</string>
|
|
||||||
<string name="drawer_error_loading_tags">Error loading tags…</string>
|
|
||||||
<string name="drawer_error_loading_sources">Error loading sources…</string>
|
|
||||||
<string name="drawer_item_filters">Filters</string>
|
|
||||||
<string name="drawer_action_clear">clear</string>
|
|
||||||
<string name="drawer_item_tags">Tags</string>
|
|
||||||
<string name="drawer_item_sources">Sources</string>
|
|
||||||
<string name="drawer_action_edit">edit</string>
|
|
||||||
<string name="no_tags_loaded">No tags loaded</string>
|
|
||||||
<string name="no_sources_loaded">No sources loaded</string>
|
|
||||||
<string name="drawer_loading">Loading …</string>
|
|
||||||
<string name="menu_home_search">Search</string>
|
|
||||||
<string name="can_delete_source">Can\'t delete the source…</string>
|
|
||||||
<string name="base_url_error">There was an issue when trying to communicate with your Selfoss Instance. If the issue persists, please get in touch with me.</string>
|
|
||||||
<string name="pref_header_theme">Themes</string>
|
|
||||||
<string name="default_theme">Default</string>
|
|
||||||
<string name="default_dark_theme">Default/Dark</string>
|
|
||||||
<string name="pref_header_debug">Debug</string>
|
|
||||||
<string name="self_hosted_cert_switch">Using a self hosted certificate ?</string>
|
|
||||||
<string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string>
|
|
||||||
<string name="pref_selfoss_category">Selfoss Api</string>
|
|
||||||
<string name="pref_api_items_number_title">Loaded items number</string>
|
|
||||||
<string name="pref_hidden_tags">Hidden Tags</string>
|
|
||||||
<string name="summary_debug_identifier">Debug identifier</string>
|
|
||||||
<string name="unique_id_to_clipboard">Identifier copied to your clipboard</string>
|
|
||||||
<string name="display_header_drawer_summary">Display a header with the selfoss instance url on the lateral drawer.</string>
|
|
||||||
<string name="display_header_drawer_title">Account header</string>
|
|
||||||
<string name="pref_general_infinite_loading_title">Load more articles on scroll</string>
|
|
||||||
<string name="translation">Translation</string>
|
|
||||||
<string name="cant_open_invalid_url">The item url is invalid. I\'m looking into solving this issue so the app won\'t crash.</string>
|
|
||||||
<string name="drawer_report_bug">Report a bug</string>
|
|
||||||
<string name="items_number_should_be_number">The items number should be an integer.</string>
|
|
||||||
<string name="reader_action_more">Read more</string>
|
|
||||||
<string name="reader_action_open">Open in browser</string>
|
|
||||||
<string name="reader_action_share">Share</string>
|
|
||||||
<string name="pref_switch_actions_pager_scroll_on">Mark articles as read when swiping between articles.</string>
|
|
||||||
<string name="add_to_favs_reader">Add to favorites</string>
|
|
||||||
<string name="remove_to_favs_reader">Remove from favorites</string>
|
|
||||||
<string name="pref_content_reader_font_size">Article reader content font size</string>
|
|
||||||
<string name="pref_header_viewer">Article viewer</string>
|
|
||||||
<string name="refresh_dialog_message">This will refresh your Selfoss instance.</string>
|
|
||||||
<string name="markall_dialog_message">This will mark all the items as read.</string>
|
|
||||||
<string name="pref_switch_actions_pager_scroll">Mark as read on swipe</string>
|
|
||||||
<string name="pref_switch_actions_pager_scroll_off">Don\'t mark articles as read when swiping.</string>
|
|
||||||
<string name="pref_acra_alwaysaccept">Automatically send crash reports</string>
|
|
||||||
<string name="pref_acra_alwaysaccept_enabled">Will send crash reports automatically</string>
|
|
||||||
<string name="pref_acra_alwaysaccept_disabled">Will ask everytime when sending crash reports.</string>
|
|
||||||
<string name="pref_debug_crash_reports">Crash reports</string>
|
|
||||||
<string name="pref_debug_debug_logs">Debug logging (these will be sent without a dialog)</string>
|
|
||||||
<string name="acra_login">Enable logging</string>
|
|
||||||
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
|
||||||
<string name="unmark">Mark item as unread</string>
|
|
||||||
<string name="pref_header_offline">Offline and cache</string>
|
|
||||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
|
||||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
|
||||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
|
||||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
|
||||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
|
||||||
<string name="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>
|
|
||||||
<string name="reader_text_align_left">Align left</string>
|
|
||||||
<string name="reader_text_align_justify">Justify</string>
|
|
||||||
<string name="settings_reader_font">Reader font</string>
|
|
||||||
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
|
||||||
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
|
||||||
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
|
||||||
<string name="remove_source">Remove source</string>
|
|
||||||
</resources>
|
|
@ -139,8 +139,6 @@
|
|||||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
|
||||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
|
||||||
<string name="no_network_connectivity">Not connected !</string>
|
<string name="no_network_connectivity">Not connected !</string>
|
||||||
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||||
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||||
@ -162,8 +160,4 @@
|
|||||||
<string name="reader_text_align_left">Align left</string>
|
<string name="reader_text_align_left">Align left</string>
|
||||||
<string name="reader_text_align_justify">Justify</string>
|
<string name="reader_text_align_justify">Justify</string>
|
||||||
<string name="settings_reader_font">Reader font</string>
|
<string name="settings_reader_font">Reader font</string>
|
||||||
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
|
||||||
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
|
||||||
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
|
||||||
<string name="remove_source">Remove source</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -139,8 +139,6 @@
|
|||||||
<string name="pref_switch_items_caching_off">文章不会被保存到设备内存,应用程序在离线时将无法阅读它们</string>
|
<string name="pref_switch_items_caching_off">文章不会被保存到设备内存,应用程序在离线时将无法阅读它们</string>
|
||||||
<string name="pref_switch_items_caching_on">文章将被保存到设备内存并可在离线时使用</string>
|
<string name="pref_switch_items_caching_on">文章将被保存到设备内存并可在离线时使用</string>
|
||||||
<string name="pref_switch_items_caching">保存项目以便离线使用</string>
|
<string name="pref_switch_items_caching">保存项目以便离线使用</string>
|
||||||
<string name="pref_switch_update_sources">检查新来源和标签</string>
|
|
||||||
<string name="pref_switch_update_sources_summary">如果你的服务器接收过多的数据库查询,请禁用此功能。</string>
|
|
||||||
<string name="no_network_connectivity">未连接!</string>
|
<string name="no_network_connectivity">未连接!</string>
|
||||||
<string name="pref_switch_periodic_refresh">同步文章</string>
|
<string name="pref_switch_periodic_refresh">同步文章</string>
|
||||||
<string name="pref_switch_periodic_refresh_off">文章将不会在后台同步</string>
|
<string name="pref_switch_periodic_refresh_off">文章将不会在后台同步</string>
|
||||||
@ -162,8 +160,4 @@
|
|||||||
<string name="reader_text_align_left">左对齐</string>
|
<string name="reader_text_align_left">左对齐</string>
|
||||||
<string name="reader_text_align_justify">左右对齐</string>
|
<string name="reader_text_align_justify">左右对齐</string>
|
||||||
<string name="settings_reader_font">阅读器字体</string>
|
<string name="settings_reader_font">阅读器字体</string>
|
||||||
<string name="reader_static_bar_title">文章查看器中的静态底部栏</string>
|
|
||||||
<string name="reader_static_bar_on">底部栏将始终显示</string>
|
|
||||||
<string name="reader_static_bar_off">底部栏可以通过浮动按钮显示</string>
|
|
||||||
<string name="remove_source">删除源</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -139,8 +139,6 @@
|
|||||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
|
||||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
|
||||||
<string name="no_network_connectivity">Not connected !</string>
|
<string name="no_network_connectivity">Not connected !</string>
|
||||||
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||||
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||||
@ -162,8 +160,4 @@
|
|||||||
<string name="reader_text_align_left">Align left</string>
|
<string name="reader_text_align_left">Align left</string>
|
||||||
<string name="reader_text_align_justify">Justify</string>
|
<string name="reader_text_align_justify">Justify</string>
|
||||||
<string name="settings_reader_font">Reader font</string>
|
<string name="settings_reader_font">Reader font</string>
|
||||||
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
|
||||||
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
|
||||||
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
|
||||||
<string name="remove_source">Remove source</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -5,9 +5,8 @@
|
|||||||
<color name="colorAccent">#FFff5722</color>
|
<color name="colorAccent">#FFff5722</color>
|
||||||
<color name="colorAccentDark">#FFbf360c</color>
|
<color name="colorAccentDark">#FFbf360c</color>
|
||||||
<color name="pink">#FFe91e63</color>
|
<color name="pink">#FFe91e63</color>
|
||||||
<color name="white">#FFFFFF</color>
|
<color name="white">#FFFFFFFF</color>
|
||||||
<color name="black">#FF000000</color>
|
<color name="black">#FF000000</color>
|
||||||
<color name="red">#FF0000</color>
|
|
||||||
<color name="refresh_progress_1">@color/colorAccentDark</color>
|
<color name="refresh_progress_1">@color/colorAccentDark</color>
|
||||||
<color name="refresh_progress_2">@color/colorAccent</color>
|
<color name="refresh_progress_2">@color/colorAccent</color>
|
||||||
<color name="refresh_progress_3">@color/pink</color>
|
<color name="refresh_progress_3">@color/pink</color>
|
||||||
@ -20,5 +19,5 @@
|
|||||||
|
|
||||||
<color name="cardBackgroundColor">#FFFFFFFF</color>
|
<color name="cardBackgroundColor">#FFFFFFFF</color>
|
||||||
<color name="materialDrawerHeaderSelectionText">#212121</color>
|
<color name="materialDrawerHeaderSelectionText">#212121</color>
|
||||||
<color name="darkBackground">#303030</color>
|
<color name="darkBackground">#FF303030</color>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -140,8 +140,6 @@
|
|||||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
|
||||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
|
||||||
<string name="no_network_connectivity">Not connected !</string>
|
<string name="no_network_connectivity">Not connected !</string>
|
||||||
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||||
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||||
@ -165,8 +163,4 @@
|
|||||||
<string name="settings_reader_font">Reader font</string>
|
<string name="settings_reader_font">Reader font</string>
|
||||||
<string name="open_sans_font_id" translatable="false">open_sans</string>
|
<string name="open_sans_font_id" translatable="false">open_sans</string>
|
||||||
<string name="roboto_font_id" translatable="false">roboto</string>
|
<string name="roboto_font_id" translatable="false">roboto</string>
|
||||||
<string name="reader_static_bar_title">Static bottom bar in the article viewer</string>
|
|
||||||
<string name="reader_static_bar_on">The bottom bar will always be displayed</string>
|
|
||||||
<string name="reader_static_bar_off">The bottom bar can be shown through the floating button</string>
|
|
||||||
<string name="remove_source">Remove source</string>
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -4,70 +4,41 @@
|
|||||||
<item name="android:windowBackground">@drawable/background_splash</item>
|
<item name="android:windowBackground">@drawable/background_splash</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="NoBar" parent="Theme.MaterialComponents.Light.NoActionBar">
|
<style name="NoBar" parent="MaterialDrawerTheme.Light">
|
||||||
<item name="colorPrimary">@color/colorPrimary</item>
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||||
<item name="colorAccent">@color/colorAccent</item>
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
<item name="colorAccentDark">@color/colorAccentDark</item>
|
<item name="colorAccentDark">@color/colorAccentDark</item>
|
||||||
<item name="cardBackgroundColor">@color/white</item>
|
<item name="cardBackgroundColor">@color/white</item>
|
||||||
<item name="android:colorBackground">@color/grey_50</item>
|
<item name="android:colorBackground">@color/md_grey_50</item>
|
||||||
<item name="colorSurface">@color/grey_50</item>
|
<item name="android:textColorPrimary">@color/md_grey_900</item>
|
||||||
<item name="android:textColorPrimary">@color/grey_900</item>
|
<item name="android:textColorSecondary">@color/md_grey_400</item>
|
||||||
<item name="android:textColorSecondary">@color/grey_400</item>
|
<item name="material_drawer_header_selection_text">@color/md_grey_900</item>
|
||||||
<item name="materialDrawerStyle">@style/App.materialDrawerStyle</item>
|
|
||||||
<item name="materialDrawerHeaderStyle">@style/Widget.MaterialDrawerHeaderStyle</item>
|
|
||||||
<item name="toolbarPopupTheme">@style/ThemeOverlay.AppCompat.Light</item>
|
<item name="toolbarPopupTheme">@style/ThemeOverlay.AppCompat.Light</item>
|
||||||
<item name="preferenceTheme">@style/PreferenceStyle</item>
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style name="NoBarDark" parent="Theme.MaterialComponents.DayNight.NoActionBar">
|
<style name="NoBarDark" parent="MaterialDrawerTheme">
|
||||||
<item name="colorPrimary">@color/colorPrimary</item>
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||||
<item name="colorAccent">@color/colorAccent</item>
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
<item name="colorAccentDark">@color/colorAccentDark</item>
|
<item name="colorAccentDark">@color/colorAccentDark</item>
|
||||||
<item name="cardBackgroundColor">@color/grey_800</item>
|
<item name="cardBackgroundColor">@color/md_grey_800</item>
|
||||||
<item name="android:colorBackground">@color/darkBackground</item>
|
<item name="android:colorBackground">@color/darkBackground</item>
|
||||||
<item name="colorSurface">@color/darkBackground</item>
|
<item name="bnbBackgroundColor">@color/md_grey_900</item>
|
||||||
<item name="alertDialogTheme">@style/AlertDialogDark</item>
|
<item name="android:textColorPrimary">@color/md_white_1000</item>
|
||||||
<item name="bnbBackgroundColor">@color/grey_900</item>
|
<item name="android:textColorSecondary">@color/md_grey_600</item>
|
||||||
<item name="android:textColorPrimary">@color/white</item>
|
<item name="material_drawer_header_selection_text">@color/md_grey_900</item>
|
||||||
<item name="android:textColorSecondary">@color/grey_600</item>
|
|
||||||
<item name="materialDrawerStyle">@style/App.materialDrawerStyle</item>
|
|
||||||
<item name="materialDrawerHeaderStyle">@style/Widget.MaterialDrawerHeaderStyle</item>
|
|
||||||
<item name="toolbarPopupTheme">@style/ThemeOverlay.AppCompat.Dark</item>
|
<item name="toolbarPopupTheme">@style/ThemeOverlay.AppCompat.Dark</item>
|
||||||
<item name="preferenceTheme">@style/PreferenceStyle</item>
|
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- ToolBar -->
|
<!-- ToolBar -->
|
||||||
<style name="ToolBarStyle" parent="Theme.AppCompat">
|
<style name="ToolBarStyle" parent="Theme.AppCompat">
|
||||||
<item name="android:textColorPrimary">@color/white</item>
|
<item name="android:textColorPrimary">@color/white</item>
|
||||||
<item name="android:textColorSecondary">@color/white</item>
|
<item name="android:textColorSecondary">@color/white</item>
|
||||||
|
<item name="material_drawer_header_selection_text">@color/md_grey_900</item>
|
||||||
<item name="actionMenuTextColor">@color/white</item>
|
<item name="actionMenuTextColor">@color/white</item>
|
||||||
<!--<item name="actionOverflowButtonStyle">@style/ActionButtonOverflowStyle</item>
|
<!--<item name="actionOverflowButtonStyle">@style/ActionButtonOverflowStyle</item>
|
||||||
<item name="drawerArrowStyle">@style/DrawerArrowStyle</item>-->
|
<item name="drawerArrowStyle">@style/DrawerArrowStyle</item>-->
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- Material Drawer Theme -->
|
|
||||||
<style name="App.materialDrawerStyle" parent="@style/Widget.MaterialDrawerStyle">
|
|
||||||
<item name="materialDrawerPrimaryIcon">?android:textColorPrimary</item>
|
|
||||||
<item name="materialDrawerSecondaryIcon">?android:textColorPrimary</item>
|
|
||||||
<item name="materialDrawerSecondaryText">?android:textColorPrimary</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<!-- Preference Theme -->
|
|
||||||
<style name="PreferenceStyle" parent="@style/PreferenceThemeOverlay">
|
|
||||||
<item name="android:tint">?android:textColorPrimary</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<!-- Spinner Theme -->
|
|
||||||
<style name="App.Spinner" parent="Widget.AppCompat.Light.DropDownItem.Spinner">
|
|
||||||
<item name="android:textColor">?android:textColorPrimary</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<!-- Alert dialog Theme -->
|
|
||||||
|
|
||||||
<style name="AlertDialogDark" parent="Theme.MaterialComponents.Dialog">
|
|
||||||
<item name="android:background">@color/darkBackground</item>
|
|
||||||
</style>
|
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -1,11 +1,9 @@
|
|||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
<EditTextPreference
|
<EditTextPreference
|
||||||
android:inputType="number"
|
android:inputType="number"
|
||||||
android:key="api_timeout"
|
android:key="api_timeout"
|
||||||
android:selectAllOnFocus="true"
|
android:selectAllOnFocus="true"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:title="@string/pref_api_timeout"
|
android:title="@string/pref_api_timeout" />
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:title="@string/pref_selfoss_category">
|
android:title="@string/pref_selfoss_category">
|
||||||
@ -11,8 +10,7 @@
|
|||||||
android:key="prefer_api_items_number"
|
android:key="prefer_api_items_number"
|
||||||
android:selectAllOnFocus="true"
|
android:selectAllOnFocus="true"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:title="@string/pref_api_items_number_title"
|
android:title="@string/pref_api_items_number_title" />
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
|
|
||||||
<EditTextPreference
|
<EditTextPreference
|
||||||
android:defaultValue=""
|
android:defaultValue=""
|
||||||
@ -20,14 +18,12 @@
|
|||||||
android:key="hidden_tags"
|
android:key="hidden_tags"
|
||||||
android:selectAllOnFocus="true"
|
android:selectAllOnFocus="true"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:title="@string/pref_hidden_tags"
|
android:title="@string/pref_hidden_tags" />
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
|
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:key="infinite_loading"
|
android:key="infinite_loading"
|
||||||
android:title="@string/pref_general_infinite_loading_title"
|
android:title="@string/pref_general_infinite_loading_title" />
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:title="@string/pref_general_category_links">
|
android:title="@string/pref_general_category_links">
|
||||||
|
|
||||||
@ -37,24 +33,14 @@
|
|||||||
android:key="prefer_internal_browser"
|
android:key="prefer_internal_browser"
|
||||||
android:summaryOff="@string/pref_general_internal_browser_off"
|
android:summaryOff="@string/pref_general_internal_browser_off"
|
||||||
android:summaryOn="@string/pref_general_internal_browser_on"
|
android:summaryOn="@string/pref_general_internal_browser_on"
|
||||||
android:title="@string/pref_general_internal_browser_title"
|
android:title="@string/pref_general_internal_browser_title" />
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="true"
|
android:defaultValue="true"
|
||||||
android:dependency="prefer_internal_browser"
|
android:dependency="prefer_internal_browser"
|
||||||
android:key="prefer_article_viewer"
|
android:key="prefer_article_viewer"
|
||||||
android:summaryOff="@string/prefer_article_viewer_off"
|
android:summaryOff="@string/prefer_article_viewer_off"
|
||||||
android:summaryOn="@string/prefer_article_viewer_on"
|
android:summaryOn="@string/prefer_article_viewer_on"
|
||||||
android:title="@string/prefer_article_viewer_title"
|
android:title="@string/prefer_article_viewer_title" />
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
<SwitchPreference
|
|
||||||
android:defaultValue="false"
|
|
||||||
android:dependency="prefer_article_viewer"
|
|
||||||
android:key="reader_static_bar"
|
|
||||||
android:summaryOff="@string/reader_static_bar_off"
|
|
||||||
android:summaryOn="@string/reader_static_bar_on"
|
|
||||||
android:title="@string/reader_static_bar_title"
|
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
|
|
||||||
<PreferenceCategory
|
<PreferenceCategory
|
||||||
android:title="@string/pref_general_category_displaying">
|
android:title="@string/pref_general_category_displaying">
|
||||||
@ -64,34 +50,29 @@
|
|||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:key="account_header_displaying"
|
android:key="account_header_displaying"
|
||||||
android:summary="@string/display_header_drawer_summary"
|
android:summary="@string/display_header_drawer_summary"
|
||||||
android:title="@string/display_header_drawer_title"
|
android:title="@string/display_header_drawer_title" />
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:key="card_view_active"
|
android:key="card_view_active"
|
||||||
android:summaryOff="@string/pref_switch_card_view_off"
|
android:summaryOff="@string/pref_switch_card_view_off"
|
||||||
android:summaryOn="@string/pref_switch_card_view_on"
|
android:summaryOn="@string/pref_switch_card_view_on"
|
||||||
android:title="@string/pref_switch_card_view_title"
|
android:title="@string/pref_switch_card_view_title" />
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:dependency="card_view_active"
|
android:dependency="card_view_active"
|
||||||
android:key="full_height_cards"
|
android:key="full_height_cards"
|
||||||
android:summaryOff="@string/card_height_off"
|
android:summaryOff="@string/card_height_off"
|
||||||
android:summaryOn="@string/card_height_on"
|
android:summaryOn="@string/card_height_on"
|
||||||
android:title="@string/card_height_title"
|
android:title="@string/card_height_title" />
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="true"
|
android:defaultValue="true"
|
||||||
android:key="display_unread_count"
|
android:key="display_unread_count"
|
||||||
android:summaryOn="@string/switch_unread_count"
|
android:summaryOn="@string/switch_unread_count"
|
||||||
android:title="@string/switch_unread_count_title"
|
android:title="@string/switch_unread_count_title" />
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:dependency="display_unread_count"
|
android:dependency="display_unread_count"
|
||||||
android:key="display_other_count"
|
android:key="display_other_count"
|
||||||
android:title="@string/display_all_counts_title"
|
android:title="@string/display_all_counts_title" />
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
57
app/src/main/res/xml/pref_headers.xml
Normal file
57
app/src/main/res/xml/pref_headers.xml
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<preference-headers xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
|
<header
|
||||||
|
android:fragment="apps.amine.bou.readerforselfoss.settings.SettingsActivity$GeneralPreferenceFragment"
|
||||||
|
android:icon="@drawable/ic_settings_black_24dp"
|
||||||
|
android:title="@string/pref_header_general">
|
||||||
|
<extra
|
||||||
|
android:name="iconDark"
|
||||||
|
android:value="ic_settings_white_24dp"/>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<header
|
||||||
|
android:fragment="apps.amine.bou.readerforselfoss.settings.SettingsActivity$ArticleViewerPreferenceFragment"
|
||||||
|
android:icon="@drawable/ic_chrome_reader_mode_black_24dp"
|
||||||
|
android:title="@string/pref_header_viewer">
|
||||||
|
<extra
|
||||||
|
android:name="iconDark"
|
||||||
|
android:value="ic_chrome_reader_mode_white_24dp"/>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<header
|
||||||
|
android:fragment="apps.amine.bou.readerforselfoss.settings.SettingsActivity$OfflinePreferenceFragment"
|
||||||
|
android:icon="@drawable/ic_signal_wifi_off_black_24dp"
|
||||||
|
android:title="@string/pref_header_offline">
|
||||||
|
<extra
|
||||||
|
android:name="iconDark"
|
||||||
|
android:value="ic_signal_wifi_off_white_24dp"/>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<header
|
||||||
|
android:fragment="apps.amine.bou.readerforselfoss.settings.SettingsActivity$ThemePreferenceFragment"
|
||||||
|
android:icon="@drawable/ic_color_lens_black_24dp"
|
||||||
|
android:title="@string/pref_header_theme">
|
||||||
|
<extra
|
||||||
|
android:name="iconDark"
|
||||||
|
android:value="ic_color_lens_white_24dp"/>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<header
|
||||||
|
android:fragment="apps.amine.bou.readerforselfoss.settings.SettingsActivity$LinksPreferenceFragment"
|
||||||
|
android:icon="@drawable/ic_info_black_24dp"
|
||||||
|
android:title="@string/pref_header_links">
|
||||||
|
<extra
|
||||||
|
android:name="iconDark"
|
||||||
|
android:value="ic_info_white_24dp"/>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<header
|
||||||
|
android:fragment="apps.amine.bou.readerforselfoss.settings.SettingsActivity$ExperimentalPreferenceFragment"
|
||||||
|
android:icon="@drawable/ic_widgets_black_24dp"
|
||||||
|
android:title="@string/pref_header_experimental">
|
||||||
|
<extra
|
||||||
|
android:name="iconDark"
|
||||||
|
android:value="ic_widgets_white_24dp"/>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
</preference-headers>
|
@ -1,17 +1,13 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
|
|
||||||
<Preference android:title="@string/issue_tracker_link"
|
<Preference android:title="@string/issue_tracker_link"
|
||||||
android:summary="@string/issue_tracker_summary"
|
android:summary="@string/issue_tracker_summary"
|
||||||
android:key="trackerLink"
|
android:key="trackerLink" />
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
|
|
||||||
<Preference android:title="@string/source_code"
|
<Preference android:title="@string/source_code"
|
||||||
android:key="sourceLink"
|
android:key="sourceLink" />
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
|
|
||||||
<Preference android:title="@string/translation"
|
<Preference android:title="@string/translation"
|
||||||
android:key="translation"
|
android:key="translation" />
|
||||||
app:iconSpaceReserved="false"/>
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
@ -1,35 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:title="@string/title_activity_settings">
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:fragment="apps.amine.bou.readerforselfoss.settings.SettingsActivity$GeneralPreferenceFragment"
|
|
||||||
android:title="@string/pref_header_general"
|
|
||||||
android:icon="@drawable/ic_settings_black_24dp" />
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:fragment="apps.amine.bou.readerforselfoss.settings.SettingsActivity$ArticleViewerPreferenceFragment"
|
|
||||||
android:title="@string/pref_header_viewer"
|
|
||||||
android:icon="@drawable/ic_chrome_reader_mode_black_24dp" />
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:fragment="apps.amine.bou.readerforselfoss.settings.SettingsActivity$OfflinePreferenceFragment"
|
|
||||||
android:title="@string/pref_header_offline"
|
|
||||||
android:icon="@drawable/ic_signal_wifi_off_black_24dp" />
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:fragment="apps.amine.bou.readerforselfoss.settings.SettingsActivity$ThemePreferenceFragment"
|
|
||||||
android:title="@string/pref_header_theme"
|
|
||||||
android:icon="@drawable/ic_color_lens_black_24dp" />
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:fragment="apps.amine.bou.readerforselfoss.settings.SettingsActivity$LinksPreferenceFragment"
|
|
||||||
android:title="@string/pref_header_links"
|
|
||||||
android:icon="@drawable/ic_info_black_24dp" />
|
|
||||||
|
|
||||||
<Preference
|
|
||||||
android:fragment="apps.amine.bou.readerforselfoss.settings.SettingsActivity$ExperimentalPreferenceFragment"
|
|
||||||
android:title="@string/pref_header_experimental"
|
|
||||||
android:icon="@drawable/ic_widgets_black_24dp" />
|
|
||||||
|
|
||||||
</PreferenceScreen>
|
|
@ -1,9 +1,7 @@
|
|||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:key="items_caching"
|
android:key="items_caching"
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
android:summaryOff="@string/pref_switch_items_caching_off"
|
android:summaryOff="@string/pref_switch_items_caching_off"
|
||||||
android:summaryOn="@string/pref_switch_items_caching_on"
|
android:summaryOn="@string/pref_switch_items_caching_on"
|
||||||
android:title="@string/pref_switch_items_caching" />
|
android:title="@string/pref_switch_items_caching" />
|
||||||
@ -12,7 +10,6 @@
|
|||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:key="periodic_refresh"
|
android:key="periodic_refresh"
|
||||||
android:dependency="items_caching"
|
android:dependency="items_caching"
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
android:summaryOff="@string/pref_switch_periodic_refresh_off"
|
android:summaryOff="@string/pref_switch_periodic_refresh_off"
|
||||||
android:summaryOn="@string/pref_switch_periodic_refresh_on"
|
android:summaryOn="@string/pref_switch_periodic_refresh_on"
|
||||||
android:title="@string/pref_switch_periodic_refresh" />
|
android:title="@string/pref_switch_periodic_refresh" />
|
||||||
@ -21,7 +18,6 @@
|
|||||||
android:dependency="periodic_refresh"
|
android:dependency="periodic_refresh"
|
||||||
android:defaultValue="360"
|
android:defaultValue="360"
|
||||||
android:inputType="number"
|
android:inputType="number"
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
android:key="periodic_refresh_minutes"
|
android:key="periodic_refresh_minutes"
|
||||||
android:selectAllOnFocus="true"
|
android:selectAllOnFocus="true"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
@ -29,22 +25,13 @@
|
|||||||
|
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
android:key="refresh_when_charging"
|
android:key="refresh_when_charging"
|
||||||
android:dependency="periodic_refresh"
|
android:dependency="periodic_refresh"
|
||||||
android:title="@string/pref_switch_refresh_when_charging" />
|
android:title="@string/pref_switch_refresh_when_charging" />
|
||||||
|
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
android:key="notify_new_items"
|
android:key="notify_new_items"
|
||||||
android:dependency="periodic_refresh"
|
android:dependency="periodic_refresh"
|
||||||
android:title="@string/pref_switch_notify_new_items" />
|
android:title="@string/pref_switch_notify_new_items" />
|
||||||
|
|
||||||
<SwitchPreference
|
|
||||||
android:defaultValue="true"
|
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
android:key="update_sources"
|
|
||||||
android:summary="@string/pref_switch_update_sources_summary"
|
|
||||||
android:title="@string/pref_switch_update_sources" />
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
@ -1,35 +1,29 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
<!-- TODO translate this file -->
|
<!-- TODO translate this file -->
|
||||||
|
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:key="dark_theme"
|
android:key="dark_theme"
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
android:title="Dark theme" />
|
android:title="Dark theme" />
|
||||||
|
|
||||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
<com.jaredrummler.android.colorpicker.ColorPreference
|
||||||
android:defaultValue="@color/colorPrimary"
|
android:defaultValue="@color/colorPrimary"
|
||||||
android:key="color_primary"
|
android:key="color_primary"
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
android:title="Primary color"/>
|
android:title="Primary color"/>
|
||||||
|
|
||||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
<com.jaredrummler.android.colorpicker.ColorPreference
|
||||||
android:defaultValue="@color/colorPrimaryDark"
|
android:defaultValue="@color/colorPrimaryDark"
|
||||||
android:key="color_primary_dark"
|
android:key="color_primary_dark"
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
android:title="Primary dark color"/>
|
android:title="Primary dark color"/>
|
||||||
|
|
||||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
<com.jaredrummler.android.colorpicker.ColorPreference
|
||||||
android:defaultValue="@color/colorAccent"
|
android:defaultValue="@color/colorAccent"
|
||||||
android:key="color_accent"
|
android:key="color_accent"
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
android:title="Accent color"/>
|
android:title="Accent color"/>
|
||||||
|
|
||||||
<com.jaredrummler.android.colorpicker.ColorPreferenceCompat
|
<com.jaredrummler.android.colorpicker.ColorPreference
|
||||||
android:defaultValue="@color/colorAccentDark"
|
android:defaultValue="@color/colorAccentDark"
|
||||||
android:key="color_accent_dark"
|
android:key="color_accent_dark"
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
android:title="Accent dark color"/>
|
android:title="Accent dark color"/>
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
@ -1,9 +1,7 @@
|
|||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
|
||||||
<SwitchPreference
|
<SwitchPreference
|
||||||
android:defaultValue="false"
|
android:defaultValue="false"
|
||||||
android:key="mark_on_scroll"
|
android:key="mark_on_scroll"
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
android:summaryOff="@string/pref_switch_actions_pager_scroll_off"
|
android:summaryOff="@string/pref_switch_actions_pager_scroll_off"
|
||||||
android:summaryOn="@string/pref_switch_actions_pager_scroll_on"
|
android:summaryOn="@string/pref_switch_actions_pager_scroll_on"
|
||||||
android:title="@string/pref_switch_actions_pager_scroll" />
|
android:title="@string/pref_switch_actions_pager_scroll" />
|
||||||
@ -12,7 +10,6 @@
|
|||||||
android:defaultValue="16"
|
android:defaultValue="16"
|
||||||
android:inputType="number"
|
android:inputType="number"
|
||||||
android:key="reader_font_size"
|
android:key="reader_font_size"
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
android:selectAllOnFocus="true"
|
android:selectAllOnFocus="true"
|
||||||
android:singleLine="true"
|
android:singleLine="true"
|
||||||
android:title="@string/pref_content_reader_font_size" />
|
android:title="@string/pref_content_reader_font_size" />
|
||||||
@ -21,6 +18,5 @@
|
|||||||
android:entries="@array/preloaded_fonts_values"
|
android:entries="@array/preloaded_fonts_values"
|
||||||
android:entryValues="@array/preloaded_fonts_keys"
|
android:entryValues="@array/preloaded_fonts_keys"
|
||||||
android:key="reader_font"
|
android:key="reader_font"
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
android:title="@string/settings_reader_font" />
|
android:title="@string/settings_reader_font" />
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
26
build.gradle
26
build.gradle
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
ext {
|
ext {
|
||||||
kotlin_version = '1.6.10'
|
kotlin_version = '1.3.31'
|
||||||
android_version = '1.0.0'
|
android_version = '1.0.0'
|
||||||
androidx_version = '1.1.0-alpha05'
|
androidx_version = '1.1.0-alpha05'
|
||||||
lifecycle_version = '2.2.0-alpha01'
|
lifecycle_version = '2.2.0-alpha01'
|
||||||
@ -12,20 +12,24 @@ buildscript {
|
|||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
jcenter()
|
jcenter()
|
||||||
maven { url "https://www.jitpack.io" }
|
maven {
|
||||||
|
url "https://jitpack.io"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:7.0.4'
|
classpath 'com.android.tools.build:gradle:3.5.3'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
repositories {
|
repositories {
|
||||||
|
// For likebutton only
|
||||||
|
maven { url "https://jitpack.io" }
|
||||||
|
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
|
||||||
jcenter()
|
jcenter()
|
||||||
maven { url "https://www.jitpack.io" }
|
mavenCentral()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,4 +37,14 @@ task clean(type: Delete) {
|
|||||||
delete rootProject.buildDir
|
delete rootProject.buildDir
|
||||||
}
|
}
|
||||||
|
|
||||||
project.ext.preDexLibs = !project.hasProperty('disablePreDex')
|
project.ext.preDexLibs = !project.hasProperty('disablePreDex')
|
||||||
|
|
||||||
|
subprojects {
|
||||||
|
project.plugins.whenPluginAdded { plugin ->
|
||||||
|
if ("com.android.build.gradle.AppPlugin" == plugin.class.name) {
|
||||||
|
project.android.dexOptions.preDexLibraries = rootProject.ext.preDexLibs
|
||||||
|
} else if ("com.android.build.gradle.LibraryPlugin" == plugin.class.name) {
|
||||||
|
project.android.dexOptions.preDexLibraries = rootProject.ext.preDexLibs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +0,0 @@
|
|||||||
A new RSS reader for <a href="http://selfoss.aditu.de/">selfoss</a>.
|
|
||||||
|
|
||||||
What it does:
|
|
||||||
|
|
||||||
<ul>
|
|
||||||
<li>Fetches read, unread, and favorite feeds.</li>
|
|
||||||
<li>Marking as read, marking as favorite.</li>
|
|
||||||
<li>Manage selfoss sources from the app.</li>
|
|
||||||
<li>Add an RSS feed from within the app, or by sharing a link from your browser.</li>
|
|
||||||
<li>Choose between multiple light and dark themes.</li>
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
PS: It only works with Selfoss
|
|
Binary file not shown.
Before Width: | Height: | Size: 266 KiB |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user