Compare commits
170 Commits
v1.5.7
...
v161810287
Author | SHA1 | Date | |
---|---|---|---|
89d34a1a71 | |||
8788e920ce | |||
d306fb53d3 | |||
374537b5c7 | |||
598149d4cd | |||
5569a47674 | |||
0dc6981913 | |||
4984f2f7ad | |||
3b6891c84a | |||
4901e7174c | |||
8d70e68fe2 | |||
d3e1527b70 | |||
0c201301f2 | |||
6090590f24 | |||
06b88c783d | |||
bb75ebf635 | |||
7d7d0014be | |||
b3f8d44794 | |||
29d1e38340 | |||
2be872e61e | |||
377c5518f7 | |||
21be7357b5 | |||
d47ba2c820 | |||
a64b14614a | |||
6a88192e77 | |||
aa7c630818 | |||
7fb54f14c7 | |||
3d709c02b7 | |||
339d384561 | |||
50338d51af | |||
92dbabf899 | |||
0043021390 | |||
70ba9b20da | |||
7fda0a04a1 | |||
3db3157dc9 | |||
2089fe60ca | |||
9606d36670 | |||
869cf64c54 | |||
f57ec1f6c0 | |||
361eea9a06 | |||
838b4056ac | |||
0c0a98510b | |||
be642ed06f | |||
fd77f38e95 | |||
c9baab7267 | |||
86985cfd5b | |||
1327a4e069 | |||
c46acbc579 | |||
4c6a403fae | |||
78920022bd | |||
7b16c41e82 | |||
3389f8bd09 | |||
8dc25c527d | |||
46d6bd57c1 | |||
db014fe13d | |||
6c293f4cac | |||
91e5d3736f | |||
e11dee220f | |||
fcebf916d2 | |||
73cc1a7297 | |||
798f112498 | |||
38b5e7dc65 | |||
2799a48f2b | |||
ad5edae6cd | |||
9cb02f0272 | |||
6d24fd9336 | |||
a3a7b78c96 | |||
e995286068 | |||
65fb6d9b7e | |||
eb02d1efad | |||
f8d3e1eefb | |||
218b8fa843 | |||
9f94af6239 | |||
d3584ac40e | |||
90bdb289d0 | |||
78a08750a2 | |||
baba851e97 | |||
2a03783623 | |||
9f2a4438b1 | |||
5ee5287ffa | |||
29547c2c94 | |||
4846c870fa | |||
c17980a032 | |||
a929e419d9 | |||
487d484bae | |||
0ca4c04c61 | |||
c857cf2d67 | |||
acb502028b | |||
533636f3a1 | |||
eb5672901b | |||
53a8716b51 | |||
3aaff612af | |||
fdcd8c6c6a | |||
bafd478604 | |||
987513a88b | |||
a450ab2a3b | |||
db89fe5aad | |||
67a30b92f6 | |||
c397de8c3e | |||
b4db532c45 | |||
ebecc9c80a | |||
4f8556fca8 | |||
68892fb41b | |||
6d6f6c72ac | |||
df5556b945 | |||
d6c74049c3 | |||
18946464a2 | |||
edb5eabee7 | |||
99a305f3e2 | |||
68dc5a6acf | |||
6816461502 | |||
15b93bbd9e | |||
cd61e140f6 | |||
4d861a84e6 | |||
f24de68618 | |||
3bcffff444 | |||
75e9031fa5 | |||
3b77e24399 | |||
0a738e895f | |||
242e5ba035 | |||
c94612106c | |||
320924b4ed | |||
403ecc4521 | |||
6a50b37364 | |||
d9d341ac5d | |||
e9805b731e | |||
c6d4337cd1 | |||
173f4b2ff7 | |||
3b9436264c | |||
35fe87d79d | |||
f1bb7ba9ad | |||
279f229166 | |||
be1794e27b | |||
4d4a2039c8 | |||
3013ae4f35 | |||
bb3f7d3786 | |||
f7cc305e44 | |||
da17f89148 | |||
ec71ab3c6f | |||
0d007f1492 | |||
96f8663b8f | |||
1a4bc1b301 | |||
b51ae58a97 | |||
b126fc32da | |||
b8d234c415 | |||
2c8902d404 | |||
80ad65b196 | |||
744d9ba72b | |||
0c1d708588 | |||
95e79e7c5d | |||
3ce3260d20 | |||
641f4f34d3 | |||
99620cb1c5 | |||
8f5f33f5d2 | |||
78e9230b82 | |||
78aa44c007 | |||
53fd944f00 | |||
9e6cb4ee3d | |||
87ad6f2826 | |||
9050f5a56f | |||
3437004082 | |||
dcf620af87 | |||
128085a02e | |||
302040ec25 | |||
e177c22032 | |||
a11007113a | |||
5e7897bcf4 | |||
9559af3637 | |||
4c499abcdb | |||
0055a503b3 |
17
.github/CONTRIBUTING.md
vendored
@ -8,7 +8,7 @@ Please read the guidelines before contributing, and follow them (or try to) when
|
||||
|
||||
### What you can do to help.
|
||||
|
||||
There are many ways to contribute to this project, you could report bugs, request missing features, suggest enhancements and changes to existing ones. You also can improve the README with useful tips that could help the other users.
|
||||
There are many ways to contribute to this project, you could [translate the app](https://crowdin.com/project/readerforselfoss), report bugs, request missing features, suggest enhancements and changes to existing ones. You also can improve the README with useful tips that could help the other users.
|
||||
|
||||
You can fork the repository, and [help me solve some issues](https://github.com/aminecmi/ReaderforSelfoss/issues?q=is%3Aissue+is%3Aopen+label%3A%22Up+For+Grabs%22) or [develop new things](https://github.com/aminecmi/ReaderforSelfoss/issues)
|
||||
|
||||
@ -28,6 +28,7 @@ Always check if the web version of your instance is working.
|
||||
|
||||
### Pull requests
|
||||
|
||||
* Don't create a PR for translations. See [here](https://github.com/aminecmi/ReaderforSelfoss/pull/170#issuecomment-355715654) for an explanation why.
|
||||
* Please ask before starting to work on an issue. I may be working on it, or someone else could be doing so.
|
||||
* Each pull request should implement **ONE** feature or bugfix. Keep in mind that you can submit as many PR as you want.
|
||||
* Your code must be simple and clear enough to avoid using comments to explain what it does.
|
||||
@ -46,15 +47,8 @@ You can directly import this project into IntellIJ/Android Studio.
|
||||
|
||||
You'll have to:
|
||||
|
||||
- Configure fabric and add your `apiKey` and `apiSecret` in the `fabric.properties` file.
|
||||
- Create a firebase project and add the `google-services.json` to the `app/` folder.
|
||||
- Define some parameters either in `~/.gradle/gradle.properties` or as gradle parameters (see the examples)
|
||||
|
||||
- mercuryApiKey: A [Mercury](https://mercury.postlight.com/web-parser/) web parser api key for the internal browser
|
||||
- feedbackEmail: An email to receive users feedback.
|
||||
- sourceUrl: an url to the source code, used in the settings. **It can be empty.**
|
||||
- trackerUrl: an url to the tracker, used in the settings. **It can be empty.**
|
||||
- githubToken: a github token used to report issues from within the app. [Details here](https://github.com/heinrichreimer/android-issue-reporter#how-to-create-a-bot-key). **It can be empty.**
|
||||
- appLoginUrl, appLoginUsername and appLoginPassword: url, username and password of a selfoss instance. **These are only used for tests. They can be empty if you don't test API calls.**
|
||||
|
||||
### Examples:
|
||||
@ -64,15 +58,10 @@ You'll have to:
|
||||
appLoginUrl="URL" # It can be empty.
|
||||
appLoginUsername="LOGIN" # It can be empty.
|
||||
appLoginPassword="PASS" # It can be empty.
|
||||
mercuryApiKey="LONGAPIKEY"
|
||||
feedbackEmail="EMAIL"
|
||||
sourceUrl="URLSOURCE" # It can be empty.
|
||||
trackerUrl="URLTRACKER" # It can be empty.
|
||||
githubToken="GITHUBTOKEN" # It can be empty or use https://github.com/heinrichreimer/android-issue-reporter#how-to-create-a-bot-key to generate one
|
||||
```
|
||||
|
||||
#### As gradle parameters
|
||||
|
||||
```
|
||||
./gradlew .... -P appLoginUrl="URL" -P appLoginUsername="LOGIN" -P appLoginPassword="PASS" -P mercuryApiKey="LONGAPIKEY" -P feedbackEmail="EMAIL" -P sourceUrl="URLSOURCE" -P trackerUrl="URLTRACKER" -P githubToken="GITHUBTOKEN"
|
||||
./gradlew .... -P appLoginUrl="URL" -P appLoginUsername="LOGIN" -P appLoginPassword="PASS"
|
||||
```
|
||||
|
1
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -5,6 +5,7 @@
|
||||
- [ ] I have updated the documentation accordingly.
|
||||
- [ ] I have added tests to cover my changes.
|
||||
- [ ] All new and existing tests passed.
|
||||
- [ ] This is **NOT** translation related. (See [here](https://github.com/aminecmi/ReaderforSelfoss/pull/170#issuecomment-355715654))
|
||||
|
||||
This closes issue #XXX
|
||||
|
||||
|
48
CHANGELOG.md
@ -1,8 +1,52 @@
|
||||
**1.6.x**
|
||||
|
||||
- Handling hidden tags.
|
||||
|
||||
- Fixed pre-lolipop issue with automatic theme changes.
|
||||
|
||||
- Removed all Build config things.
|
||||
|
||||
- Removed firebase and fabric.
|
||||
|
||||
- Added Acra for optional crash reporting and error logging.
|
||||
|
||||
- Dynamic themes !
|
||||
|
||||
- Strings cleaning.
|
||||
|
||||
- Versions updates.
|
||||
|
||||
- Fixes #215, #208.
|
||||
|
||||
**1.5.7.x**
|
||||
|
||||
- Added confirmation to the mark as read and update menues.
|
||||
|
||||
- Add to favorites from article viewer.
|
||||
|
||||
- Added an option to use a webview in the article viewer (see #149)
|
||||
|
||||
- Fixes (#151)
|
||||
- Fixes (#151 #152 #155 #157 #160 #174) and more.
|
||||
|
||||
- New year fixes !!!
|
||||
|
||||
- Changed page indicator position as it was overlaping content.
|
||||
|
||||
- Now using slack instead of gitter.
|
||||
|
||||
- Moved completely to a webview to fix #161.
|
||||
|
||||
- Fixed typos in French ( Thanks @aancel )
|
||||
|
||||
- Updated the Contribution guide about translations.
|
||||
|
||||
- Better handling for articles update. (See #169)
|
||||
|
||||
- Ability to change the article viewer content font size (see #153)
|
||||
|
||||
- Versions updates * 2.
|
||||
|
||||
- Added padding to the recyclerview.
|
||||
|
||||
**1.5.5.x (didn't last long) AND 1.5.6.x**
|
||||
|
||||
@ -20,6 +64,8 @@
|
||||
|
||||
- Changed versions handling.
|
||||
|
||||
- Removed indonesian english as it was causing issues with the english version of the app.
|
||||
|
||||
**1.5.4.22**
|
||||
|
||||
- You can now scroll through the loaded articles !
|
||||
|
21
README.md
@ -1,17 +1,20 @@
|
||||
# ReaderForSelfoss
|
||||
|
||||
[](https://crowdin.com/project/readerforselfoss) [](https://gitter.im/amine-bou/ReaderForSelfoss)
|
||||
|
||||
[](http://jenkins.amine-bou.fr/job/ReaderForSelfoss/)
|
||||
|
||||
[](https://www.codetriage.com/aminecmi/readerforselfoss)
|
||||
|
||||
This is the repo of [Reader For Selfoss](https://play.google.com/store/apps/details?id=apps.amine.bou.readerforselfoss&hl=en).
|
||||
[](https://join.slack.com/t/readerforselfoss/shared_invite/enQtMjkyNzc3NjM2Mjc1LTUzZTZhOGM5YjQ1MTI5MWZiODRjMjE1ZDBmMzQxZmQ3NWZhYTNhMTBjNGEwNmE2ZGFjODU5NjUxZjBkMWJmMDQ) [](https://jenkins.amine-bou.fr/job/ReaderForSelfoss/) [](https://www.codetriage.com/aminecmi/readerforselfoss) [](https://crowdin.com/project/readerforselfoss)
|
||||
|
||||
It's an RSS Reader for Android, that **only** works with [Selfoss](https://selfoss.aditu.de/)
|
||||
|
||||
The last APK built from source is available [here](https://jenkins.amine-bou.fr/job/ReaderForSelfoss/lastSuccessfulBuild/artifact/SignApksBuilder-out/selfoss-key/selfoss/app-githubConfig-release-unsigned.apk/app-githubConfig-release.apk).
|
||||
<a href='https://play.google.com/store/apps/details?id=apps.amine.bou.readerforselfoss'><img alt='Get it on Google Play' src='https://play.google.com/intl/en_us/badges/images/generic/en_badge_web_generic.png' height="100"/></a> <a href="https://f-droid.org/packages/apps.amine.bou.readerforselfoss"><img src="https://f-droid.org/badge/get-it-on.png" alt="Get it on F-Droid" height="100"></a>
|
||||
|
||||
Also, the last APK built from source is available [here](https://jenkins.amine-bou.fr/job/ReaderForSelfoss/lastSuccessfulBuild/artifact/SignApksBuilder-out/selfoss-key/selfoss/app-githubConfig-release-unsigned.apk/app-githubConfig-release.apk).
|
||||
|
||||
## Join the alpha channel
|
||||
|
||||
**Keep in mind, it could be instable, but you'll have the new updates faster**
|
||||
|
||||
- First, join the google [group](https://groups.google.com/d/forum/reader-for-selfoss-alpha-testing).
|
||||
- Then, join the [alpha channel](https://play.google.com/apps/testing/apps.amine.bou.readerforselfoss) of the app.
|
||||
- You'll be able to update the app for the current alpha version.
|
||||
|
||||
## Want to help ?
|
||||
|
||||
@ -23,4 +26,4 @@ Check the [Contribution guide](https://github.com/aminecmi/ReaderforSelfoss/blob
|
||||
- [See what I'm doing](https://github.com/aminecmi/ReaderforSelfoss/projects/1)
|
||||
- [Create an issue, or request a new feature](https://github.com/aminecmi/ReaderforSelfoss/issues)
|
||||
- [Help translation the app](https://crowdin.com/project/readerforselfoss)
|
||||
- [Ask for help](https://gitter.im/amine-bou/ReaderForSelfoss)
|
||||
- [Ask for help](https://join.slack.com/t/readerforselfoss/shared_invite/enQtMjkyNzc3NjM2Mjc1LTUzZTZhOGM5YjQ1MTI5MWZiODRjMjE1ZDBmMzQxZmQ3NWZhYTNhMTBjNGEwNmE2ZGFjODU5NjUxZjBkMWJmMDQ)
|
||||
|
179
app/build.gradle
@ -1,17 +1,12 @@
|
||||
buildscript {
|
||||
repositories {
|
||||
maven { url 'https://maven.fabric.io/public' }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'io.fabric.tools:gradle:1.+'
|
||||
}
|
||||
}
|
||||
|
||||
ext {
|
||||
configuration = [
|
||||
buildDate: new Date()
|
||||
]
|
||||
// This will make me able to build multiple times a day. May break thinks. I may forget it.
|
||||
todaysBuilds = "1"
|
||||
}
|
||||
|
||||
def gitVersion() {
|
||||
@ -20,40 +15,34 @@ def gitVersion() {
|
||||
}
|
||||
|
||||
def versionCodeFromGit() {
|
||||
def versionCode = gitVersion() + (ext.configuration.buildDate.format("yyMMD")).toInteger()
|
||||
println "version code " + versionCode
|
||||
return versionCode.toInteger()
|
||||
println "version code " + gitVersion()
|
||||
return gitVersion().toInteger()
|
||||
}
|
||||
|
||||
def versionNameFromGit() {
|
||||
def versionName = gitVersion() + ext.configuration.buildDate.format('yyyyMMddHHmm')
|
||||
println "version name " + versionName
|
||||
return versionName
|
||||
println "version name " + gitVersion()
|
||||
return gitVersion()
|
||||
}
|
||||
|
||||
apply plugin: 'org.sonarqube'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
apply plugin: 'io.fabric'
|
||||
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
url 'https://maven.fabric.io/public'
|
||||
}
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 27
|
||||
buildToolsVersion '27.0.2'
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
compileSdkVersion 28
|
||||
buildToolsVersion '28.0.3'
|
||||
defaultConfig {
|
||||
applicationId "apps.amine.bou.readerforselfoss"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 27
|
||||
targetSdkVersion 28
|
||||
versionCode versionCodeFromGit()
|
||||
versionName versionNameFromGit()
|
||||
|
||||
@ -66,14 +55,14 @@ android {
|
||||
vectorDrawables.useSupportLibrary = true
|
||||
|
||||
// tests
|
||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
|
||||
buildConfigField "String", "MERCURY_KEY", mercuryApiKey
|
||||
buildConfigField "String", "FEEDBACK_EMAIL", feedbackEmail
|
||||
buildConfigField "String", "SOURCE_URL", sourceUrl
|
||||
buildConfigField "String", "TRACKER_URL", trackerUrl
|
||||
buildConfigField "String", "TRANSLATION_URL", translationUrl
|
||||
buildConfigField "String", "GITHUB_TOKEN", githubToken
|
||||
javaCompileOptions {
|
||||
annotationProcessorOptions {
|
||||
arguments = ["room.schemaLocation":
|
||||
"$projectDir/schemas".toString()]
|
||||
}
|
||||
}
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
@ -86,6 +75,7 @@ android {
|
||||
buildConfigField "String", "LOGIN_URL", appLoginUrl
|
||||
buildConfigField "String", "LOGIN_USERNAME", appLoginUsername
|
||||
buildConfigField "String", "LOGIN_PASSWORD", appLoginPassword
|
||||
applicationIdSuffix ".dev"
|
||||
}
|
||||
}
|
||||
flavorDimensions "build"
|
||||
@ -93,112 +83,88 @@ android {
|
||||
githubConfig {
|
||||
versionNameSuffix '-github'
|
||||
dimension "build"
|
||||
buildConfigField "boolean", "GITHUB_VERSION", "true"
|
||||
}
|
||||
storeConfig {
|
||||
// As jenkins publishes to alpha first, this is the default suffix now.
|
||||
versionNameSuffix '-store'
|
||||
dimension "build"
|
||||
buildConfigField "boolean", "GITHUB_VERSION", "false"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
// Testing
|
||||
androidTestCompile 'com.android.support.test.espresso:espresso-core:3.0.1'
|
||||
androidTestCompile 'com.android.support.test:runner:1.0.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0-beta02'
|
||||
androidTestImplementation 'androidx.test:runner:1.1.0-beta02'
|
||||
// Espresso-contrib for DatePicker, RecyclerView, Drawer actions, Accessibility checks, CountingIdlingResource
|
||||
androidTestCompile 'com.android.support.test.espresso:espresso-contrib:3.0.1'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.1.0-beta02'
|
||||
// Espresso-intents for validation and stubbing of Intents
|
||||
androidTestCompile 'com.android.support.test.espresso:espresso-intents:3.0.1'
|
||||
|
||||
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
|
||||
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.0-beta02'
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
// Android Support
|
||||
compile 'com.android.support:appcompat-v7:27.0.2'
|
||||
compile 'com.android.support:design:27.0.2'
|
||||
compile 'com.android.support:recyclerview-v7:27.0.2'
|
||||
compile 'com.android.support:support-v4:27.0.2'
|
||||
compile 'com.android.support:support-vector-drawable:27.0.2'
|
||||
compile 'com.android.support:customtabs:27.0.2'
|
||||
compile 'com.android.support:cardview-v7:27.0.2'
|
||||
compile 'com.android.support.constraint:constraint-layout:1.0.2'
|
||||
|
||||
// Firebase + crashlytics
|
||||
compile 'com.google.firebase:firebase-core:11.6.2'
|
||||
compile 'com.google.firebase:firebase-config:11.6.2'
|
||||
compile 'com.google.firebase:firebase-invites:11.6.2'
|
||||
compile('com.crashlytics.sdk.android:crashlytics:2.8.0@aar') {
|
||||
transitive = true;
|
||||
}
|
||||
implementation "androidx.appcompat:appcompat:$android_version"
|
||||
implementation "com.google.android.material:material:$android_version"
|
||||
implementation "androidx.recyclerview:recyclerview:$android_version"
|
||||
implementation "androidx.legacy:legacy-support-v4:$android_version"
|
||||
implementation "androidx.vectordrawable:vectordrawable:$android_version"
|
||||
implementation "androidx.browser:browser:$android_version"
|
||||
implementation "androidx.cardview:cardview:$android_version"
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2'
|
||||
|
||||
//multidex
|
||||
compile 'com.android.support:multidex:1.0.2'
|
||||
|
||||
// Intro
|
||||
compile 'agency.tango.android:material-intro-screen:0.0.5'
|
||||
implementation 'androidx.multidex:multidex:2.0.0'
|
||||
|
||||
// About
|
||||
compile('com.mikepenz:aboutlibraries:6.0.0@aar') {
|
||||
implementation('com.mikepenz:aboutlibraries:6.2.0@aar') {
|
||||
transitive = true
|
||||
}
|
||||
|
||||
// Retrofit + http logging + okhttp
|
||||
compile 'com.squareup.retrofit2:retrofit:2.3.0'
|
||||
compile 'com.squareup.okhttp3:logging-interceptor:3.9.0'
|
||||
compile 'com.squareup.retrofit2:converter-gson:2.3.0'
|
||||
compile 'com.burgstaller:okhttp-digest:1.12'
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.3.0'
|
||||
implementation 'com.squareup.okhttp3:logging-interceptor:3.9.0'
|
||||
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
|
||||
implementation 'com.burgstaller:okhttp-digest:1.12'
|
||||
|
||||
// Material-ish things
|
||||
compile 'com.ashokvarma.android:bottom-navigation-bar:2.0.3'
|
||||
compile 'com.github.jd-alexander:LikeButton:0.2.1'
|
||||
compile 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
|
||||
compile 'org.sufficientlysecure:html-textview:3.5'
|
||||
implementation 'com.ashokvarma.android:bottom-navigation-bar:2.0.5'
|
||||
implementation 'com.github.jd-alexander:LikeButton:0.2.3'
|
||||
implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
|
||||
|
||||
// glide
|
||||
compile 'com.github.bumptech.glide:glide:4.1.1'
|
||||
compile 'com.github.bumptech.glide:okhttp3-integration:4.1.1'
|
||||
implementation 'com.github.bumptech.glide:glide:4.1.1'
|
||||
implementation 'com.github.bumptech.glide:okhttp3-integration:4.1.1'
|
||||
|
||||
// Asking politely users to rate the app
|
||||
compile 'com.github.stkent:amplify:2.1.0'
|
||||
implementation 'com.github.stkent:amplify:2.2.0'
|
||||
|
||||
// Drawer
|
||||
implementation 'co.zsmb:materialdrawer-kt:1.2.1'
|
||||
compile 'com.anupcowkur:reservoir:3.1.0'
|
||||
implementation 'co.zsmb:materialdrawer-kt:2.0.1'
|
||||
|
||||
// Themes
|
||||
compile 'com.52inc:scoops:1.0.0'
|
||||
|
||||
// Github issues reporter
|
||||
compile 'com.heinrichreimersoftware:android-issue-reporter:1.3.1'
|
||||
|
||||
compile 'com.github.rubensousa:floatingtoolbar:1.5.1'
|
||||
implementation 'com.52inc:scoops:1.0.0'
|
||||
implementation 'com.jaredrummler:colorpicker:1.0.2'
|
||||
implementation 'com.github.rubensousa:floatingtoolbar:1.5.1'
|
||||
|
||||
// Pager
|
||||
compile 'me.relex:circleindicator:1.2.2@aar'
|
||||
implementation 'me.relex:circleindicator:2.0.0@aar'
|
||||
|
||||
implementation 'androidx.core:core-ktx:1.0.0'
|
||||
|
||||
// Crash
|
||||
implementation 'ch.acra:acra-http:5.1.3'
|
||||
implementation 'ch.acra:acra-dialog:5.1.3'
|
||||
|
||||
implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
|
||||
|
||||
implementation "androidx.room:room-runtime:$room_version"
|
||||
kapt "androidx.room:room-compiler:$room_version"
|
||||
}
|
||||
|
||||
apply plugin: 'com.google.gms.google-services'
|
||||
|
||||
|
||||
afterEvaluate {
|
||||
initFabricPropertiesIfNeeded()
|
||||
initAppLoginPropertiesIfNeeded()
|
||||
initAppForSecretPropertiesIfNeeded()
|
||||
}
|
||||
|
||||
def initFabricPropertiesIfNeeded() {
|
||||
def propertiesFile = file('fabric.properties')
|
||||
if (!propertiesFile.exists()) {
|
||||
def commentMessage = "This is autogenerated fabric property from system environment to prevent key to be committed to source control."
|
||||
ant.propertyfile(file: "fabric.properties", comment: commentMessage) {
|
||||
entry(key: "apiSecret", value: crashlyticsdemoApisecret)
|
||||
entry(key: "apiKey", value: crashlyticsdemoApikey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def initAppLoginPropertiesIfNeeded() {
|
||||
@ -212,18 +178,3 @@ def initAppLoginPropertiesIfNeeded() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def initAppForSecretPropertiesIfNeeded() {
|
||||
def propertiesFile = file(System.getProperty("user.home") + '/.gradle/gradle.properties')
|
||||
if (!propertiesFile.exists()) {
|
||||
def commentMessage = "This is autogenerated local property from system environment to prevent key to be committed to source control."
|
||||
ant.propertyfile(file: System.getProperty("user.home") + "/.gradle/gradle.properties", comment: commentMessage) {
|
||||
entry(key: "mercuryApiKey", value: System.getProperty("mercuryApiKey"))
|
||||
entry(key: "feedbackEmail", value: System.getProperty("feedbackEmail"))
|
||||
entry(key: "sourceUrl", value: System.getProperty("sourceUrl"))
|
||||
entry(key: "trackerUrl", value: System.getProperty("trackerUrl"))
|
||||
entry(key: "translationUrl", value: System.getProperty("translationUrl"))
|
||||
entry(key: "githubToken", value: System.getProperty("githubToken"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
6
app/proguard-rules.pro
vendored
@ -48,7 +48,11 @@
|
||||
#}
|
||||
-dontwarn okio.**
|
||||
-dontwarn retrofit2.Platform$Java8
|
||||
-keepattributes Signature
|
||||
-keep class retrofit.** { *; }
|
||||
-keepclasseswithmembers class * {
|
||||
@retrofit.http.* <methods>;
|
||||
}
|
||||
-keepattributes *Annotation*,Signature
|
||||
-keepattributes Exceptions
|
||||
-dontwarn okio.**
|
||||
-dontwarn javax.annotation.Nullable
|
||||
|
@ -0,0 +1,96 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 1,
|
||||
"identityHash": "08ca537d7ac9d4dd216e8e395d70801a",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "tags",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tag` TEXT NOT NULL, `color` TEXT NOT NULL, `unread` INTEGER NOT NULL, PRIMARY KEY(`tag`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "tag",
|
||||
"columnName": "tag",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "color",
|
||||
"columnName": "color",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unread",
|
||||
"columnName": "unread",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"tag"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "sources",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `title` TEXT NOT NULL, `tags` TEXT NOT NULL, `spout` TEXT NOT NULL, `error` TEXT NOT NULL, `icon` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tags",
|
||||
"columnName": "tags",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "spout",
|
||||
"columnName": "spout",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "error",
|
||||
"columnName": "error",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "icon",
|
||||
"columnName": "icon",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"08ca537d7ac9d4dd216e8e395d70801a\")"
|
||||
]
|
||||
}
|
||||
}
|
@ -0,0 +1,176 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 2,
|
||||
"identityHash": "6fa6944b04100d68eab61039876a8804",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "tags",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tag` TEXT NOT NULL, `color` TEXT NOT NULL, `unread` INTEGER NOT NULL, PRIMARY KEY(`tag`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "tag",
|
||||
"columnName": "tag",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "color",
|
||||
"columnName": "color",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unread",
|
||||
"columnName": "unread",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"tag"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "sources",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `title` TEXT NOT NULL, `tags` TEXT NOT NULL, `spout` TEXT NOT NULL, `error` TEXT NOT NULL, `icon` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tags",
|
||||
"columnName": "tags",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "spout",
|
||||
"columnName": "spout",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "error",
|
||||
"columnName": "error",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "icon",
|
||||
"columnName": "icon",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "items",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `datetime` TEXT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `unread` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `thumbnail` TEXT NOT NULL, `icon` TEXT NOT NULL, `link` TEXT NOT NULL, `sourcetitle` TEXT NOT NULL, `tags` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "datetime",
|
||||
"columnName": "datetime",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "content",
|
||||
"columnName": "content",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unread",
|
||||
"columnName": "unread",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbnail",
|
||||
"columnName": "thumbnail",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "icon",
|
||||
"columnName": "icon",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "link",
|
||||
"columnName": "link",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "sourcetitle",
|
||||
"columnName": "sourcetitle",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tags",
|
||||
"columnName": "tags",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"6fa6944b04100d68eab61039876a8804\")"
|
||||
]
|
||||
}
|
||||
}
|
@ -2,30 +2,29 @@ package apps.amine.bou.readerforselfoss
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.support.test.InstrumentationRegistry
|
||||
import android.support.test.espresso.Espresso.onView
|
||||
import android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
||||
import android.support.test.espresso.action.ViewActions.click
|
||||
import android.support.test.espresso.action.ViewActions.closeSoftKeyboard
|
||||
import android.support.test.espresso.action.ViewActions.pressBack
|
||||
import android.support.test.espresso.action.ViewActions.pressKey
|
||||
import android.support.test.espresso.action.ViewActions.typeText
|
||||
import android.support.test.espresso.assertion.ViewAssertions.matches
|
||||
import android.support.test.espresso.contrib.DrawerActions
|
||||
import android.support.test.espresso.intent.Intents
|
||||
import android.support.test.espresso.intent.Intents.intended
|
||||
import android.support.test.espresso.intent.Intents.times
|
||||
import android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
||||
import android.support.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||
import android.support.test.espresso.matcher.ViewMatchers.isRoot
|
||||
import android.support.test.espresso.matcher.ViewMatchers.withContentDescription
|
||||
import android.support.test.espresso.matcher.ViewMatchers.withId
|
||||
import android.support.test.espresso.matcher.ViewMatchers.withText
|
||||
import android.support.test.rule.ActivityTestRule
|
||||
import android.support.test.runner.AndroidJUnit4
|
||||
import androidx.test.InstrumentationRegistry
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
||||
import androidx.test.espresso.action.ViewActions.click
|
||||
import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
|
||||
import androidx.test.espresso.action.ViewActions.pressBack
|
||||
import androidx.test.espresso.action.ViewActions.pressKey
|
||||
import androidx.test.espresso.action.ViewActions.typeText
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.contrib.DrawerActions
|
||||
import androidx.test.espresso.intent.Intents
|
||||
import androidx.test.espresso.intent.Intents.intended
|
||||
import androidx.test.espresso.intent.Intents.times
|
||||
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isRoot
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||
import androidx.test.rule.ActivityTestRule
|
||||
import androidx.test.runner.AndroidJUnit4
|
||||
import android.view.KeyEvent
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import com.heinrichreimersoftware.androidissuereporter.IssueReporterLauncher
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
@ -92,25 +91,6 @@ class HomeActivityEspressoTest {
|
||||
intended(hasComponent(LoginActivity::class.java.name), times(1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun drawerTesting() {
|
||||
|
||||
rule.launchActivity(Intent())
|
||||
|
||||
onView(withId(R.id.material_drawer_layout)).perform(DrawerActions.open())
|
||||
|
||||
onView(withText(R.string.drawer_report_bug)).perform(click())
|
||||
intended(hasComponent(IssueReporterLauncher.Activity::class.java.name))
|
||||
onView(isRoot()).perform(pressBack())
|
||||
onView(isRoot()).perform(pressBack())
|
||||
intended(hasComponent(HomeActivity::class.java.name))
|
||||
|
||||
onView(withId(R.id.material_drawer_layout)).perform(DrawerActions.open())
|
||||
|
||||
onView(withId(R.id.material_drawer_layout)).perform(DrawerActions.open())
|
||||
onView(withText(R.string.drawer_action_clear)).perform(click())
|
||||
}
|
||||
|
||||
// TODO: test articles opening and actions for cards and lists
|
||||
|
||||
@After
|
||||
|
@ -1,91 +0,0 @@
|
||||
package apps.amine.bou.readerforselfoss
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.support.test.InstrumentationRegistry.getInstrumentation
|
||||
import android.support.test.espresso.Espresso.onView
|
||||
import android.support.test.espresso.action.ViewActions.click
|
||||
import android.support.test.espresso.assertion.ViewAssertions.matches
|
||||
import android.support.test.espresso.intent.Intents
|
||||
import android.support.test.espresso.intent.Intents.intended
|
||||
import android.support.test.espresso.intent.Intents.times
|
||||
import android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
||||
import android.support.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||
import android.support.test.espresso.matcher.ViewMatchers.withId
|
||||
import android.support.test.espresso.matcher.ViewMatchers.withText
|
||||
import android.support.test.rule.ActivityTestRule
|
||||
import android.support.test.runner.AndroidJUnit4
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.util.*
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class IntroActivityEspressoTest {
|
||||
|
||||
@Rule @JvmField
|
||||
val rule = ActivityTestRule(IntroActivity::class.java, true, false)
|
||||
|
||||
@Before
|
||||
fun clearData() {
|
||||
val editor =
|
||||
getInstrumentation().targetContext
|
||||
.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||
.edit()
|
||||
editor.clear()
|
||||
editor.commit()
|
||||
|
||||
Intents.init()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nextEachTimes() {
|
||||
|
||||
rule.launchActivity(Intent())
|
||||
|
||||
onView(withText(R.string.intro_hello_title)).check(matches(isDisplayed()))
|
||||
onView(withId(R.id.button_next)).perform(click())
|
||||
onView(withText(R.string.intro_needs_selfoss_message)).check(matches(isDisplayed()))
|
||||
onView(withId(R.id.button_next)).perform(click())
|
||||
onView(withText(R.string.intro_all_set_message)).check(matches(isDisplayed()))
|
||||
onView(withId(R.id.button_next)).perform(click())
|
||||
|
||||
intended(hasComponent(IntroActivity::class.java.name), times(1))
|
||||
intended(hasComponent(LoginActivity::class.java.name), times(1))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun nextBackRandomTimes() {
|
||||
val max = 5
|
||||
val min = 1
|
||||
|
||||
val random = (Random().nextInt(max + 1 - min)) + min
|
||||
|
||||
rule.launchActivity(Intent())
|
||||
|
||||
onView(withText(R.string.intro_hello_title)).check(matches(isDisplayed()))
|
||||
onView(withId(R.id.button_next)).perform(click())
|
||||
|
||||
repeat(random) { _ ->
|
||||
onView(withText(R.string.intro_needs_selfoss_message)).check(matches(isDisplayed()))
|
||||
onView(withId(R.id.button_next)).perform(click())
|
||||
onView(withText(R.string.intro_all_set_message)).check(matches(isDisplayed()))
|
||||
onView(withId(R.id.button_back)).perform(click())
|
||||
}
|
||||
|
||||
onView(withId(R.id.button_next)).perform(click())
|
||||
onView(withText(R.string.intro_all_set_message)).check(matches(isDisplayed()))
|
||||
onView(withId(R.id.button_next)).perform(click())
|
||||
|
||||
intended(hasComponent(IntroActivity::class.java.name), times(1))
|
||||
intended(hasComponent(LoginActivity::class.java.name), times(1))
|
||||
}
|
||||
|
||||
@After
|
||||
fun releaseIntents() {
|
||||
Intents.release()
|
||||
}
|
||||
}
|
@ -2,25 +2,25 @@ package apps.amine.bou.readerforselfoss
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.support.test.InstrumentationRegistry
|
||||
import android.support.test.espresso.Espresso.onView
|
||||
import android.support.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
||||
import android.support.test.espresso.action.ViewActions.click
|
||||
import android.support.test.espresso.action.ViewActions.closeSoftKeyboard
|
||||
import android.support.test.espresso.action.ViewActions.pressBack
|
||||
import android.support.test.espresso.action.ViewActions.typeText
|
||||
import android.support.test.espresso.assertion.ViewAssertions.matches
|
||||
import android.support.test.espresso.intent.Intents
|
||||
import android.support.test.espresso.intent.Intents.intended
|
||||
import android.support.test.espresso.intent.Intents.times
|
||||
import android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
||||
import android.support.test.espresso.matcher.ViewMatchers
|
||||
import android.support.test.espresso.matcher.ViewMatchers.isRoot
|
||||
import android.support.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
|
||||
import android.support.test.espresso.matcher.ViewMatchers.withId
|
||||
import android.support.test.espresso.matcher.ViewMatchers.withText
|
||||
import android.support.test.rule.ActivityTestRule
|
||||
import android.support.test.runner.AndroidJUnit4
|
||||
import androidx.test.InstrumentationRegistry
|
||||
import androidx.test.espresso.Espresso.onView
|
||||
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
||||
import androidx.test.espresso.action.ViewActions.click
|
||||
import androidx.test.espresso.action.ViewActions.closeSoftKeyboard
|
||||
import androidx.test.espresso.action.ViewActions.pressBack
|
||||
import androidx.test.espresso.action.ViewActions.typeText
|
||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||
import androidx.test.espresso.intent.Intents
|
||||
import androidx.test.espresso.intent.Intents.intended
|
||||
import androidx.test.espresso.intent.Intents.times
|
||||
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
||||
import androidx.test.espresso.matcher.ViewMatchers
|
||||
import androidx.test.espresso.matcher.ViewMatchers.isRoot
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withId
|
||||
import androidx.test.espresso.matcher.ViewMatchers.withText
|
||||
import androidx.test.rule.ActivityTestRule
|
||||
import androidx.test.runner.AndroidJUnit4
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import com.mikepenz.aboutlibraries.ui.LibsActivity
|
||||
import org.junit.After
|
||||
|
@ -3,13 +3,13 @@ package apps.amine.bou.readerforselfoss
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.test.InstrumentationRegistry.getInstrumentation
|
||||
import android.support.test.espresso.intent.Intents
|
||||
import android.support.test.espresso.intent.Intents.intended
|
||||
import android.support.test.espresso.intent.Intents.times
|
||||
import android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
||||
import android.support.test.rule.ActivityTestRule
|
||||
import android.support.test.runner.AndroidJUnit4
|
||||
import androidx.test.InstrumentationRegistry.getInstrumentation
|
||||
import androidx.test.espresso.intent.Intents
|
||||
import androidx.test.espresso.intent.Intents.intended
|
||||
import androidx.test.espresso.intent.Intents.times
|
||||
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
||||
import androidx.test.rule.ActivityTestRule
|
||||
import androidx.test.runner.AndroidJUnit4
|
||||
import org.junit.After
|
||||
|
||||
import org.junit.Before
|
||||
@ -45,7 +45,6 @@ class MainActivityEspressoTest {
|
||||
rule.launchActivity(intent)
|
||||
|
||||
intended(hasComponent(MainActivity::class.java.name))
|
||||
intended(hasComponent(IntroActivity::class.java.name))
|
||||
intended(hasComponent(LoginActivity::class.java.name), times(0))
|
||||
}
|
||||
|
||||
@ -58,7 +57,6 @@ class MainActivityEspressoTest {
|
||||
|
||||
intended(hasComponent(MainActivity::class.java.name))
|
||||
intended(hasComponent(LoginActivity::class.java.name))
|
||||
intended(hasComponent(IntroActivity::class.java.name), times(0))
|
||||
}
|
||||
|
||||
@After
|
||||
|
@ -1,7 +1,7 @@
|
||||
package apps.amine.bou.readerforselfoss
|
||||
|
||||
import android.support.design.widget.TextInputLayout
|
||||
import android.support.test.espresso.matcher.ViewMatchers
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import androidx.test.espresso.matcher.ViewMatchers
|
||||
import android.view.View
|
||||
import org.hamcrest.Description
|
||||
import org.hamcrest.Matcher
|
||||
|
@ -1,13 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="apps.amine.bou.readerforselfoss">
|
||||
package="apps.amine.bou.readerforselfoss"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<!-- For firebase only -->
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
|
||||
<application
|
||||
android:name=".MyApp"
|
||||
android:allowBackup="true"
|
||||
@ -24,10 +21,6 @@
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".IntroActivity"
|
||||
android:theme="@style/Theme.Intro">
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".LoginActivity"
|
||||
android:label="@string/title_activity_login">
|
||||
@ -77,6 +70,8 @@
|
||||
|
||||
<meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
|
||||
android:value="true" />
|
||||
|
||||
<meta-data android:name="android.max_aspect" android:value="2.1" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
@ -1,10 +1,11 @@
|
||||
package apps.amine.bou.readerforselfoss
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.constraint.ConstraintLayout
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.view.View
|
||||
import android.widget.AdapterView
|
||||
import android.widget.ArrayAdapter
|
||||
@ -16,6 +17,8 @@ import android.widget.Toast
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Spout
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.themes.Toppings
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import apps.amine.bou.readerforselfoss.utils.isBaseUrlValid
|
||||
import com.ftinc.scoop.Scoop
|
||||
@ -23,22 +26,63 @@ import kotlinx.android.synthetic.main.activity_add_source.*
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import android.graphics.PorterDuff
|
||||
|
||||
|
||||
|
||||
class AddSourceActivity : AppCompatActivity() {
|
||||
|
||||
private var mSpoutsValue: String? = null
|
||||
private lateinit var api: SelfossApi
|
||||
|
||||
private lateinit var appColors: AppColors
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
appColors = AppColors(this@AddSourceActivity)
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
Scoop.getInstance().apply(this)
|
||||
|
||||
setContentView(R.layout.activity_add_source)
|
||||
|
||||
val scoop = Scoop.getInstance()
|
||||
scoop.bind(this, Toppings.PRIMARY.value, toolbar)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
|
||||
}
|
||||
|
||||
val drawable = nameInput.background
|
||||
drawable.setColorFilter(appColors.colorAccent, PorterDuff.Mode.SRC_ATOP)
|
||||
|
||||
|
||||
// TODO: clean
|
||||
if(Build.VERSION.SDK_INT > 16) {
|
||||
nameInput.background = drawable
|
||||
} else{
|
||||
nameInput.setBackgroundDrawable(drawable)
|
||||
}
|
||||
|
||||
val drawable1 = sourceUri.background
|
||||
drawable1.setColorFilter(appColors.colorAccent, PorterDuff.Mode.SRC_ATOP)
|
||||
|
||||
if(Build.VERSION.SDK_INT > 16) {
|
||||
sourceUri.background = drawable1
|
||||
} else{
|
||||
sourceUri.setBackgroundDrawable(drawable1)
|
||||
}
|
||||
|
||||
val drawable2 = tags.background
|
||||
drawable2.setColorFilter(appColors.colorAccent, PorterDuff.Mode.SRC_ATOP)
|
||||
|
||||
if(Build.VERSION.SDK_INT > 16) {
|
||||
tags.background = drawable2
|
||||
} else{
|
||||
tags.setBackgroundDrawable(drawable2)
|
||||
}
|
||||
|
||||
setSupportActionBar(toolbar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||
|
||||
var api: SelfossApi? = null
|
||||
|
||||
try {
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
api = SelfossApi(
|
||||
@ -53,10 +97,15 @@ class AddSourceActivity : AppCompatActivity() {
|
||||
|
||||
maybeGetDetailsFromIntentSharing(intent, sourceUri, nameInput)
|
||||
|
||||
saveBtn.setTextColor(appColors.colorAccent)
|
||||
|
||||
saveBtn.setOnClickListener {
|
||||
handleSaveSource(tags, nameInput.text.toString(), sourceUri.text.toString(), api!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
val config = Config(this)
|
||||
|
||||
if (config.baseUrl.isEmpty() || !config.baseUrl.isBaseUrlValid()) {
|
||||
@ -74,10 +123,12 @@ class AddSourceActivity : AppCompatActivity() {
|
||||
) {
|
||||
val spoutsKV = HashMap<String, String>()
|
||||
spoutsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||
override fun onItemSelected(adapterView: AdapterView<*>, view: View, i: Int, l: Long) {
|
||||
override fun onItemSelected(adapterView: AdapterView<*>, view: View?, i: Int, l: Long) {
|
||||
if (view != null) {
|
||||
val spoutName = (view as TextView).text.toString()
|
||||
mSpoutsValue = spoutsKV[spoutName]
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNothingSelected(adapterView: AdapterView<*>) {
|
||||
mSpoutsValue = null
|
||||
@ -95,7 +146,7 @@ class AddSourceActivity : AppCompatActivity() {
|
||||
|
||||
val itemsStrings = items.map { it.value.name }
|
||||
for ((key, value) in items) {
|
||||
spoutsKV.put(value.name, key)
|
||||
spoutsKV[value.name] = key
|
||||
}
|
||||
|
||||
mProgress.visibility = View.GONE
|
||||
@ -149,7 +200,8 @@ class AddSourceActivity : AppCompatActivity() {
|
||||
|
||||
private fun handleSaveSource(tags: EditText, title: String, url: String, api: SelfossApi) {
|
||||
|
||||
val sourceDetailsAvailable = title.isEmpty() || url.isEmpty() || mSpoutsValue == null || mSpoutsValue!!.isEmpty()
|
||||
val sourceDetailsAvailable =
|
||||
title.isEmpty() || url.isEmpty() || mSpoutsValue == null || mSpoutsValue!!.isEmpty()
|
||||
|
||||
if (sourceDetailsAvailable) {
|
||||
Toast.makeText(this, R.string.form_not_complete, Toast.LENGTH_SHORT).show()
|
||||
|
@ -1,70 +0,0 @@
|
||||
package apps.amine.bou.readerforselfoss
|
||||
|
||||
import agency.tango.materialintroscreen.MaterialIntroActivity
|
||||
import agency.tango.materialintroscreen.MessageButtonBehaviour
|
||||
import agency.tango.materialintroscreen.SlideFragmentBuilder
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.v7.app.AppCompatDelegate
|
||||
import android.view.View
|
||||
|
||||
class IntroActivity : MaterialIntroActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||
|
||||
addSlide(
|
||||
SlideFragmentBuilder()
|
||||
.backgroundColor(R.color.colorPrimary)
|
||||
.buttonsColor(R.color.colorAccent)
|
||||
.image(R.drawable.web_hi_res_512)
|
||||
.title(getString(R.string.intro_hello_title))
|
||||
.description(getString(R.string.intro_hello_message))
|
||||
.build()
|
||||
)
|
||||
|
||||
addSlide(
|
||||
SlideFragmentBuilder()
|
||||
.backgroundColor(R.color.colorAccent)
|
||||
.buttonsColor(R.color.colorPrimary)
|
||||
.image(R.drawable.ic_info_outline_white_48px)
|
||||
.title(getString(R.string.intro_needs_selfoss_title))
|
||||
.description(getString(R.string.intro_needs_selfoss_message))
|
||||
.build(),
|
||||
MessageButtonBehaviour(
|
||||
View.OnClickListener {
|
||||
val browserIntent = Intent(
|
||||
Intent.ACTION_VIEW,
|
||||
Uri.parse("https://selfoss.aditu.de")
|
||||
)
|
||||
startActivity(browserIntent)
|
||||
}, getString(R.string.intro_needs_selfoss_link)
|
||||
)
|
||||
)
|
||||
|
||||
addSlide(
|
||||
SlideFragmentBuilder()
|
||||
.backgroundColor(R.color.colorPrimaryDark)
|
||||
.buttonsColor(R.color.colorAccentDark)
|
||||
.image(R.drawable.ic_thumb_up_white_48px)
|
||||
.title(getString(R.string.intro_all_set_title))
|
||||
.description(getString(R.string.intro_all_set_message))
|
||||
.build()
|
||||
)
|
||||
}
|
||||
|
||||
override fun onFinish() {
|
||||
super.onFinish()
|
||||
val getPrefs = PreferenceManager.getDefaultSharedPreferences(baseContext)
|
||||
val e = getPrefs.edit()
|
||||
e.putBoolean("firstStart", false)
|
||||
e.apply()
|
||||
val intent = Intent(this, LoginActivity::class.java)
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
}
|
@ -6,8 +6,8 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Bundle
|
||||
import android.support.v7.app.AlertDialog
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.text.TextUtils
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
@ -17,14 +17,14 @@ import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import apps.amine.bou.readerforselfoss.utils.isBaseUrlValid
|
||||
import com.crashlytics.android.Crashlytics
|
||||
import com.ftinc.scoop.Scoop
|
||||
import com.google.firebase.analytics.FirebaseAnalytics
|
||||
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
|
||||
import com.mikepenz.aboutlibraries.Libs
|
||||
import com.mikepenz.aboutlibraries.LibsBuilder
|
||||
import kotlinx.android.synthetic.main.activity_login.*
|
||||
import org.acra.ACRA
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
@ -38,13 +38,15 @@ class LoginActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var settings: SharedPreferences
|
||||
private lateinit var editor: SharedPreferences.Editor
|
||||
private lateinit var firebaseAnalytics: FirebaseAnalytics
|
||||
private lateinit var userIdentifier: String
|
||||
private var logErrors: Boolean = false
|
||||
private lateinit var appColors: AppColors
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
appColors = AppColors(this@LoginActivity)
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
Scoop.getInstance().apply(this)
|
||||
|
||||
setContentView(R.layout.activity_login)
|
||||
|
||||
setSupportActionBar(toolbar)
|
||||
@ -62,8 +64,6 @@ class LoginActivity : AppCompatActivity() {
|
||||
goToMain()
|
||||
}
|
||||
|
||||
firebaseAnalytics = FirebaseAnalytics.getInstance(this)
|
||||
|
||||
handleActions()
|
||||
}
|
||||
|
||||
@ -210,9 +210,7 @@ class LoginActivity : AppCompatActivity() {
|
||||
httpLoginView.error = getString(R.string.wrong_infos)
|
||||
httpPasswordView.error = getString(R.string.wrong_infos)
|
||||
if (logErrors) {
|
||||
Crashlytics.setUserIdentifier(userIdentifier)
|
||||
Crashlytics.log(100, "LOGIN_DEBUG_ERRROR", t.message)
|
||||
Crashlytics.logException(t)
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(t, this@LoginActivity)
|
||||
Toast.makeText(
|
||||
this@LoginActivity,
|
||||
t.message,
|
||||
@ -227,7 +225,6 @@ class LoginActivity : AppCompatActivity() {
|
||||
response: Response<SuccessResponse>
|
||||
) {
|
||||
if (response.body() != null && response.body()!!.isSuccess) {
|
||||
firebaseAnalytics.logEvent(FirebaseAnalytics.Event.LOGIN, Bundle())
|
||||
goToMain()
|
||||
} else {
|
||||
preferenceError(Exception("No response body..."))
|
||||
|
@ -3,7 +3,7 @@ package apps.amine.bou.readerforselfoss
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
@ -11,17 +11,9 @@ class MainActivity : AppCompatActivity() {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
if (PreferenceManager.getDefaultSharedPreferences(baseContext).getBoolean(
|
||||
"firstStart",
|
||||
true
|
||||
)) {
|
||||
val i = Intent(this@MainActivity, IntroActivity::class.java)
|
||||
startActivity(i)
|
||||
} else {
|
||||
val intent = Intent(this, LoginActivity::class.java)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
||||
startActivity(intent)
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
@ -4,33 +4,49 @@ import android.content.Context
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.multidex.MultiDexApplication
|
||||
import androidx.multidex.MultiDexApplication
|
||||
import android.widget.ImageView
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import com.anupcowkur.reservoir.Reservoir
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.crashlytics.android.Crashlytics
|
||||
import com.ftinc.scoop.Scoop
|
||||
import com.github.stkent.amplify.feedback.DefaultEmailFeedbackCollector
|
||||
import com.github.stkent.amplify.feedback.GooglePlayStoreFeedbackCollector
|
||||
import com.github.stkent.amplify.tracking.Amplify
|
||||
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader
|
||||
import com.mikepenz.materialdrawer.util.DrawerImageLoader
|
||||
import io.fabric.sdk.android.Fabric
|
||||
import org.acra.ACRA
|
||||
import org.acra.ReportField
|
||||
import org.acra.annotation.AcraCore
|
||||
import org.acra.annotation.AcraDialog
|
||||
import org.acra.annotation.AcraHttpSender
|
||||
import org.acra.sender.HttpSender
|
||||
import java.io.IOException
|
||||
import java.util.UUID.randomUUID
|
||||
|
||||
|
||||
@AcraHttpSender(uri = "http://amine-bou.fr:5984/acra-selfoss/_design/acra-storage/_update/report",
|
||||
basicAuthLogin = "selfoss",
|
||||
basicAuthPassword = "selfoss",
|
||||
httpMethod = HttpSender.Method.PUT)
|
||||
@AcraDialog(resText = R.string.crash_dialog_text,
|
||||
resCommentPrompt = R.string.crash_dialog_comment,
|
||||
resTheme = android.R.style.Theme_DeviceDefault_Dialog)
|
||||
@AcraCore(reportContent = [ReportField.REPORT_ID, ReportField.INSTALLATION_ID,
|
||||
ReportField.APP_VERSION_CODE, ReportField.APP_VERSION_NAME,
|
||||
ReportField.BUILD, ReportField.ANDROID_VERSION, ReportField.BRAND, ReportField.PHONE_MODEL,
|
||||
ReportField.AVAILABLE_MEM_SIZE, ReportField.TOTAL_MEM_SIZE,
|
||||
ReportField.STACK_TRACE, ReportField.APPLICATION_LOG, ReportField.LOGCAT,
|
||||
ReportField.INITIAL_CONFIGURATION, ReportField.CRASH_CONFIGURATION, ReportField.IS_SILENT,
|
||||
ReportField.USER_APP_START_DATE, ReportField.USER_COMMENT, ReportField.USER_CRASH_DATE, ReportField.USER_EMAIL, ReportField.CUSTOM_DATA],
|
||||
buildConfigClass = BuildConfig::class)
|
||||
class MyApp : MultiDexApplication() {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
Fabric.with(this, Crashlytics())
|
||||
|
||||
initAmplify()
|
||||
|
||||
initCache()
|
||||
|
||||
val prefs = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||
if (prefs.getString("unique_id", "").isEmpty()) {
|
||||
val editor = prefs.edit()
|
||||
@ -45,21 +61,21 @@ class MyApp : MultiDexApplication() {
|
||||
tryToHandleBug()
|
||||
}
|
||||
|
||||
override fun attachBaseContext(base: Context?) {
|
||||
super.attachBaseContext(base)
|
||||
val prefs = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||
ACRA.init(this)
|
||||
ACRA.getErrorReporter().putCustomData("unique_id", prefs.getString("unique_id", ""))
|
||||
|
||||
}
|
||||
|
||||
private fun initAmplify() {
|
||||
Amplify.initSharedInstance(this)
|
||||
.setPositiveFeedbackCollectors(GooglePlayStoreFeedbackCollector())
|
||||
.setCriticalFeedbackCollectors(DefaultEmailFeedbackCollector(BuildConfig.FEEDBACK_EMAIL))
|
||||
.setCriticalFeedbackCollectors(DefaultEmailFeedbackCollector(Config.feedbackEmail))
|
||||
.applyAllDefaultRules()
|
||||
}
|
||||
|
||||
private fun initCache() {
|
||||
try {
|
||||
Reservoir.init(this, 8192) //in bytes
|
||||
} catch (e: IOException) {
|
||||
//failure
|
||||
}
|
||||
}
|
||||
|
||||
private fun initDrawerImageLoader() {
|
||||
DrawerImageLoader.init(object : AbstractDrawerImageLoader() {
|
||||
override fun set(
|
||||
@ -87,19 +103,7 @@ class MyApp : MultiDexApplication() {
|
||||
private fun initTheme() {
|
||||
Scoop.waffleCone()
|
||||
.addFlavor(getString(R.string.default_theme), R.style.NoBar, true)
|
||||
.addFlavor(getString(R.string.default_dark_theme), R.style.NoBarDark)
|
||||
.addFlavor(getString(R.string.teal_orange_theme), R.style.NoBarTealOrange)
|
||||
.addFlavor(getString(R.string.teal_orange_dark_theme), R.style.NoBarTealOrangeDark)
|
||||
.addFlavor(getString(R.string.cyan_pink_theme), R.style.NoBarCyanPink)
|
||||
.addFlavor(getString(R.string.cyan_pink_dark_theme), R.style.NoBarCyanPinkDark)
|
||||
.addFlavor(getString(R.string.grey_orange_theme), R.style.NoBarGreyOrange)
|
||||
.addFlavor(getString(R.string.grey_orange_dark_theme), R.style.NoBarGreyOrangeDark)
|
||||
.addFlavor(getString(R.string.blue_amber_theme), R.style.NoBarBlueAmber)
|
||||
.addFlavor(getString(R.string.blue_amber_dark_theme), R.style.NoBarBlueAmberDark)
|
||||
.addFlavor(getString(R.string.indigo_pink_theme), R.style.NoBarIndigoPink)
|
||||
.addFlavor(getString(R.string.indigo_pink_dark_theme), R.style.NoBarIndigoPinkDark)
|
||||
.addFlavor(getString(R.string.red_teal_theme), R.style.NoBarRedTeal)
|
||||
.addFlavor(getString(R.string.red_teal_dark_theme), R.style.NoBarRedTealDark)
|
||||
.addFlavor(getString(R.string.default_dark_theme), R.style.NoBarDark, false)
|
||||
.setSharedPreferences(PreferenceManager.getDefaultSharedPreferences(this))
|
||||
.initialize()
|
||||
}
|
||||
|
@ -1,24 +1,32 @@
|
||||
package apps.amine.bou.readerforselfoss
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.v4.app.FragmentManager
|
||||
import android.support.v4.app.FragmentStatePagerAdapter
|
||||
import android.support.v4.view.ViewPager
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Toast
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.fragments.ArticleFragment
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.themes.Toppings
|
||||
import apps.amine.bou.readerforselfoss.transformers.DepthPageTransformer
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
|
||||
import apps.amine.bou.readerforselfoss.utils.succeeded
|
||||
import com.crashlytics.android.Crashlytics
|
||||
import apps.amine.bou.readerforselfoss.utils.toggleStar
|
||||
import com.ftinc.scoop.Scoop
|
||||
import kotlinx.android.synthetic.main.activity_reader.*
|
||||
import me.relex.circleindicator.CircleIndicator
|
||||
import org.acra.ACRA
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
@ -26,67 +34,94 @@ import retrofit2.Response
|
||||
class ReaderActivity : AppCompatActivity() {
|
||||
|
||||
private var markOnScroll: Boolean = false
|
||||
private var debugReadingItems: Boolean = false
|
||||
private var currentItem: Int = 0
|
||||
private lateinit var userIdentifier: String
|
||||
|
||||
private var useWebview: Boolean = false
|
||||
private lateinit var api: SelfossApi
|
||||
|
||||
private lateinit var toolbarMenu: Menu
|
||||
|
||||
private fun showMenuItem(willAddToFavorite: Boolean) {
|
||||
toolbarMenu.findItem(R.id.save).isVisible = willAddToFavorite
|
||||
toolbarMenu.findItem(R.id.unsave).isVisible = !willAddToFavorite
|
||||
}
|
||||
|
||||
private fun canFavorite() {
|
||||
showMenuItem(true)
|
||||
}
|
||||
|
||||
private fun canRemoveFromFavorite() {
|
||||
showMenuItem(false)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
Scoop.getInstance().apply(this)
|
||||
|
||||
setContentView(R.layout.activity_reader)
|
||||
|
||||
val scoop = Scoop.getInstance()
|
||||
scoop.bind(this, Toppings.PRIMARY.value, toolBar)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
|
||||
}
|
||||
|
||||
setSupportActionBar(toolBar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||
|
||||
val settings = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||
val sharedPref = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
|
||||
val debugReadingItems = sharedPref.getBoolean("read_debug", false)
|
||||
val userIdentifier = sharedPref.getString("unique_id", "")
|
||||
markOnScroll = sharedPref.getBoolean("mark_on_scroll", false)
|
||||
useWebview = sharedPref.getBoolean("prefer_webview_in_article_viewer", false)
|
||||
debugReadingItems = prefs.getBoolean("read_debug", false)
|
||||
userIdentifier = prefs.getString("unique_id", "")
|
||||
markOnScroll = prefs.getBoolean("mark_on_scroll", false)
|
||||
|
||||
api = SelfossApi(
|
||||
this,
|
||||
this@ReaderActivity,
|
||||
prefs.getBoolean("isSelfSignedCert", false),
|
||||
prefs.getBoolean("should_log_everything", false)
|
||||
)
|
||||
|
||||
if (allItems.isEmpty()) {
|
||||
Crashlytics.setUserIdentifier(userIdentifier)
|
||||
Crashlytics.log(
|
||||
100,
|
||||
"READER_ITEMS_EMPTY",
|
||||
"Items empty when trying to open the Article Reader. Was (static) companion object field set ?"
|
||||
)
|
||||
Crashlytics.logException(Exception("Empty items on Reader Activity."))
|
||||
|
||||
finish()
|
||||
}
|
||||
|
||||
val api = SelfossApi(
|
||||
this,
|
||||
this@ReaderActivity,
|
||||
settings.getBoolean("isSelfSignedCert", false),
|
||||
sharedPref.getBoolean("should_log_everything", false)
|
||||
)
|
||||
currentItem = intent.getIntExtra("currentItem", 0)
|
||||
|
||||
val currentItem = intent.getIntExtra("currentItem", 0)
|
||||
readItem(allItems[currentItem].id)
|
||||
|
||||
var adapter = ScreenSlidePagerAdapter(supportFragmentManager)
|
||||
pager.adapter = adapter
|
||||
pager.adapter = ScreenSlidePagerAdapter(supportFragmentManager, AppColors(this@ReaderActivity))
|
||||
pager.currentItem = currentItem
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
notifyAdapter()
|
||||
|
||||
pager.setPageTransformer(true, DepthPageTransformer())
|
||||
(indicator as CircleIndicator).setViewPager(pager)
|
||||
|
||||
if (markOnScroll) {
|
||||
pager.addOnPageChangeListener(
|
||||
object : ViewPager.SimpleOnPageChangeListener() {
|
||||
var isLastItem = false
|
||||
|
||||
override fun onPageSelected(position: Int) {
|
||||
isLastItem = (position === (allItems.size - 1))
|
||||
|
||||
if (allItems[position].starred) {
|
||||
canRemoveFromFavorite()
|
||||
} else {
|
||||
canFavorite()
|
||||
}
|
||||
readItem(allItems[pager.currentItem].id)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
override fun onPageScrollStateChanged(state: Int) {
|
||||
if (state === ViewPager.SCROLL_STATE_DRAGGING || (state === ViewPager.SCROLL_STATE_IDLE && isLastItem)) {
|
||||
api.markItem(allItems[pager.currentItem].id).enqueue(
|
||||
fun readItem(id: String) {
|
||||
if (markOnScroll) {
|
||||
api.markItem(id).enqueue(
|
||||
object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<SuccessResponse>,
|
||||
@ -101,13 +136,8 @@ class ReaderActivity : AppCompatActivity() {
|
||||
"response errorBody: ${response.errorBody()?.string()} " +
|
||||
"body success: ${response.body()?.success} " +
|
||||
"body isSuccess: ${response.body()?.isSuccess}"
|
||||
Crashlytics.setUserIdentifier(userIdentifier)
|
||||
Crashlytics.log(
|
||||
100,
|
||||
"READ_DEBUG_SUCCESS",
|
||||
message
|
||||
)
|
||||
Crashlytics.logException(Exception("Was success, but did it work ?"))
|
||||
ACRA.getErrorReporter()
|
||||
.maybeHandleSilentException(Exception(message), this@ReaderActivity)
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,22 +146,16 @@ class ReaderActivity : AppCompatActivity() {
|
||||
t: Throwable
|
||||
) {
|
||||
if (debugReadingItems) {
|
||||
Crashlytics.setUserIdentifier(userIdentifier)
|
||||
Crashlytics.log(
|
||||
100,
|
||||
"READ_DEBUG_ERROR",
|
||||
t.message
|
||||
)
|
||||
Crashlytics.logException(t)
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(t, this@ReaderActivity)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun notifyAdapter() {
|
||||
(pager.adapter as ScreenSlidePagerAdapter).notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
@ -146,14 +170,37 @@ class ReaderActivity : AppCompatActivity() {
|
||||
oldInstanceState!!.clear()
|
||||
}
|
||||
|
||||
private inner class ScreenSlidePagerAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm) {
|
||||
private inner class ScreenSlidePagerAdapter(fm: FragmentManager, val appColors: AppColors) :
|
||||
FragmentStatePagerAdapter(fm) {
|
||||
|
||||
|
||||
override fun getCount(): Int {
|
||||
return allItems.size
|
||||
}
|
||||
|
||||
override fun getItem(position: Int): ArticleFragment {
|
||||
return ArticleFragment.newInstance(position, allItems, useWebview)
|
||||
return ArticleFragment.newInstance(position, allItems)
|
||||
}
|
||||
|
||||
override fun startUpdate(container: ViewGroup) {
|
||||
super.startUpdate(container)
|
||||
|
||||
container.background = ColorDrawable(ContextCompat.getColor(this@ReaderActivity, appColors.colorBackground))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
val inflater = menuInflater
|
||||
inflater.inflate(R.menu.reader_menu, menu)
|
||||
toolbarMenu = menu
|
||||
|
||||
if (!allItems.isEmpty() && allItems[currentItem].starred) {
|
||||
canRemoveFromFavorite()
|
||||
} else {
|
||||
canFavorite()
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
@ -162,6 +209,54 @@ class ReaderActivity : AppCompatActivity() {
|
||||
onBackPressed()
|
||||
return true
|
||||
}
|
||||
R.id.save -> {
|
||||
api.starrItem(allItems[pager.currentItem].id)
|
||||
.enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<SuccessResponse>,
|
||||
response: Response<SuccessResponse>
|
||||
) {
|
||||
allItems[pager.currentItem] = allItems[pager.currentItem].toggleStar()
|
||||
notifyAdapter()
|
||||
canRemoveFromFavorite()
|
||||
}
|
||||
|
||||
override fun onFailure(
|
||||
call: Call<SuccessResponse>,
|
||||
t: Throwable
|
||||
) {
|
||||
Toast.makeText(
|
||||
baseContext,
|
||||
R.string.cant_mark_favortie,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
})
|
||||
}
|
||||
R.id.unsave -> {
|
||||
api.unstarrItem(allItems[pager.currentItem].id)
|
||||
.enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<SuccessResponse>,
|
||||
response: Response<SuccessResponse>
|
||||
) {
|
||||
allItems[pager.currentItem] = allItems[pager.currentItem].toggleStar()
|
||||
notifyAdapter()
|
||||
canFavorite()
|
||||
}
|
||||
|
||||
override fun onFailure(
|
||||
call: Call<SuccessResponse>,
|
||||
t: Throwable
|
||||
) {
|
||||
Toast.makeText(
|
||||
baseContext,
|
||||
R.string.cant_unmark_favortie,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
@ -1,14 +1,18 @@
|
||||
package apps.amine.bou.readerforselfoss
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.v7.app.AppCompatActivity
|
||||
import android.support.v7.widget.LinearLayoutManager
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import android.widget.Toast
|
||||
import apps.amine.bou.readerforselfoss.adapters.SourcesListAdapter
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Sources
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Source
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.themes.Toppings
|
||||
import com.ftinc.scoop.Scoop
|
||||
import kotlinx.android.synthetic.main.activity_sources.*
|
||||
import retrofit2.Call
|
||||
@ -17,14 +21,27 @@ import retrofit2.Response
|
||||
|
||||
class SourcesActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var appColors: AppColors
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
appColors = AppColors(this@SourcesActivity)
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
Scoop.getInstance().apply(this)
|
||||
|
||||
setContentView(R.layout.activity_sources)
|
||||
|
||||
val scoop = Scoop.getInstance()
|
||||
scoop.bind(this, Toppings.PRIMARY.value, toolbar)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
|
||||
}
|
||||
|
||||
setSupportActionBar(toolbar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||
|
||||
fab.rippleColor = appColors.colorAccentDark
|
||||
fab.backgroundTintList = ColorStateList.valueOf(appColors.colorAccent)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
@ -44,18 +61,18 @@ class SourcesActivity : AppCompatActivity() {
|
||||
prefs.getBoolean("isSelfSignedCert", false),
|
||||
prefs.getBoolean("should_log_everything", false)
|
||||
)
|
||||
var items: ArrayList<Sources> = ArrayList()
|
||||
var items: ArrayList<Source> = ArrayList()
|
||||
|
||||
recyclerView.setHasFixedSize(true)
|
||||
recyclerView.layoutManager = mLayoutManager
|
||||
|
||||
api.sources.enqueue(object : Callback<List<Sources>> {
|
||||
api.sources.enqueue(object : Callback<List<Source>> {
|
||||
override fun onResponse(
|
||||
call: Call<List<Sources>>,
|
||||
response: Response<List<Sources>>
|
||||
call: Call<List<Source>>,
|
||||
response: Response<List<Source>>
|
||||
) {
|
||||
if (response.body() != null && response.body()!!.isNotEmpty()) {
|
||||
items = response.body() as ArrayList<Sources>
|
||||
items = response.body() as ArrayList<Source>
|
||||
}
|
||||
val mAdapter = SourcesListAdapter(this@SourcesActivity, items, api)
|
||||
recyclerView.adapter = mAdapter
|
||||
@ -69,7 +86,7 @@ class SourcesActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<List<Sources>>, t: Throwable) {
|
||||
override fun onFailure(call: Call<List<Source>>, t: Throwable) {
|
||||
Toast.makeText(
|
||||
this@SourcesActivity,
|
||||
R.string.cant_get_sources,
|
||||
|
@ -2,16 +2,13 @@ package apps.amine.bou.readerforselfoss.adapters
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.support.design.widget.Snackbar
|
||||
import android.support.v7.widget.CardView
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import androidx.cardview.widget.CardView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.text.Html
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView.ScaleType
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
@ -26,12 +23,10 @@ import apps.amine.bou.readerforselfoss.utils.openInBrowserAsNewTask
|
||||
import apps.amine.bou.readerforselfoss.utils.openItemUrl
|
||||
import apps.amine.bou.readerforselfoss.utils.shareLink
|
||||
import apps.amine.bou.readerforselfoss.utils.sourceAndDateText
|
||||
import apps.amine.bou.readerforselfoss.utils.succeeded
|
||||
import apps.amine.bou.readerforselfoss.utils.toTextDrawableString
|
||||
import com.amulyakhare.textdrawable.TextDrawable
|
||||
import com.amulyakhare.textdrawable.util.ColorGenerator
|
||||
import com.bumptech.glide.Glide
|
||||
import com.crashlytics.android.Crashlytics
|
||||
import com.like.LikeButton
|
||||
import com.like.OnLikeListener
|
||||
import kotlinx.android.synthetic.main.card_item.view.*
|
||||
@ -40,20 +35,22 @@ import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
|
||||
class ItemCardAdapter(
|
||||
private val app: Activity,
|
||||
private val items: ArrayList<Item>,
|
||||
private val api: SelfossApi,
|
||||
override val app: Activity,
|
||||
override var items: ArrayList<Item>,
|
||||
override val api: SelfossApi,
|
||||
private val helper: CustomTabActivityHelper,
|
||||
private val internalBrowser: Boolean,
|
||||
private val articleViewer: Boolean,
|
||||
private val fullHeightCards: Boolean,
|
||||
private val appColors: AppColors,
|
||||
val debugReadingItems: Boolean,
|
||||
val userIdentifier: String
|
||||
) : RecyclerView.Adapter<ItemCardAdapter.ViewHolder>() {
|
||||
override val appColors: AppColors,
|
||||
override val debugReadingItems: Boolean,
|
||||
override val userIdentifier: String,
|
||||
override val updateItems: (ArrayList<Item>) -> Unit
|
||||
) : ItemsAdapter<ItemCardAdapter.ViewHolder>() {
|
||||
private val c: Context = app.baseContext
|
||||
private val generator: ColorGenerator = ColorGenerator.MATERIAL
|
||||
private val imageMaxHeight: Int = c.resources.getDimension(R.dimen.card_image_max_height).toInt()
|
||||
private val imageMaxHeight: Int =
|
||||
c.resources.getDimension(R.dimen.card_image_max_height).toInt()
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val v = LayoutInflater.from(c).inflate(R.layout.card_item, parent, false) as CardView
|
||||
@ -67,6 +64,8 @@ class ItemCardAdapter(
|
||||
holder.mView.favButton.isLiked = itm.starred
|
||||
holder.mView.title.text = Html.fromHtml(itm.title)
|
||||
|
||||
holder.mView.title.setLinkTextColor(appColors.colorAccent)
|
||||
|
||||
holder.mView.sourceTitleAndDate.text = itm.sourceAndDateText()
|
||||
|
||||
if (!fullHeightCards) {
|
||||
@ -90,7 +89,7 @@ class ItemCardAdapter(
|
||||
TextDrawable
|
||||
.builder()
|
||||
.round()
|
||||
.build(itm.sourcetitle.toTextDrawableString(), color)
|
||||
.build(itm.sourcetitle.toTextDrawableString(c), color)
|
||||
holder.mView.sourceImage.setImageDrawable(drawable)
|
||||
} else {
|
||||
c.circularBitmapDrawable(itm.getIcon(c), holder.mView.sourceImage)
|
||||
@ -103,89 +102,9 @@ class ItemCardAdapter(
|
||||
return items.size
|
||||
}
|
||||
|
||||
private fun doUnmark(i: Item, position: Int) {
|
||||
val s = Snackbar
|
||||
.make(
|
||||
app.findViewById(R.id.coordLayout),
|
||||
R.string.marked_as_read,
|
||||
Snackbar.LENGTH_LONG
|
||||
)
|
||||
.setAction(R.string.undo_string) {
|
||||
items.add(position, i)
|
||||
notifyItemInserted(position)
|
||||
|
||||
api.unmarkItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<SuccessResponse>,
|
||||
response: Response<SuccessResponse>
|
||||
) {
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||
items.remove(i)
|
||||
notifyItemRemoved(position)
|
||||
doUnmark(i, position)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
val view = s.view
|
||||
val tv: TextView = view.findViewById(android.support.design.R.id.snackbar_text)
|
||||
tv.setTextColor(Color.WHITE)
|
||||
s.show()
|
||||
}
|
||||
|
||||
fun removeItemAtIndex(position: Int) {
|
||||
|
||||
val i = items[position]
|
||||
|
||||
items.remove(i)
|
||||
notifyItemRemoved(position)
|
||||
|
||||
api.markItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<SuccessResponse>,
|
||||
response: Response<SuccessResponse>
|
||||
) {
|
||||
if (!response.succeeded() && debugReadingItems) {
|
||||
val message =
|
||||
"message: ${response.message()} " +
|
||||
"response isSuccess: ${response.isSuccessful} " +
|
||||
"response code: ${response.code()} " +
|
||||
"response message: ${response.message()} " +
|
||||
"response errorBody: ${response.errorBody()?.string()} " +
|
||||
"body success: ${response.body()?.success} " +
|
||||
"body isSuccess: ${response.body()?.isSuccess}"
|
||||
Crashlytics.setUserIdentifier(userIdentifier)
|
||||
Crashlytics.log(100, "READ_DEBUG_SUCCESS", message)
|
||||
Crashlytics.logException(Exception("Was success, but did it work ?"))
|
||||
|
||||
Toast.makeText(c, message, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
doUnmark(i, position)
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||
if (debugReadingItems) {
|
||||
Crashlytics.setUserIdentifier(userIdentifier)
|
||||
Crashlytics.log(100, "READ_DEBUG_ERROR", t.message)
|
||||
Crashlytics.logException(t)
|
||||
Toast.makeText(c, t.message, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
Toast.makeText(
|
||||
app,
|
||||
app.getString(R.string.cant_mark_read),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
items.add(i)
|
||||
notifyItemInserted(position)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
inner class ViewHolder(val mView: CardView) : RecyclerView.ViewHolder(mView) {
|
||||
init {
|
||||
mView.setCardBackgroundColor(appColors.cardBackground)
|
||||
mView.setCardBackgroundColor(appColors.cardBackgroundColor)
|
||||
handleClickListeners()
|
||||
handleCustomTabActions()
|
||||
}
|
||||
|
@ -2,21 +2,19 @@ package apps.amine.bou.readerforselfoss.adapters
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.support.constraint.ConstraintLayout
|
||||
import android.support.design.widget.Snackbar
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.text.Html
|
||||
import android.util.TypedValue
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent
|
||||
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
||||
import apps.amine.bou.readerforselfoss.utils.glide.bitmapCenterCrop
|
||||
@ -25,10 +23,9 @@ import apps.amine.bou.readerforselfoss.utils.openInBrowserAsNewTask
|
||||
import apps.amine.bou.readerforselfoss.utils.openItemUrl
|
||||
import apps.amine.bou.readerforselfoss.utils.shareLink
|
||||
import apps.amine.bou.readerforselfoss.utils.sourceAndDateText
|
||||
import apps.amine.bou.readerforselfoss.utils.succeeded
|
||||
import apps.amine.bou.readerforselfoss.utils.toTextDrawableString
|
||||
import com.amulyakhare.textdrawable.TextDrawable
|
||||
import com.amulyakhare.textdrawable.util.ColorGenerator
|
||||
import com.crashlytics.android.Crashlytics
|
||||
import com.like.LikeButton
|
||||
import com.like.OnLikeListener
|
||||
import kotlinx.android.synthetic.main.list_item.view.*
|
||||
@ -39,16 +36,17 @@ import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
class ItemListAdapter(
|
||||
private val app: Activity,
|
||||
private val items: ArrayList<Item>,
|
||||
private val api: SelfossApi,
|
||||
override val app: Activity,
|
||||
override var items: ArrayList<Item>,
|
||||
override val api: SelfossApi,
|
||||
private val helper: CustomTabActivityHelper,
|
||||
private val clickBehavior: Boolean,
|
||||
private val internalBrowser: Boolean,
|
||||
private val articleViewer: Boolean,
|
||||
val debugReadingItems: Boolean,
|
||||
val userIdentifier: String
|
||||
) : RecyclerView.Adapter<ItemListAdapter.ViewHolder>() {
|
||||
override val debugReadingItems: Boolean,
|
||||
override val userIdentifier: String,
|
||||
override val appColors: AppColors,
|
||||
override val updateItems: (ArrayList<Item>) -> Unit
|
||||
) : ItemsAdapter<ItemListAdapter.ViewHolder>() {
|
||||
private val generator: ColorGenerator = ColorGenerator.MATERIAL
|
||||
private val c: Context = app.baseContext
|
||||
private val bars: ArrayList<Boolean> = ArrayList(Collections.nCopies(items.size + 1, false))
|
||||
@ -66,9 +64,10 @@ class ItemListAdapter(
|
||||
val itm = items[position]
|
||||
|
||||
|
||||
holder.mView.favButton.isLiked = itm.starred
|
||||
holder.mView.title.text = Html.fromHtml(itm.title)
|
||||
|
||||
holder.mView.title.setLinkTextColor(appColors.colorAccent)
|
||||
|
||||
holder.mView.sourceTitleAndDate.text = itm.sourceAndDateText()
|
||||
|
||||
if (itm.getThumbnail(c).isEmpty()) {
|
||||
@ -92,14 +91,13 @@ class ItemListAdapter(
|
||||
|
||||
if (itm.getIcon(c).isEmpty()) {
|
||||
val color = generator.getColor(itm.sourcetitle)
|
||||
val textDrawable = StringBuilder()
|
||||
for (s in itm.sourcetitle.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) {
|
||||
textDrawable.append(s[0])
|
||||
}
|
||||
|
||||
val builder = TextDrawable.builder().round()
|
||||
val drawable =
|
||||
TextDrawable
|
||||
.builder()
|
||||
.round()
|
||||
.build(itm.sourcetitle.toTextDrawableString(c), color)
|
||||
|
||||
val drawable = builder.build(textDrawable.toString(), color)
|
||||
holder.mView.itemImage.setImageDrawable(drawable)
|
||||
} else {
|
||||
c.circularBitmapDrawable(itm.getIcon(c), holder.mView.itemImage)
|
||||
@ -108,96 +106,22 @@ class ItemListAdapter(
|
||||
c.bitmapCenterCrop(itm.getThumbnail(c), holder.mView.itemImage)
|
||||
}
|
||||
|
||||
// TODO: maybe handle this differently. It crashes when changing tab
|
||||
try {
|
||||
if (bars[position]) {
|
||||
holder.mView.actionBar.visibility = View.VISIBLE
|
||||
} else {
|
||||
holder.mView.actionBar.visibility = View.GONE
|
||||
}
|
||||
} catch (e: IndexOutOfBoundsException) {
|
||||
holder.mView.actionBar.visibility = View.GONE
|
||||
}
|
||||
|
||||
holder.mView.favButton.isLiked = itm.starred
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = items.size
|
||||
|
||||
private fun doUnmark(i: Item, position: Int) {
|
||||
val s = Snackbar
|
||||
.make(
|
||||
app.findViewById(R.id.coordLayout),
|
||||
R.string.marked_as_read,
|
||||
Snackbar.LENGTH_LONG
|
||||
)
|
||||
.setAction(R.string.undo_string) {
|
||||
items.add(position, i)
|
||||
notifyItemInserted(position)
|
||||
|
||||
api.unmarkItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<SuccessResponse>,
|
||||
response: Response<SuccessResponse>
|
||||
) {
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||
items.remove(i)
|
||||
notifyItemRemoved(position)
|
||||
doUnmark(i, position)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
val view = s.view
|
||||
val tv: TextView = view.findViewById(android.support.design.R.id.snackbar_text)
|
||||
tv.setTextColor(Color.WHITE)
|
||||
s.show()
|
||||
}
|
||||
|
||||
fun removeItemAtIndex(position: Int) {
|
||||
|
||||
val i = items[position]
|
||||
|
||||
items.remove(i)
|
||||
notifyItemRemoved(position)
|
||||
|
||||
api.markItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<SuccessResponse>,
|
||||
response: Response<SuccessResponse>
|
||||
) {
|
||||
if (!response.succeeded() && debugReadingItems) {
|
||||
val message =
|
||||
"message: ${response.message()} " +
|
||||
"response isSuccess: ${response.isSuccessful} " +
|
||||
"response code: ${response.code()} " +
|
||||
"response message: ${response.message()} " +
|
||||
"response errorBody: ${response.errorBody()?.string()} " +
|
||||
"body success: ${response.body()?.success} " +
|
||||
"body isSuccess: ${response.body()?.isSuccess}"
|
||||
Crashlytics.setUserIdentifier(userIdentifier)
|
||||
Crashlytics.log(100, "READ_DEBUG_SUCCESS", message)
|
||||
Crashlytics.logException(Exception("Was success, but did it work ?"))
|
||||
Toast.makeText(c, message, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
doUnmark(i, position)
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||
if (debugReadingItems) {
|
||||
Crashlytics.setUserIdentifier(userIdentifier)
|
||||
Crashlytics.log(100, "READ_DEBUG_ERROR", t.message)
|
||||
Crashlytics.logException(t)
|
||||
Toast.makeText(c, t.message, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
Toast.makeText(
|
||||
app,
|
||||
app.getString(R.string.cant_mark_read),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
items.add(i)
|
||||
notifyItemInserted(position)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
inner class ViewHolder(val mView: ConstraintLayout) : RecyclerView.ViewHolder(mView) {
|
||||
|
||||
init {
|
||||
@ -269,24 +193,6 @@ class ItemListAdapter(
|
||||
val customTabsIntent = c.buildCustomTabsIntent()
|
||||
helper.bindCustomTabsService(app)
|
||||
|
||||
|
||||
if (!clickBehavior) {
|
||||
mView.setOnClickListener {
|
||||
c.openItemUrl(
|
||||
items,
|
||||
adapterPosition,
|
||||
items[adapterPosition].getLinkDecoded(),
|
||||
customTabsIntent,
|
||||
internalBrowser,
|
||||
articleViewer,
|
||||
app
|
||||
)
|
||||
}
|
||||
mView.setOnLongClickListener {
|
||||
actionBarShowHide()
|
||||
true
|
||||
}
|
||||
} else {
|
||||
mView.setOnClickListener { actionBarShowHide() }
|
||||
mView.setOnLongClickListener {
|
||||
c.openItemUrl(
|
||||
@ -301,7 +207,6 @@ class ItemListAdapter(
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun actionBarShowHide() {
|
||||
bars[adapterPosition] = true
|
||||
|
@ -0,0 +1,131 @@
|
||||
package apps.amine.bou.readerforselfoss.adapters
|
||||
|
||||
import android.app.Activity
|
||||
import android.graphics.Color
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
|
||||
import apps.amine.bou.readerforselfoss.utils.succeeded
|
||||
import org.acra.ACRA
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
|
||||
abstract class ItemsAdapter<VH : RecyclerView.ViewHolder?> : RecyclerView.Adapter<VH>() {
|
||||
abstract var items: ArrayList<Item>
|
||||
abstract val api: SelfossApi
|
||||
abstract val debugReadingItems: Boolean
|
||||
abstract val userIdentifier: String
|
||||
abstract val app: Activity
|
||||
abstract val appColors: AppColors
|
||||
abstract val updateItems: (ArrayList<Item>) -> Unit
|
||||
|
||||
fun updateAllItems(newItems: ArrayList<Item>) {
|
||||
items = newItems
|
||||
notifyDataSetChanged()
|
||||
updateItems(items)
|
||||
}
|
||||
|
||||
private fun doUnmark(i: Item, position: Int) {
|
||||
val s = Snackbar
|
||||
.make(
|
||||
app.findViewById(R.id.coordLayout),
|
||||
R.string.marked_as_read,
|
||||
Snackbar.LENGTH_LONG
|
||||
)
|
||||
.setAction(R.string.undo_string) {
|
||||
items.add(position, i)
|
||||
notifyItemInserted(position)
|
||||
updateItems(items)
|
||||
|
||||
api.unmarkItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<SuccessResponse>,
|
||||
response: Response<SuccessResponse>
|
||||
) {
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||
items.remove(i)
|
||||
notifyItemRemoved(position)
|
||||
updateItems(items)
|
||||
doUnmark(i, position)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
val view = s.view
|
||||
val tv: TextView = view.findViewById(com.google.android.material.R.id.snackbar_text)
|
||||
tv.setTextColor(Color.WHITE)
|
||||
s.show()
|
||||
}
|
||||
|
||||
fun removeItemAtIndex(position: Int) {
|
||||
|
||||
val i = items[position]
|
||||
|
||||
items.remove(i)
|
||||
notifyItemRemoved(position)
|
||||
updateItems(items)
|
||||
|
||||
|
||||
api.markItem(i.id).enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<SuccessResponse>,
|
||||
response: Response<SuccessResponse>
|
||||
) {
|
||||
if (!response.succeeded() && debugReadingItems) {
|
||||
val message =
|
||||
"message: ${response.message()} " +
|
||||
"response isSuccess: ${response.isSuccessful} " +
|
||||
"response code: ${response.code()} " +
|
||||
"response message: ${response.message()} " +
|
||||
"response errorBody: ${response.errorBody()?.string()} " +
|
||||
"body success: ${response.body()?.success} " +
|
||||
"body isSuccess: ${response.body()?.isSuccess}"
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(Exception(message), app)
|
||||
Toast.makeText(app.baseContext, message, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
doUnmark(i, position)
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||
if (debugReadingItems) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(t, app)
|
||||
Toast.makeText(app.baseContext, t.message, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
Toast.makeText(
|
||||
app,
|
||||
app.getString(R.string.cant_mark_read),
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
items.add(i)
|
||||
notifyItemInserted(position)
|
||||
updateItems(items)
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun addItemAtIndex(item: Item, position: Int) {
|
||||
items.add(position, item)
|
||||
notifyItemInserted(position)
|
||||
updateItems(items)
|
||||
|
||||
}
|
||||
|
||||
fun addItemsAtEnd(newItems: List<Item>) {
|
||||
val oldSize = items.size
|
||||
items.addAll(newItems)
|
||||
notifyItemRangeInserted(oldSize, newItems.size)
|
||||
updateItems(items)
|
||||
|
||||
}
|
||||
}
|
@ -2,15 +2,15 @@ package apps.amine.bou.readerforselfoss.adapters
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.support.constraint.ConstraintLayout
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.Toast
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Sources
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Source
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.utils.glide.circularBitmapDrawable
|
||||
import apps.amine.bou.readerforselfoss.utils.toTextDrawableString
|
||||
@ -23,7 +23,7 @@ import retrofit2.Response
|
||||
|
||||
class SourcesListAdapter(
|
||||
private val app: Activity,
|
||||
private val items: ArrayList<Sources>,
|
||||
private val items: ArrayList<Source>,
|
||||
private val api: SelfossApi
|
||||
) : RecyclerView.Adapter<SourcesListAdapter.ViewHolder>() {
|
||||
private val c: Context = app.baseContext
|
||||
@ -48,7 +48,7 @@ class SourcesListAdapter(
|
||||
TextDrawable
|
||||
.builder()
|
||||
.round()
|
||||
.build(itm.title.toTextDrawableString(), color)
|
||||
.build(itm.title.toTextDrawableString(c), color)
|
||||
holder.mView.itemImage.setImageDrawable(drawable)
|
||||
} else {
|
||||
c.circularBitmapDrawable(itm.getIcon(c), holder.mView.itemImage)
|
||||
|
@ -7,7 +7,7 @@ import retrofit2.Call
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.gson.GsonConverterFactory
|
||||
|
||||
class MercuryApi(private val key: String, shouldLog: Boolean) {
|
||||
class MercuryApi(shouldLog: Boolean) {
|
||||
private val service: MercuryService
|
||||
|
||||
init {
|
||||
@ -26,7 +26,7 @@ class MercuryApi(private val key: String, shouldLog: Boolean) {
|
||||
val retrofit =
|
||||
Retrofit
|
||||
.Builder()
|
||||
.baseUrl("https://mercury.postlight.com")
|
||||
.baseUrl("https://www.amine-bou.fr")
|
||||
.client(client)
|
||||
.addConverterFactory(GsonConverterFactory.create(gson))
|
||||
.build()
|
||||
@ -34,6 +34,6 @@ class MercuryApi(private val key: String, shouldLog: Boolean) {
|
||||
}
|
||||
|
||||
fun parseUrl(url: String): Call<ParsedContent> {
|
||||
return service.parseUrl(url, this.key)
|
||||
return service.parseUrl(url)
|
||||
}
|
||||
}
|
||||
|
@ -6,9 +6,9 @@ import com.google.gson.annotations.SerializedName
|
||||
|
||||
class ParsedContent(
|
||||
@SerializedName("title") val title: String,
|
||||
@SerializedName("content") val content: String,
|
||||
@SerializedName("content") val content: String?,
|
||||
@SerializedName("date_published") val date_published: String,
|
||||
@SerializedName("lead_image_url") val lead_image_url: String,
|
||||
@SerializedName("lead_image_url") val lead_image_url: String?,
|
||||
@SerializedName("dek") val dek: String,
|
||||
@SerializedName("url") val url: String,
|
||||
@SerializedName("domain") val domain: String,
|
||||
@ -20,7 +20,8 @@ class ParsedContent(
|
||||
|
||||
companion object {
|
||||
@JvmField
|
||||
val CREATOR: Parcelable.Creator<ParsedContent> = object : Parcelable.Creator<ParsedContent> {
|
||||
val CREATOR: Parcelable.Creator<ParsedContent> =
|
||||
object : Parcelable.Creator<ParsedContent> {
|
||||
override fun createFromParcel(source: Parcel): ParsedContent = ParsedContent(source)
|
||||
override fun newArray(size: Int): Array<ParsedContent?> = arrayOfNulls(size)
|
||||
}
|
||||
|
@ -6,6 +6,6 @@ import retrofit2.http.Header
|
||||
import retrofit2.http.Query
|
||||
|
||||
interface MercuryService {
|
||||
@GET("parser")
|
||||
fun parseUrl(@Query("url") url: String, @Header("x-api-key") key: String): Call<ParsedContent>
|
||||
@GET("parser.php")
|
||||
fun parseUrl(@Query("link") link: String): Call<ParsedContent>
|
||||
}
|
||||
|
@ -125,6 +125,9 @@ class SelfossApi(
|
||||
): Call<List<Item>> =
|
||||
getItems("starred", tag, sourceId, search, itemsNumber, offset)
|
||||
|
||||
fun allItems(): Call<List<Item>> =
|
||||
service.allItems(userName, password)
|
||||
|
||||
private fun getItems(
|
||||
type: String,
|
||||
tag: String?,
|
||||
@ -159,7 +162,7 @@ class SelfossApi(
|
||||
fun update(): Call<String> =
|
||||
service.update(userName, password)
|
||||
|
||||
val sources: Call<List<Sources>>
|
||||
val sources: Call<List<Source>>
|
||||
get() = service.sources(userName, password)
|
||||
|
||||
fun deleteSource(id: String): Call<SuccessResponse> =
|
||||
|
@ -9,13 +9,13 @@ import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import apps.amine.bou.readerforselfoss.utils.isEmptyOrNullOrNullString
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
private fun constructUrl(config: Config?, path: String, file: String): String {
|
||||
val baseUriBuilder = Uri.parse(config!!.baseUrl).buildUpon()
|
||||
baseUriBuilder.appendPath(path).appendPath(file)
|
||||
|
||||
private fun constructUrl(config: Config?, path: String, file: String?): String {
|
||||
return if (file.isEmptyOrNullOrNullString()) {
|
||||
""
|
||||
} else {
|
||||
val baseUriBuilder = Uri.parse(config!!.baseUrl).buildUpon()
|
||||
baseUriBuilder.appendPath(path).appendPath(file)
|
||||
|
||||
baseUriBuilder.toString()
|
||||
}
|
||||
}
|
||||
@ -42,7 +42,7 @@ data class Spout(
|
||||
@SerializedName("description") val description: String
|
||||
)
|
||||
|
||||
data class Sources(
|
||||
data class Source(
|
||||
@SerializedName("id") val id: String,
|
||||
@SerializedName("title") val title: String,
|
||||
@SerializedName("tags") val tags: String,
|
||||
@ -66,11 +66,12 @@ data class Item(
|
||||
@SerializedName("title") val title: String,
|
||||
@SerializedName("content") val content: String,
|
||||
@SerializedName("unread") val unread: Boolean,
|
||||
@SerializedName("starred") val starred: Boolean,
|
||||
@SerializedName("starred") var starred: Boolean,
|
||||
@SerializedName("thumbnail") val thumbnail: String,
|
||||
@SerializedName("icon") val icon: String,
|
||||
@SerializedName("link") val link: String,
|
||||
@SerializedName("sourcetitle") val sourcetitle: String
|
||||
@SerializedName("sourcetitle") val sourcetitle: String,
|
||||
@SerializedName("tags") val tags: String
|
||||
) : Parcelable {
|
||||
|
||||
var config: Config? = null
|
||||
@ -92,7 +93,8 @@ data class Item(
|
||||
thumbnail = source.readString(),
|
||||
icon = source.readString(),
|
||||
link = source.readString(),
|
||||
sourcetitle = source.readString()
|
||||
sourcetitle = source.readString(),
|
||||
tags = source.readString()
|
||||
)
|
||||
|
||||
override fun describeContents() = 0
|
||||
@ -108,6 +110,7 @@ data class Item(
|
||||
dest.writeString(icon)
|
||||
dest.writeString(link)
|
||||
dest.writeString(sourcetitle)
|
||||
dest.writeString(tags)
|
||||
}
|
||||
|
||||
fun getIcon(app: Context): String {
|
||||
@ -127,7 +130,8 @@ data class Item(
|
||||
// TODO: maybe find a better way to handle these kind of urls
|
||||
fun getLinkDecoded(): String {
|
||||
var stringUrl: String
|
||||
stringUrl = if (link.startsWith("http://news.google.com/news/") || link.startsWith("https://news.google.com/news/")) {
|
||||
stringUrl =
|
||||
if (link.startsWith("http://news.google.com/news/") || link.startsWith("https://news.google.com/news/")) {
|
||||
if (link.contains("&url=")) {
|
||||
link.substringAfter("&url=")
|
||||
} else {
|
||||
@ -144,7 +148,7 @@ data class Item(
|
||||
|
||||
// handle url not starting with http
|
||||
if (stringUrl.startsWith("//")) {
|
||||
stringUrl = "http:" + stringUrl
|
||||
stringUrl = "http:$stringUrl"
|
||||
}
|
||||
|
||||
return stringUrl
|
||||
|
@ -27,6 +27,12 @@ internal interface SelfossService {
|
||||
@Query("offset") offset: Int
|
||||
): Call<List<Item>>
|
||||
|
||||
@GET("items")
|
||||
fun allItems(
|
||||
@Query("username") username: String,
|
||||
@Query("password") password: String
|
||||
): Call<List<Item>>
|
||||
|
||||
@Headers("Content-Type: application/x-www-form-urlencoded")
|
||||
@POST("mark/{id}")
|
||||
fun markAsRead(
|
||||
@ -95,7 +101,7 @@ internal interface SelfossService {
|
||||
fun sources(
|
||||
@Query("username") username: String,
|
||||
@Query("password") password: String
|
||||
): Call<List<Sources>>
|
||||
): Call<List<Source>>
|
||||
|
||||
@DELETE("source/{id}")
|
||||
fun deleteSource(
|
||||
|
@ -1,44 +1,53 @@
|
||||
package apps.amine.bou.readerforselfoss.fragments
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import android.support.customtabs.CustomTabsIntent
|
||||
import android.support.design.widget.FloatingActionButton
|
||||
import android.support.v4.app.Fragment
|
||||
import android.support.v4.widget.NestedScrollView
|
||||
import android.text.Html
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.util.TypedValue
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.WebSettings
|
||||
import apps.amine.bou.readerforselfoss.BuildConfig
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
import apps.amine.bou.readerforselfoss.api.mercury.MercuryApi
|
||||
import apps.amine.bou.readerforselfoss.api.mercury.ParsedContent
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent
|
||||
import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
||||
import apps.amine.bou.readerforselfoss.utils.isEmptyOrNullOrNullString
|
||||
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
|
||||
import apps.amine.bou.readerforselfoss.utils.openItemUrl
|
||||
import apps.amine.bou.readerforselfoss.utils.shareLink
|
||||
import apps.amine.bou.readerforselfoss.utils.sourceAndDateText
|
||||
import apps.amine.bou.readerforselfoss.utils.succeeded
|
||||
import apps.amine.bou.readerforselfoss.utils.toPx
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.crashlytics.android.Crashlytics
|
||||
import com.github.rubensousa.floatingtoolbar.FloatingToolbar
|
||||
import kotlinx.android.synthetic.main.fragment_article.view.*
|
||||
import org.sufficientlysecure.htmltextview.HtmlHttpImageGetter
|
||||
import org.acra.ACRA
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.net.MalformedURLException
|
||||
import java.net.URL
|
||||
|
||||
class ArticleFragment : Fragment() {
|
||||
private lateinit var pageNumber: Number
|
||||
private var fontSize: Int = 14
|
||||
private lateinit var allItems: ArrayList<Item>
|
||||
private lateinit var mCustomTabActivityHelper: CustomTabActivityHelper
|
||||
private lateinit var url: String
|
||||
@ -46,9 +55,9 @@ class ArticleFragment : Fragment() {
|
||||
private lateinit var contentSource: String
|
||||
private lateinit var contentImage: String
|
||||
private lateinit var contentTitle: String
|
||||
private lateinit var editor: SharedPreferences.Editor
|
||||
private lateinit var fab: FloatingActionButton
|
||||
|
||||
private var useWebview: Boolean = false
|
||||
private lateinit var appColors: AppColors
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
@ -56,14 +65,17 @@ class ArticleFragment : Fragment() {
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
appColors = AppColors(activity!!)
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
pageNumber = arguments!!.getInt(ARG_POSITION)
|
||||
allItems = arguments!!.getParcelableArrayList(ARG_ITEMS)
|
||||
useWebview = arguments!!.getBoolean(ARG_WEBVIEW)
|
||||
}
|
||||
|
||||
private lateinit var rootView: ViewGroup
|
||||
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
@ -78,17 +90,37 @@ class ArticleFragment : Fragment() {
|
||||
contentImage = allItems[pageNumber.toInt()].getThumbnail(activity!!)
|
||||
contentSource = allItems[pageNumber.toInt()].sourceAndDateText()
|
||||
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
editor = prefs.edit()
|
||||
fontSize = prefs.getString("reader_font_size", "14").toInt()
|
||||
|
||||
val settings = activity!!.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||
val debugReadingItems = prefs.getBoolean("read_debug", false)
|
||||
|
||||
val api = SelfossApi(
|
||||
context!!,
|
||||
activity!!,
|
||||
settings.getBoolean("isSelfSignedCert", false),
|
||||
prefs.getBoolean("should_log_everything", false)
|
||||
)
|
||||
|
||||
fab = rootView.fab
|
||||
val mFloatingToolbar: FloatingToolbar = rootView.floatingToolbar
|
||||
mFloatingToolbar.attachFab(fab)
|
||||
|
||||
fab.backgroundTintList = ColorStateList.valueOf(appColors.colorAccent)
|
||||
|
||||
fab.rippleColor = appColors.colorAccentDark
|
||||
|
||||
val floatingToolbar: FloatingToolbar = rootView.floatingToolbar
|
||||
floatingToolbar.attachFab(fab)
|
||||
|
||||
floatingToolbar.background = ColorDrawable(appColors.colorAccent)
|
||||
|
||||
val customTabsIntent = activity!!.buildCustomTabsIntent()
|
||||
mCustomTabActivityHelper = CustomTabActivityHelper()
|
||||
mCustomTabActivityHelper.bindCustomTabsService(activity)
|
||||
|
||||
val prefs = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
|
||||
mFloatingToolbar.setClickListener(
|
||||
floatingToolbar.setClickListener(
|
||||
object : FloatingToolbar.ItemClickListener {
|
||||
override fun onItemClick(item: MenuItem) {
|
||||
when (item.itemId) {
|
||||
@ -103,6 +135,35 @@ class ArticleFragment : Fragment() {
|
||||
false,
|
||||
activity!!
|
||||
)
|
||||
R.id.unread_action -> api.unmarkItem(allItems[pageNumber.toInt()].id).enqueue(
|
||||
object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<SuccessResponse>,
|
||||
response: Response<SuccessResponse>
|
||||
) {
|
||||
if (!response.succeeded() && debugReadingItems) {
|
||||
val message =
|
||||
"message: ${response.message()} " +
|
||||
"response isSuccess: ${response.isSuccessful} " +
|
||||
"response code: ${response.code()} " +
|
||||
"response message: ${response.message()} " +
|
||||
"response errorBody: ${response.errorBody()?.string()} " +
|
||||
"body success: ${response.body()?.success} " +
|
||||
"body isSuccess: ${response.body()?.isSuccess}"
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(Exception(message), activity!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(
|
||||
call: Call<SuccessResponse>,
|
||||
t: Throwable
|
||||
) {
|
||||
if (debugReadingItems) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(t, activity!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
@ -112,23 +173,19 @@ class ArticleFragment : Fragment() {
|
||||
}
|
||||
)
|
||||
|
||||
rootView.source.text = contentSource
|
||||
|
||||
if (contentText.isEmptyOrNullOrNullString()) {
|
||||
getContentFromMercury(customTabsIntent, prefs)
|
||||
} else {
|
||||
rootView.source.text = contentSource
|
||||
rootView.titleView.text = contentTitle
|
||||
|
||||
if (!useWebview) {
|
||||
htmlToTextview(contentText, customTabsIntent, prefs)
|
||||
} else {
|
||||
htmlToWebview(contentText)
|
||||
}
|
||||
htmlToWebview(contentText, prefs)
|
||||
|
||||
if (!contentImage.isEmptyOrNullOrNullString()) {
|
||||
if (!contentImage.isEmptyOrNullOrNullString() && context != null) {
|
||||
rootView.imageView.visibility = View.VISIBLE
|
||||
Glide
|
||||
.with(activity!!.baseContext)
|
||||
.with(context!!)
|
||||
.asBitmap()
|
||||
.load(contentImage)
|
||||
.apply(RequestOptions.fitCenterTransform())
|
||||
@ -143,15 +200,11 @@ class ArticleFragment : Fragment() {
|
||||
if (scrollY > oldScrollY) {
|
||||
fab.hide()
|
||||
} else {
|
||||
if (mFloatingToolbar.isShowing) mFloatingToolbar.hide() else fab.show()
|
||||
if (floatingToolbar.isShowing) floatingToolbar.hide() else fab.show()
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
if (!useWebview) {
|
||||
rootView.content.movementMethod = LinkMovementMethod.getInstance()
|
||||
}
|
||||
|
||||
return rootView
|
||||
}
|
||||
|
||||
@ -161,7 +214,6 @@ class ArticleFragment : Fragment() {
|
||||
) {
|
||||
rootView.progressBar.visibility = View.VISIBLE
|
||||
val parser = MercuryApi(
|
||||
BuildConfig.MERCURY_KEY,
|
||||
prefs.getBoolean("should_log_everything", false)
|
||||
)
|
||||
|
||||
@ -171,40 +223,76 @@ class ArticleFragment : Fragment() {
|
||||
call: Call<ParsedContent>,
|
||||
response: Response<ParsedContent>
|
||||
) {
|
||||
if (response.body() != null && response.body()!!.content != null && response.body()!!.content.isNotEmpty()) {
|
||||
rootView.source.text = response.body()!!.domain
|
||||
// TODO: clean all the following after finding the mercury content issue
|
||||
try {
|
||||
if (response.body() != null && response.body()!!.content != null && !response.body()!!.content.isNullOrEmpty()) {
|
||||
try {
|
||||
rootView.titleView.text = response.body()!!.title
|
||||
try {
|
||||
// Note: Mercury may return relative urls... If it does the url val will not be changed.
|
||||
URL(response.body()!!.url)
|
||||
url = response.body()!!.url
|
||||
|
||||
if (response.body()!!.content != null && !response.body()!!.content.isEmpty()) {
|
||||
if (!useWebview) {
|
||||
htmlToTextview(
|
||||
response.body()!!.content,
|
||||
customTabsIntent,
|
||||
prefs
|
||||
)
|
||||
} else {
|
||||
htmlToWebview(response.body()!!.content)
|
||||
} catch (e: MalformedURLException) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, activity!!)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
if (context != null) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
|
||||
}
|
||||
}
|
||||
|
||||
if (response.body()!!.lead_image_url != null && !response.body()!!.lead_image_url.isEmpty()) {
|
||||
try {
|
||||
htmlToWebview(response.body()!!.content.orEmpty(), prefs)
|
||||
} catch (e: Exception) {
|
||||
if (context != null) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (response.body()!!.lead_image_url != null && !response.body()!!.lead_image_url.isNullOrEmpty() && context != null) {
|
||||
rootView.imageView.visibility = View.VISIBLE
|
||||
try {
|
||||
Glide
|
||||
.with(activity!!.baseContext)
|
||||
.with(context!!)
|
||||
.asBitmap()
|
||||
.load(response.body()!!.lead_image_url)
|
||||
.apply(RequestOptions.fitCenterTransform())
|
||||
.into(rootView.imageView)
|
||||
} catch (e: Exception) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
|
||||
}
|
||||
} else {
|
||||
rootView.imageView.visibility = View.GONE
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
if (context != null) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
rootView.nestedScrollView.scrollTo(0, 0)
|
||||
|
||||
rootView.progressBar.visibility = View.GONE
|
||||
} catch (e: Exception) {
|
||||
if (context != null) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
openInBrowserAfterFailing(customTabsIntent)
|
||||
} catch (e: Exception) {
|
||||
if (context != null) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
if (context != null) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, context!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -216,59 +304,107 @@ class ArticleFragment : Fragment() {
|
||||
)
|
||||
}
|
||||
|
||||
private fun htmlToTextview(
|
||||
c: String,
|
||||
customTabsIntent: CustomTabsIntent,
|
||||
prefs: SharedPreferences
|
||||
) {
|
||||
try {
|
||||
rootView.content.visibility = View.VISIBLE
|
||||
rootView.content.text = Html.fromHtml(
|
||||
c,
|
||||
HtmlHttpImageGetter(rootView.content, null, true),
|
||||
null
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Crashlytics.setUserIdentifier(prefs.getString("unique_id", ""))
|
||||
Crashlytics.log(100, "CANT_TRANSFORM_TO_HTML", e.message)
|
||||
Crashlytics.logException(e)
|
||||
openInBrowserAfterFailing(customTabsIntent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun htmlToWebview(c: String) {
|
||||
val attrBackground = TypedValue()
|
||||
activity!!.baseContext.theme.resolveAttribute(
|
||||
android.R.attr.colorBackground,
|
||||
attrBackground,
|
||||
true
|
||||
)
|
||||
|
||||
val defaultColor = TypedValue()
|
||||
activity!!.baseContext.theme.resolveAttribute(
|
||||
android.R.attr.colorAccent,
|
||||
defaultColor,
|
||||
true
|
||||
)
|
||||
val accentColor = fab.backgroundTintList?.defaultColor ?: defaultColor.data
|
||||
val stringColor = String.format("#%06X", 0xFFFFFF and accentColor)
|
||||
private fun htmlToWebview(c: String, prefs: SharedPreferences) {
|
||||
val stringColor = String.format("#%06X", 0xFFFFFF and appColors.colorAccent)
|
||||
|
||||
rootView.webcontent.visibility = View.VISIBLE
|
||||
rootView.webcontent.setBackgroundColor(attrBackground.data)
|
||||
val (textColor, backgroundColor) = if (appColors.isDarkTheme) {
|
||||
if (context != null) {
|
||||
rootView.webcontent.setBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
context!!,
|
||||
R.color.dark_webview
|
||||
)
|
||||
)
|
||||
Pair(ContextCompat.getColor(context!!, R.color.dark_webview_text), ContextCompat.getColor(context!!, R.color.light_webview_text))
|
||||
} else {
|
||||
Pair(null, null)
|
||||
}
|
||||
} else {
|
||||
if (context != null) {
|
||||
rootView.webcontent.setBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
context!!,
|
||||
R.color.light_webview
|
||||
)
|
||||
)
|
||||
Pair(ContextCompat.getColor(context!!, R.color.light_webview_text), ContextCompat.getColor(context!!, R.color.dark_webview_text))
|
||||
} else {
|
||||
Pair(null, null)
|
||||
}
|
||||
}
|
||||
|
||||
val stringTextColor: String = if (textColor != null) {
|
||||
String.format("#%06X", 0xFFFFFF and textColor)
|
||||
} else {
|
||||
"#000000"
|
||||
}
|
||||
|
||||
val stringBackgroundColor = if (backgroundColor != null) {
|
||||
String.format("#%06X", 0xFFFFFF and backgroundColor)
|
||||
} else {
|
||||
"#FFFFFF"
|
||||
}
|
||||
|
||||
rootView.webcontent.settings.useWideViewPort = true
|
||||
rootView.webcontent.settings.loadWithOverviewMode = true
|
||||
rootView.webcontent.settings.javaScriptEnabled = false
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
rootView.webcontent.settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING
|
||||
rootView.webcontent.settings.layoutAlgorithm =
|
||||
WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING
|
||||
} else {
|
||||
rootView.webcontent.settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.SINGLE_COLUMN
|
||||
}
|
||||
|
||||
rootView.webcontent.loadData(
|
||||
"<style>img{display: inline-block;height: auto; width: 100%; max-width: 100%;} a{color: $stringColor}</style>$c",
|
||||
"text/html; charset=utf-8",
|
||||
"utf-8"
|
||||
var baseUrl: String? = null
|
||||
|
||||
try {
|
||||
val itemUrl = URL(url)
|
||||
baseUrl = itemUrl.protocol + "://" + itemUrl.host
|
||||
} catch (e: MalformedURLException) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, activity!!)
|
||||
}
|
||||
|
||||
rootView.webcontent.loadDataWithBaseURL(
|
||||
baseUrl,
|
||||
"""<html>
|
||||
|<head>
|
||||
| <style>
|
||||
| img {
|
||||
| display: inline-block;
|
||||
| height: auto;
|
||||
| width: 100%;
|
||||
| max-width: 100%;
|
||||
| }
|
||||
| a {
|
||||
| color: $stringColor !important;
|
||||
| }
|
||||
| *:not(a) {
|
||||
| color: $stringTextColor;
|
||||
| }
|
||||
| * {
|
||||
| font-size: ${fontSize.toPx}px;
|
||||
| text-align: justify;
|
||||
| word-break: break-word;
|
||||
| overflow:hidden;
|
||||
| }
|
||||
| a, pre, code {
|
||||
| text-align: left;
|
||||
| }
|
||||
| pre, code {
|
||||
| white-space: pre-wrap;
|
||||
| width:100%;
|
||||
| background-color: $stringBackgroundColor;
|
||||
| }
|
||||
| </style>
|
||||
|</head>
|
||||
|<body>
|
||||
| $c
|
||||
|</body>""".trimMargin(),
|
||||
"text/html",
|
||||
"utf-8",
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
@ -286,22 +422,21 @@ class ArticleFragment : Fragment() {
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val ARG_POSITION = "position"
|
||||
private val ARG_ITEMS = "items"
|
||||
private val ARG_WEBVIEW = "userWebview"
|
||||
private const val ARG_POSITION = "position"
|
||||
private const val ARG_ITEMS = "items"
|
||||
|
||||
fun newInstance(
|
||||
position: Int,
|
||||
allItems: ArrayList<Item>,
|
||||
webview: Boolean
|
||||
allItems: ArrayList<Item>
|
||||
): ArticleFragment {
|
||||
val fragment = ArticleFragment()
|
||||
val args = Bundle()
|
||||
args.putInt(ARG_POSITION, position)
|
||||
args.putBoolean(ARG_WEBVIEW, webview)
|
||||
args.putParcelableArrayList(ARG_ITEMS, allItems)
|
||||
fragment.arguments = args
|
||||
return fragment
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,36 @@
|
||||
package apps.amine.bou.readerforselfoss.persistence.dao
|
||||
|
||||
import androidx.room.Delete
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.SourceEntity
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.TagEntity
|
||||
|
||||
@Dao
|
||||
interface DrawerDataDao {
|
||||
@Query("SELECT * FROM tags")
|
||||
fun tags(): List<TagEntity>
|
||||
|
||||
@Query("SELECT * FROM sources")
|
||||
fun sources(): List<SourceEntity>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertAllTags(vararg tags: TagEntity)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertAllSources(vararg sources: SourceEntity)
|
||||
|
||||
@Query("DELETE FROM tags")
|
||||
fun deleteAllTags()
|
||||
|
||||
@Query("DELETE FROM sources")
|
||||
fun deleteAllSources()
|
||||
|
||||
@Delete
|
||||
fun deleteTag(tag: TagEntity)
|
||||
|
||||
@Delete
|
||||
fun deleteSource(source: SourceEntity)
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
package apps.amine.bou.readerforselfoss.persistence.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.ItemEntity
|
||||
import androidx.room.Update
|
||||
|
||||
|
||||
|
||||
@Dao
|
||||
interface ItemsDao {
|
||||
@Query("SELECT * FROM items")
|
||||
fun items(): List<ItemEntity>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun insertAllItems(vararg tags: ItemEntity)
|
||||
|
||||
@Query("DELETE FROM items")
|
||||
fun deleteAllItems()
|
||||
|
||||
@Update
|
||||
fun updateItem(item: ItemEntity)
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package apps.amine.bou.readerforselfoss.persistence.database
|
||||
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.room.Database
|
||||
import apps.amine.bou.readerforselfoss.persistence.dao.DrawerDataDao
|
||||
import apps.amine.bou.readerforselfoss.persistence.dao.ItemsDao
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.ItemEntity
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.SourceEntity
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.TagEntity
|
||||
|
||||
@Database(entities = [TagEntity::class, SourceEntity::class, ItemEntity::class], version = 2)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
abstract fun drawerDataDao(): DrawerDataDao
|
||||
|
||||
abstract fun itemsDao(): ItemsDao
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
package apps.amine.bou.readerforselfoss.persistence.entities
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "tags")
|
||||
data class TagEntity(
|
||||
@PrimaryKey
|
||||
@ColumnInfo(name = "tag")
|
||||
val tag: String,
|
||||
@ColumnInfo(name = "color")
|
||||
val color: String,
|
||||
@ColumnInfo(name = "unread")
|
||||
val unread: Int
|
||||
)
|
||||
|
||||
@Entity(tableName = "sources")
|
||||
data class SourceEntity(
|
||||
@PrimaryKey
|
||||
@ColumnInfo(name = "id")
|
||||
val id: String,
|
||||
@ColumnInfo(name = "title")
|
||||
val title: String,
|
||||
@ColumnInfo(name = "tags")
|
||||
val tags: String,
|
||||
@ColumnInfo(name = "spout")
|
||||
val spout: String,
|
||||
@ColumnInfo(name = "error")
|
||||
val error: String,
|
||||
@ColumnInfo(name = "icon")
|
||||
val icon: String
|
||||
)
|
@ -0,0 +1,32 @@
|
||||
package apps.amine.bou.readerforselfoss.persistence.entities
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "items")
|
||||
data class ItemEntity(
|
||||
@PrimaryKey
|
||||
@ColumnInfo(name = "id")
|
||||
val id: String,
|
||||
@ColumnInfo(name = "datetime")
|
||||
val datetime: String,
|
||||
@ColumnInfo(name = "title")
|
||||
val title: String,
|
||||
@ColumnInfo(name = "content")
|
||||
val content: String,
|
||||
@ColumnInfo(name = "unread")
|
||||
val unread: Boolean,
|
||||
@ColumnInfo(name = "starred")
|
||||
var starred: Boolean,
|
||||
@ColumnInfo(name = "thumbnail")
|
||||
val thumbnail: String,
|
||||
@ColumnInfo(name = "icon")
|
||||
val icon: String,
|
||||
@ColumnInfo(name = "link")
|
||||
val link: String,
|
||||
@ColumnInfo(name = "sourcetitle")
|
||||
val sourcetitle: String,
|
||||
@ColumnInfo(name = "tags")
|
||||
val tags: String
|
||||
)
|
@ -0,0 +1,10 @@
|
||||
package apps.amine.bou.readerforselfoss.persistence.migrations
|
||||
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import androidx.room.migration.Migration
|
||||
|
||||
val MIGRATION_1_2: Migration = object : Migration(1, 2) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE IF NOT EXISTS `items` (`id` TEXT NOT NULL, `datetime` TEXT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `unread` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `thumbnail` TEXT NOT NULL, `icon` TEXT NOT NULL, `link` TEXT NOT NULL, `sourcetitle` TEXT NOT NULL, `tags` TEXT NOT NULL, PRIMARY KEY(`id`))")
|
||||
}
|
||||
}
|
@ -1,38 +1,43 @@
|
||||
package apps.amine.bou.readerforselfoss.settings;
|
||||
|
||||
import android.content.res.Configuration;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceActivity;
|
||||
import android.support.annotation.LayoutRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.AppBarLayout;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AppCompatDelegate;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import androidx.annotation.LayoutRes;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import com.google.android.material.appbar.AppBarLayout;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import androidx.appcompat.app.AppCompatDelegate;
|
||||
import androidx.appcompat.widget.Toolbar;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import apps.amine.bou.readerforselfoss.R;
|
||||
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 { //NOSONAR
|
||||
public abstract class AppCompatPreferenceActivity extends PreferenceActivity {
|
||||
|
||||
private AppCompatDelegate mDelegate;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
new AppColors(this);
|
||||
|
||||
getDelegate().installViewFactory();
|
||||
getDelegate().onCreate(savedInstanceState);
|
||||
Scoop.getInstance().apply(this);
|
||||
super.onCreate(savedInstanceState);
|
||||
}
|
||||
|
||||
@ -40,9 +45,16 @@ public abstract class AppCompatPreferenceActivity extends PreferenceActivity { /
|
||||
protected void onPostCreate(Bundle savedInstanceState) {
|
||||
super.onPostCreate(savedInstanceState);
|
||||
|
||||
LinearLayout root = (LinearLayout)findViewById(android.R.id.list).getParent().getParent().getParent();
|
||||
LinearLayout root = (LinearLayout) findViewById(android.R.id.list).getParent().getParent().getParent();
|
||||
AppBarLayout bar = (AppBarLayout) LayoutInflater.from(this).inflate(R.layout.settings_toolbar, root, false);
|
||||
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);
|
||||
|
@ -1,8 +1,6 @@
|
||||
package apps.amine.bou.readerforselfoss.settings;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
@ -21,16 +19,21 @@ import android.preference.PreferenceActivity;
|
||||
import android.preference.PreferenceFragment;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.preference.SwitchPreference;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import androidx.appcompat.app.ActionBar;
|
||||
import android.text.Editable;
|
||||
import android.text.InputFilter;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.Toast;
|
||||
|
||||
import apps.amine.bou.readerforselfoss.BuildConfig;
|
||||
import java.util.List;
|
||||
|
||||
import apps.amine.bou.readerforselfoss.R;
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors;
|
||||
import apps.amine.bou.readerforselfoss.utils.Config;
|
||||
import com.ftinc.scoop.ui.ScoopSettingsActivity;
|
||||
|
||||
|
||||
/**
|
||||
@ -44,7 +47,7 @@ import com.ftinc.scoop.ui.ScoopSettingsActivity;
|
||||
* href="http://developer.android.com/guide/topics/ui/settings.html">Settings
|
||||
* API Guide</a> for more information on developing a Settings UI.
|
||||
*/
|
||||
public class SettingsActivity extends AppCompatPreferenceActivity { //NOSONAR
|
||||
public class SettingsActivity extends AppCompatPreferenceActivity {
|
||||
/**
|
||||
* A preference value change listener that updates the preference's summary
|
||||
* to reflect its new value.
|
||||
@ -90,6 +93,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity { //NOSONAR
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
new AppColors(this);
|
||||
super.onCreate(savedInstanceState);
|
||||
setupActionBar();
|
||||
}
|
||||
@ -130,8 +134,11 @@ public class SettingsActivity extends AppCompatPreferenceActivity { //NOSONAR
|
||||
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)
|
||||
|| DebugPreferenceFragment.class.getName().equals(fragmentName)
|
||||
|| LinksPreferenceFragment.class.getName().equals(fragmentName);
|
||||
|| LinksPreferenceFragment.class.getName().equals(fragmentName)
|
||||
|| ThemePreferenceFragment.class.getName().equals(fragmentName);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -146,26 +153,15 @@ public class SettingsActivity extends AppCompatPreferenceActivity { //NOSONAR
|
||||
addPreferencesFromResource(R.xml.pref_general);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
SwitchPreference cardViewActive = (SwitchPreference) findPreference("card_view_active");
|
||||
final SwitchPreference tabOnTap = (SwitchPreference) findPreference("tab_on_tap");
|
||||
tabOnTap.setEnabled(!cardViewActive.isChecked());
|
||||
cardViewActive.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
|
||||
public boolean onPreferenceChange(Preference preference, Object newValue){
|
||||
boolean isEnabled = (Boolean) newValue;
|
||||
tabOnTap.setEnabled(!isEnabled);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
EditTextPreference itemsNumber = (EditTextPreference) findPreference("prefer_api_items_number");
|
||||
itemsNumber.getEditText().setFilters(new InputFilter[]{
|
||||
new InputFilter (){
|
||||
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)
|
||||
if (input <= 200 && input > 0)
|
||||
return null;
|
||||
} catch (NumberFormatException nfe) {
|
||||
Toast.makeText(getActivity(), R.string.items_number_should_be_number, Toast.LENGTH_LONG).show();
|
||||
@ -174,6 +170,57 @@ public class SettingsActivity extends AppCompatPreferenceActivity { //NOSONAR
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
if (id == android.R.id.home) {
|
||||
getActivity().finish();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public static class ArticleViewerPreferenceFragment extends PreferenceFragment {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
addPreferencesFromResource(R.xml.pref_viewer);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
final EditTextPreference fontSize = (EditTextPreference) findPreference("reader_font_size");
|
||||
fontSize.getEditText().addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
try {
|
||||
fontSize.getEditText().setTextSize(Integer.parseInt(editable.toString()));
|
||||
} catch (NumberFormatException e) {}
|
||||
}
|
||||
});
|
||||
fontSize.getEditText().setFilters(new InputFilter[]{
|
||||
new InputFilter() {
|
||||
|
||||
@Override
|
||||
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
|
||||
try {
|
||||
int input = Integer.parseInt(dest.toString() + source.toString());
|
||||
if (input > 0)
|
||||
return null;
|
||||
} catch (NumberFormatException nfe) {}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -195,7 +242,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity { //NOSONAR
|
||||
addPreferencesFromResource(R.xml.pref_debug);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
SharedPreferences pref = getActivity().getSharedPreferences(Config.Companion.getSettingsName(), Context.MODE_PRIVATE);
|
||||
SharedPreferences pref = getActivity().getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE);
|
||||
final String id = pref.getString("unique_id", "...");
|
||||
|
||||
final Preference identifier = findPreference("debug_identifier");
|
||||
@ -205,12 +252,15 @@ public class SettingsActivity extends AppCompatPreferenceActivity { //NOSONAR
|
||||
identifier.setOnPreferenceClickListener(new OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
if (clipboard != null) {
|
||||
ClipData clip = ClipData.newPlainText("Selfoss unique id", id);
|
||||
clipboard.setPrimaryClip(clip);
|
||||
|
||||
Toast.makeText(getActivity(), R.string.unique_id_to_clipboard, Toast.LENGTH_LONG).show();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
});
|
||||
identifier.setTitle(id);
|
||||
}
|
||||
@ -243,10 +293,10 @@ public class SettingsActivity extends AppCompatPreferenceActivity { //NOSONAR
|
||||
addPreferencesFromResource(R.xml.pref_links);
|
||||
setHasOptionsMenu(true);
|
||||
|
||||
findPreference( "trackerLink" ).setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
findPreference("trackerLink").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
openUrl(Uri.parse(BuildConfig.TRACKER_URL));
|
||||
openUrl(Uri.parse(Config.trackerUrl));
|
||||
return true;
|
||||
}
|
||||
});
|
||||
@ -254,7 +304,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity { //NOSONAR
|
||||
findPreference("sourceLink").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
openUrl(Uri.parse(BuildConfig.SOURCE_URL));
|
||||
openUrl(Uri.parse(Config.sourceUrl));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
@ -262,7 +312,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity { //NOSONAR
|
||||
findPreference("translation").setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
|
||||
@Override
|
||||
public boolean onPreferenceClick(Preference preference) {
|
||||
openUrl(Uri.parse(BuildConfig.TRANSLATION_URL));
|
||||
openUrl(Uri.parse(Config.translationUrl));
|
||||
return false;
|
||||
}
|
||||
});
|
||||
@ -279,17 +329,62 @@ public class SettingsActivity extends AppCompatPreferenceActivity { //NOSONAR
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public static class ThemePreferenceFragment extends PreferenceFragment {
|
||||
@Override
|
||||
public void onHeaderClick(Header header, int position) {
|
||||
super.onHeaderClick(header, position);
|
||||
if (header.id == R.id.theme_change) {
|
||||
Intent intent = ScoopSettingsActivity.createIntent(getApplicationContext());
|
||||
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
getApplicationContext().startActivity(intent);
|
||||
finish();
|
||||
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 == android.R.id.home) {
|
||||
getActivity().finish();
|
||||
return true;
|
||||
} else if (id == R.id.clear) {
|
||||
SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getActivity());
|
||||
SharedPreferences.Editor editor = pref.edit();
|
||||
editor.remove("color_primary");
|
||||
editor.remove("color_primary_dark");
|
||||
editor.remove("color_accent");
|
||||
editor.remove("color_accent_dark");
|
||||
editor.remove("dark_theme");
|
||||
editor.apply();
|
||||
getActivity().finish();
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||
inflater.inflate(R.menu.settings_theme, menu);
|
||||
}
|
||||
}
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||
public static class OfflinePreferenceFragment extends PreferenceFragment {
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
addPreferencesFromResource(R.xml.pref_offline);
|
||||
setHasOptionsMenu(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
if (id == android.R.id.home) {
|
||||
getActivity().finish();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
|
@ -2,49 +2,68 @@ package apps.amine.bou.readerforselfoss.themes
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.support.annotation.ColorInt
|
||||
import android.preference.PreferenceManager
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.appcompat.view.ContextThemeWrapper
|
||||
import android.util.TypedValue
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
|
||||
class AppColors(a: Activity) {
|
||||
@ColorInt val accent: Int
|
||||
@ColorInt val dark: Int
|
||||
@ColorInt val primary: Int
|
||||
@ColorInt val cardBackground: Int
|
||||
@ColorInt val windowBackground: Int
|
||||
|
||||
@ColorInt val colorPrimary: Int
|
||||
@ColorInt val colorPrimaryDark: Int
|
||||
@ColorInt val colorAccent: Int
|
||||
@ColorInt val colorAccentDark: Int
|
||||
@ColorInt val cardBackgroundColor: Int
|
||||
@ColorInt val colorBackground: Int
|
||||
val isDarkTheme: Boolean
|
||||
|
||||
init {
|
||||
val sharedPref = PreferenceManager.getDefaultSharedPreferences(a)
|
||||
|
||||
colorPrimary =
|
||||
sharedPref.getInt(
|
||||
"color_primary",
|
||||
a.resources.getColor(R.color.colorPrimary)
|
||||
)
|
||||
colorPrimaryDark =
|
||||
sharedPref.getInt(
|
||||
"color_primary_dark",
|
||||
a.resources.getColor(R.color.colorPrimaryDark)
|
||||
)
|
||||
colorAccent =
|
||||
sharedPref.getInt(
|
||||
"color_accent",
|
||||
a.resources.getColor(R.color.colorAccent)
|
||||
)
|
||||
colorAccentDark =
|
||||
sharedPref.getInt(
|
||||
"color_accent_dark",
|
||||
a.resources.getColor(R.color.colorAccentDark)
|
||||
)
|
||||
isDarkTheme =
|
||||
sharedPref.getBoolean(
|
||||
"dark_theme",
|
||||
false
|
||||
)
|
||||
|
||||
colorBackground = if (isDarkTheme) {
|
||||
a.setTheme(R.style.NoBarDark)
|
||||
R.color.darkBackground
|
||||
} else {
|
||||
a.setTheme(R.style.NoBar)
|
||||
android.R.color.background_light
|
||||
}
|
||||
|
||||
val wrapper = Context::class.java
|
||||
val method = wrapper!!.getMethod("getThemeResId")
|
||||
method.isAccessible = true
|
||||
|
||||
isDarkTheme = when (method.invoke(a.baseContext)) {
|
||||
R.style.NoBarTealOrangeDark,
|
||||
R.style.NoBarDark,
|
||||
R.style.NoBarBlueAmberDark,
|
||||
R.style.NoBarGreyOrangeDark,
|
||||
R.style.NoBarIndigoPinkDark,
|
||||
R.style.NoBarRedTealDark,
|
||||
R.style.NoBarCyanPinkDark -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
val typedAccent = TypedValue()
|
||||
val typedAccentDark = TypedValue()
|
||||
val typedPrimary = TypedValue()
|
||||
val typedCardBackground = TypedValue()
|
||||
val typedWindowBackground = TypedValue()
|
||||
|
||||
a.theme.resolveAttribute(R.attr.colorAccent, typedAccent, true)
|
||||
a.theme.resolveAttribute(R.attr.colorAccent, typedAccent, true)
|
||||
a.theme.resolveAttribute(R.attr.colorPrimary, typedPrimary, true)
|
||||
a.theme.resolveAttribute(R.attr.cardBackgroundColor, typedCardBackground, true)
|
||||
a.theme.resolveAttribute(android.R.attr.colorBackground, typedWindowBackground, true)
|
||||
accent = typedAccent.data
|
||||
dark = typedAccentDark.data
|
||||
primary = typedPrimary.data
|
||||
cardBackground = typedCardBackground.data
|
||||
windowBackground = typedWindowBackground.data
|
||||
|
||||
cardBackgroundColor = typedCardBackground.data
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,8 @@
|
||||
package apps.amine.bou.readerforselfoss.themes
|
||||
|
||||
enum class Toppings(val value: Int) {
|
||||
PRIMARY(1),
|
||||
PRIMARY_DARK(2),
|
||||
ACCENT(3),
|
||||
ACCENT_DARK(4)
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
package apps.amine.bou.readerforselfoss.transformers
|
||||
|
||||
import android.support.v4.view.ViewPager
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import android.view.View
|
||||
|
||||
class DepthPageTransformer : ViewPager.PageTransformer {
|
||||
|
@ -0,0 +1,12 @@
|
||||
package apps.amine.bou.readerforselfoss.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.preference.PreferenceManager
|
||||
import org.acra.ErrorReporter
|
||||
|
||||
fun ErrorReporter.maybeHandleSilentException(throwable: Throwable, ctx: Context) {
|
||||
val sharedPref = PreferenceManager.getDefaultSharedPreferences(ctx)
|
||||
if (sharedPref.getBoolean("acra_should_log", false)) {
|
||||
this.handleSilentException(throwable)
|
||||
}
|
||||
}
|
@ -2,60 +2,11 @@ package apps.amine.bou.readerforselfoss.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import android.support.v7.app.AlertDialog
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
|
||||
|
||||
fun String?.isEmptyOrNullOrNullString(): Boolean =
|
||||
this == null || this == "null" || this.isEmpty()
|
||||
|
||||
fun Context.checkApkVersion(
|
||||
settings: SharedPreferences,
|
||||
editor: SharedPreferences.Editor,
|
||||
mFirebaseRemoteConfig: FirebaseRemoteConfig
|
||||
) = {
|
||||
fun isThereAnUpdate() {
|
||||
val APK_LINK = "github_apk"
|
||||
|
||||
val apkLink = mFirebaseRemoteConfig.getString(APK_LINK)
|
||||
val storedLink = settings.getString(APK_LINK, "")
|
||||
if (apkLink != storedLink && !apkLink.isEmpty()) {
|
||||
val alertDialog = AlertDialog.Builder(this).create()
|
||||
alertDialog.setTitle(getString(R.string.new_apk_available_title))
|
||||
alertDialog.setMessage(getString(R.string.new_apk_available_message))
|
||||
alertDialog.setButton(
|
||||
AlertDialog.BUTTON_POSITIVE,
|
||||
getString(R.string.new_apk_available_get)
|
||||
) { _, _ ->
|
||||
editor.putString(APK_LINK, apkLink)
|
||||
editor.apply()
|
||||
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(apkLink))
|
||||
startActivity(browserIntent)
|
||||
}
|
||||
alertDialog.setButton(
|
||||
AlertDialog.BUTTON_NEUTRAL, getString(R.string.new_apk_available_no),
|
||||
{ dialog, _ ->
|
||||
editor.putString(APK_LINK, apkLink)
|
||||
editor.apply()
|
||||
dialog.dismiss()
|
||||
}
|
||||
)
|
||||
alertDialog.show()
|
||||
}
|
||||
}
|
||||
|
||||
mFirebaseRemoteConfig.fetch(43200)
|
||||
.addOnCompleteListener { task ->
|
||||
if (task.isSuccessful) {
|
||||
mFirebaseRemoteConfig.activateFetched()
|
||||
}
|
||||
|
||||
isThereAnUpdate()
|
||||
}
|
||||
}
|
||||
|
||||
fun String.longHash(): Long {
|
||||
var h = 98764321261L
|
||||
val l = this.length
|
||||
|
@ -26,7 +26,15 @@ class Config(c: Context) {
|
||||
get() = settings.getString("httpPassword", "")
|
||||
|
||||
companion object {
|
||||
val settingsName = "paramsselfoss"
|
||||
const val settingsName = "paramsselfoss"
|
||||
|
||||
const val feedbackEmail = "aminecmi@gmail.com"
|
||||
|
||||
const val translationUrl = "https://crwd.in/readerforselfoss"
|
||||
|
||||
const val sourceUrl = "https://github.com/aminecmi/ReaderforSelfoss"
|
||||
|
||||
const val trackerUrl = "https://github.com/aminecmi/ReaderforSelfoss/issues"
|
||||
|
||||
fun logoutAndRedirect(
|
||||
c: Context,
|
||||
|
@ -7,7 +7,7 @@ import javax.net.ssl.SSLContext
|
||||
import javax.net.ssl.TrustManager
|
||||
import javax.net.ssl.X509TrustManager
|
||||
|
||||
fun getUnsafeHttpClient() =
|
||||
fun getUnsafeHttpClient(): OkHttpClient.Builder =
|
||||
try {
|
||||
// Create a trust manager that does not validate certificate chains
|
||||
val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
|
||||
|
@ -1,15 +1,21 @@
|
||||
package apps.amine.bou.readerforselfoss.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.text.format.DateUtils
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
import org.acra.ACRA
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
fun String.toTextDrawableString(): String {
|
||||
fun String.toTextDrawableString(c: Context): String {
|
||||
val textDrawable = StringBuilder()
|
||||
for (s in this.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()) {
|
||||
for (s in this.split(" ".toRegex()).filter { !it.isEmpty() }.toTypedArray()) {
|
||||
try {
|
||||
textDrawable.append(s[0])
|
||||
} catch (e: StringIndexOutOfBoundsException) {
|
||||
ACRA.getErrorReporter().maybeHandleSilentException(e, c)
|
||||
}
|
||||
}
|
||||
return textDrawable.toString()
|
||||
}
|
||||
@ -29,3 +35,17 @@ fun Item.sourceAndDateText(): String {
|
||||
|
||||
return this.sourcetitle + formattedDate
|
||||
}
|
||||
|
||||
fun Item.toggleStar(): Item {
|
||||
this.starred = !this.starred
|
||||
return this
|
||||
}
|
||||
|
||||
fun List<Item>.flattenTags(): List<Item> =
|
||||
this.flatMap {
|
||||
val item = it
|
||||
val tags: List<String> = it.tags.split(",")
|
||||
tags.map {
|
||||
item.copy(tags = it.trim())
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.BitmapFactory
|
||||
import android.net.Uri
|
||||
import android.support.customtabs.CustomTabsIntent
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import android.util.Patterns
|
||||
import android.widget.Toast
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
|
@ -1,53 +0,0 @@
|
||||
package apps.amine.bou.readerforselfoss.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.support.design.widget.CoordinatorLayout
|
||||
import android.support.design.widget.FloatingActionButton
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
|
||||
class ScrollAwareFABBehavior(
|
||||
context: Context,
|
||||
attrs: AttributeSet
|
||||
) : CoordinatorLayout.Behavior<FloatingActionButton>() {
|
||||
|
||||
override fun onStartNestedScroll(
|
||||
coordinatorLayout: CoordinatorLayout,
|
||||
child: FloatingActionButton,
|
||||
directTargetChild: View,
|
||||
target: View,
|
||||
nestedScrollAxes: Int
|
||||
): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onNestedScroll(
|
||||
coordinatorLayout: CoordinatorLayout,
|
||||
child: FloatingActionButton,
|
||||
target: View,
|
||||
dxConsumed: Int,
|
||||
dyConsumed: Int,
|
||||
dxUnconsumed: Int,
|
||||
dyUnconsumed: Int
|
||||
) {
|
||||
super.onNestedScroll(
|
||||
coordinatorLayout,
|
||||
child,
|
||||
target,
|
||||
dxConsumed,
|
||||
dyConsumed,
|
||||
dxUnconsumed,
|
||||
dyUnconsumed
|
||||
)
|
||||
if (dyConsumed > 0 && child.visibility == View.VISIBLE) {
|
||||
child.hide(object : FloatingActionButton.OnVisibilityChangedListener() {
|
||||
override fun onHidden(fab: FloatingActionButton?) {
|
||||
super.onHidden(fab)
|
||||
fab!!.visibility = View.INVISIBLE
|
||||
}
|
||||
})
|
||||
} else if (dyConsumed < 0 && child.visibility != View.VISIBLE) {
|
||||
child.show()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package apps.amine.bou.readerforselfoss.utils
|
||||
|
||||
import android.content.res.Resources
|
||||
|
||||
val Int.toPx: Int
|
||||
get() = (this * Resources.getSystem().displayMetrics.density).toInt()
|
||||
|
||||
val Int.toDp: Int
|
||||
get() = (this / Resources.getSystem().displayMetrics.density).toInt()
|
@ -1,15 +1,15 @@
|
||||
package apps.amine.bou.readerforselfoss.utils.customtabs;
|
||||
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.customtabs.CustomTabsClient;
|
||||
import android.support.customtabs.CustomTabsIntent;
|
||||
import android.support.customtabs.CustomTabsServiceConnection;
|
||||
import android.support.customtabs.CustomTabsSession;
|
||||
import androidx.browser.customtabs.CustomTabsClient;
|
||||
import androidx.browser.customtabs.CustomTabsIntent;
|
||||
import androidx.browser.customtabs.CustomTabsServiceConnection;
|
||||
import androidx.browser.customtabs.CustomTabsSession;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* This is a helper class to manage the connection to the Custom Tabs Service.
|
||||
@ -48,6 +48,7 @@ public class CustomTabActivityHelper implements ServiceConnectionCallback {
|
||||
|
||||
/**
|
||||
* Unbinds the Activity from the Custom Tabs Service.
|
||||
*
|
||||
* @param activity the activity that is connected to the service.
|
||||
*/
|
||||
public void unbindCustomTabsService(Activity activity) {
|
||||
@ -74,6 +75,7 @@ public class CustomTabActivityHelper implements ServiceConnectionCallback {
|
||||
|
||||
/**
|
||||
* Register a Callback to be called when connected or disconnected from the Custom Tabs Service.
|
||||
*
|
||||
* @param connectionCallback
|
||||
*/
|
||||
public void setConnectionCallback(ConnectionCallback connectionCallback) {
|
||||
@ -82,6 +84,7 @@ public class CustomTabActivityHelper implements ServiceConnectionCallback {
|
||||
|
||||
/**
|
||||
* Binds the Activity to the Custom Tabs Service.
|
||||
*
|
||||
* @param activity the activity to be binded to the service.
|
||||
*/
|
||||
public void bindCustomTabsService(Activity activity) {
|
||||
@ -95,16 +98,15 @@ public class CustomTabActivityHelper implements ServiceConnectionCallback {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see {@link CustomTabsSession#mayLaunchUrl(Uri, Bundle, List)}.
|
||||
* @return true if call to mayLaunchUrl was accepted.
|
||||
* @see {@link CustomTabsSession#mayLaunchUrl(Uri, Bundle, List)}.
|
||||
*/
|
||||
public boolean mayLaunchUrl(Uri uri, Bundle extras, List<Bundle> otherLikelyBundles) {
|
||||
if (mClient == null) return false;
|
||||
|
||||
CustomTabsSession session = getSession();
|
||||
if (session == null) return false;
|
||||
return session != null && session.mayLaunchUrl(uri, extras, otherLikelyBundles);
|
||||
|
||||
return session.mayLaunchUrl(uri, extras, otherLikelyBundles);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -142,7 +144,6 @@ public class CustomTabActivityHelper implements ServiceConnectionCallback {
|
||||
*/
|
||||
public interface CustomTabFallback {
|
||||
/**
|
||||
*
|
||||
* @param activity The Activity that wants to open the Uri.
|
||||
* @param uri The uri to be opened by the fallback.
|
||||
*/
|
||||
|
@ -1,19 +1,19 @@
|
||||
package apps.amine.bou.readerforselfoss.utils.customtabs;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.net.Uri;
|
||||
import android.support.customtabs.CustomTabsService;
|
||||
import androidx.browser.customtabs.CustomTabsService;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import apps.amine.bou.readerforselfoss.utils.customtabs.helpers.KeepAliveService;
|
||||
|
||||
@SuppressWarnings("ALL")
|
||||
@ -28,7 +28,8 @@ class CustomTabsHelper {
|
||||
|
||||
private static String sPackageNameToUse;
|
||||
|
||||
private CustomTabsHelper() {}
|
||||
private CustomTabsHelper() {
|
||||
}
|
||||
|
||||
public static void addKeepAliveExtra(Context context, Intent intent) {
|
||||
Intent keepAliveIntent = new Intent().setClassName(
|
||||
@ -40,7 +41,7 @@ class CustomTabsHelper {
|
||||
* Goes through all apps that handle VIEW intents and have a warmup service. Picks
|
||||
* the one chosen by the user if there is one, otherwise makes a best effort to return a
|
||||
* valid package name.
|
||||
*
|
||||
* <p>
|
||||
* This is <strong>not</strong> threadsafe.
|
||||
*
|
||||
* @param context {@link Context} to use for accessing {@link PackageManager}.
|
||||
@ -94,6 +95,7 @@ class CustomTabsHelper {
|
||||
|
||||
/**
|
||||
* Used to check whether there is a specialized handler for a given intent.
|
||||
*
|
||||
* @param intent The intent to check with.
|
||||
* @return Whether there is a specialized handler for the given intent.
|
||||
*/
|
||||
|
@ -1,11 +1,11 @@
|
||||
package apps.amine.bou.readerforselfoss.utils.customtabs;
|
||||
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.support.customtabs.CustomTabsClient;
|
||||
import android.support.customtabs.CustomTabsServiceConnection;
|
||||
import androidx.browser.customtabs.CustomTabsClient;
|
||||
import androidx.browser.customtabs.CustomTabsServiceConnection;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* Implementation for the CustomTabsServiceConnection that avoids leaking the
|
||||
|
@ -1,12 +1,13 @@
|
||||
package apps.amine.bou.readerforselfoss.utils.customtabs;
|
||||
|
||||
|
||||
import android.support.customtabs.CustomTabsClient;
|
||||
import androidx.browser.customtabs.CustomTabsClient;
|
||||
|
||||
|
||||
public interface ServiceConnectionCallback {
|
||||
/**
|
||||
* Called when the service is connected.
|
||||
*
|
||||
* @param client a CustomTabsClient
|
||||
*/
|
||||
void onServiceConnected(CustomTabsClient client);
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* From https://github.com/mikepenz/MaterialDrawer/blob/develop/app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/CustomBaseViewHolder.java */
|
||||
package apps.amine.bou.readerforselfoss.utils.drawer
|
||||
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
|
@ -2,10 +2,10 @@
|
||||
package apps.amine.bou.readerforselfoss.utils.drawer
|
||||
|
||||
import android.net.Uri
|
||||
import android.support.annotation.ColorInt
|
||||
import android.support.annotation.ColorRes
|
||||
import android.support.annotation.StringRes
|
||||
import android.support.v7.widget.RecyclerView
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
|
||||
import com.mikepenz.materialdrawer.holder.ColorHolder
|
||||
import com.mikepenz.materialdrawer.holder.ImageHolder
|
||||
@ -15,7 +15,8 @@ import com.mikepenz.materialdrawer.util.DrawerImageLoader
|
||||
import com.mikepenz.materialdrawer.util.DrawerUIUtils
|
||||
import com.mikepenz.materialize.util.UIUtils
|
||||
|
||||
abstract class CustomUrlBasePrimaryDrawerItem<T, VH : RecyclerView.ViewHolder> : BaseDrawerItem<T, VH>() {
|
||||
abstract class CustomUrlBasePrimaryDrawerItem<T, VH : RecyclerView.ViewHolder> :
|
||||
BaseDrawerItem<T, VH>() {
|
||||
fun withIcon(url: String): T {
|
||||
this.icon = ImageHolder(url)
|
||||
return this as T
|
||||
|
@ -1,8 +1,8 @@
|
||||
/* From https://github.com/mikepenz/MaterialDrawer/blob/develop/app/src/main/java/com/mikepenz/materialdrawer/app/drawerItems/CustomUrlPrimaryDrawerItem.java */
|
||||
package apps.amine.bou.readerforselfoss.utils.drawer
|
||||
|
||||
import android.support.annotation.LayoutRes
|
||||
import android.support.annotation.StringRes
|
||||
import androidx.annotation.LayoutRes
|
||||
import androidx.annotation.StringRes
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
@ -10,7 +10,9 @@ import com.mikepenz.materialdrawer.holder.BadgeStyle
|
||||
import com.mikepenz.materialdrawer.holder.StringHolder
|
||||
import com.mikepenz.materialdrawer.model.interfaces.ColorfulBadgeable
|
||||
|
||||
class CustomUrlPrimaryDrawerItem : CustomUrlBasePrimaryDrawerItem<CustomUrlPrimaryDrawerItem, CustomUrlPrimaryDrawerItem.ViewHolder>(), ColorfulBadgeable<CustomUrlPrimaryDrawerItem> {
|
||||
class CustomUrlPrimaryDrawerItem :
|
||||
CustomUrlBasePrimaryDrawerItem<CustomUrlPrimaryDrawerItem, CustomUrlPrimaryDrawerItem.ViewHolder>(),
|
||||
ColorfulBadgeable<CustomUrlPrimaryDrawerItem> {
|
||||
protected var mBadge: StringHolder = StringHolder("")
|
||||
protected var mBadgeStyle = BadgeStyle()
|
||||
|
||||
|
@ -2,7 +2,7 @@ package apps.amine.bou.readerforselfoss.utils.glide
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.support.v4.graphics.drawable.RoundedBitmapDrawableFactory
|
||||
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory
|
||||
import android.widget.ImageView
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
|
@ -0,0 +1,72 @@
|
||||
package apps.amine.bou.readerforselfoss.utils.persistence
|
||||
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Source
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Tag
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.ItemEntity
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.SourceEntity
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.TagEntity
|
||||
|
||||
fun TagEntity.toView(): Tag =
|
||||
Tag(
|
||||
this.tag,
|
||||
this.color,
|
||||
this.unread
|
||||
)
|
||||
|
||||
fun SourceEntity.toView(): Source =
|
||||
Source(
|
||||
this.id,
|
||||
this.title,
|
||||
this.tags,
|
||||
this.spout,
|
||||
this.error,
|
||||
this.icon
|
||||
)
|
||||
|
||||
fun Source.toEntity(): SourceEntity =
|
||||
SourceEntity(
|
||||
this.id,
|
||||
this.title,
|
||||
this.tags,
|
||||
this.spout,
|
||||
this.error,
|
||||
this.icon.orEmpty()
|
||||
)
|
||||
|
||||
fun Tag.toEntity(): TagEntity =
|
||||
TagEntity(
|
||||
this.tag,
|
||||
this.color,
|
||||
this.unread
|
||||
)
|
||||
|
||||
fun ItemEntity.toView(): Item =
|
||||
Item(
|
||||
this.id,
|
||||
this.datetime,
|
||||
this.title,
|
||||
this.content,
|
||||
this.unread,
|
||||
this.starred,
|
||||
this.thumbnail,
|
||||
this.icon,
|
||||
this.link,
|
||||
this.sourcetitle,
|
||||
this.tags
|
||||
)
|
||||
|
||||
fun Item.toEntity(): ItemEntity =
|
||||
ItemEntity(
|
||||
this.id,
|
||||
this.datetime,
|
||||
this.title,
|
||||
this.content,
|
||||
this.unread,
|
||||
this.starred,
|
||||
this.thumbnail,
|
||||
this.icon,
|
||||
this.link,
|
||||
this.sourcetitle,
|
||||
this.tags
|
||||
)
|
After Width: | Height: | Size: 206 B |
BIN
app/src/main/res/drawable-hdpi/ic_fiber_new.png
Normal file
After Width: | Height: | Size: 324 B |
BIN
app/src/main/res/drawable-hdpi/ic_history.png
Normal file
After Width: | Height: | Size: 551 B |
After Width: | Height: | Size: 523 B |
After Width: | Height: | Size: 134 B |
BIN
app/src/main/res/drawable-mdpi/ic_fiber_new.png
Normal file
After Width: | Height: | Size: 215 B |
BIN
app/src/main/res/drawable-mdpi/ic_history.png
Normal file
After Width: | Height: | Size: 352 B |
After Width: | Height: | Size: 361 B |
After Width: | Height: | Size: 174 B |
BIN
app/src/main/res/drawable-xhdpi/ic_fiber_new.png
Normal file
After Width: | Height: | Size: 327 B |
BIN
app/src/main/res/drawable-xhdpi/ic_history.png
Normal file
After Width: | Height: | Size: 684 B |
After Width: | Height: | Size: 660 B |
After Width: | Height: | Size: 255 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_fiber_new.png
Normal file
After Width: | Height: | Size: 490 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_history.png
Normal file
After Width: | Height: | Size: 986 B |
After Width: | Height: | Size: 982 B |
After Width: | Height: | Size: 311 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_fiber_new.png
Normal file
After Width: | Height: | Size: 567 B |
BIN
app/src/main/res/drawable-xxxhdpi/ic_history.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
@ -1,4 +0,0 @@
|
||||
<vector android:height="24dp" android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FFFFFF" android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
|
||||
</vector>
|
@ -1,4 +0,0 @@
|
||||
<vector android:height="24dp" android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="#FFFFFF" android:pathData="M1,21h4L5,9L1,9v12zM23,10c0,-1.1 -0.9,-2 -2,-2h-6.31l0.95,-4.57 0.03,-0.32c0,-0.41 -0.17,-0.79 -0.44,-1.06L14.17,1 7.59,7.59C7.22,7.95 7,8.45 7,9v10c0,1.1 0.9,2 2,2h9c0.83,0 1.54,-0.5 1.84,-1.22l3.02,-7.05c0.09,-0.23 0.14,-0.47 0.14,-0.73v-1.91l-0.01,-0.01L23,10z"/>
|
||||
</vector>
|
@ -1,36 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
<ScrollView xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="apps.amine.bou.readerforselfoss.AddSourceActivity"
|
||||
tools:context="apps.amine.bou.readerforselfoss.AddSourceActivity">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
<android.support.design.widget.AppBarLayout
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:theme="@style/ToolBarStyle"
|
||||
app:popupTheme="?attr/toolbarPopupTheme" />
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:paddingBottom="@dimen/activity_vertical_margin"
|
||||
android:paddingLeft="@dimen/activity_horizontal_margin"
|
||||
android:paddingRight="@dimen/activity_horizontal_margin"
|
||||
android:paddingTop="@dimen/activity_vertical_margin"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="match_parent">
|
||||
<android.support.constraint.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
@ -119,19 +120,20 @@
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:layout_constraintVertical_bias="0.0"/>
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<ProgressBar
|
||||
style="?android:attr/progressBarStyleLarge"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/progress"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
style="?android:attr/progressBarStyleLarge"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
android:visibility="visible"/>
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
tools:visibility="gone" />
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
@ -30,13 +30,13 @@
|
||||
app:prompt_view_background_color="?attr/colorAccent"
|
||||
app:prompt_view_thanks_display_time_ms="2000"/>
|
||||
|
||||
<android.support.design.widget.CoordinatorLayout
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/coordLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/promptView">
|
||||
|
||||
<android.support.design.widget.CoordinatorLayout
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/intern_coordLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
@ -46,18 +46,18 @@
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<android.support.design.widget.AppBarLayout
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:theme="@style/ToolBarStyle"
|
||||
app:popupTheme="?attr/toolbarPopupTheme" />
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/drawer_layout"
|
||||
@ -66,7 +66,7 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<android.support.v4.widget.SwipeRefreshLayout
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/swipeRefreshLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
@ -86,28 +86,30 @@
|
||||
android:text="@string/nothing_here"
|
||||
android:textAlignment="center"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Headline"
|
||||
android:background="@color/transparent"
|
||||
android:background="@android:color/transparent"
|
||||
android:visibility="gone" />
|
||||
<android.support.v7.widget.RecyclerView
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@color/transparent"
|
||||
android:background="@android:color/transparent"
|
||||
android:clipToPadding="false"
|
||||
android:paddingBottom="60dp"
|
||||
android:scrollbars="vertical"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
|
||||
</LinearLayout>
|
||||
|
||||
</android.support.v4.widget.SwipeRefreshLayout>
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
</FrameLayout>
|
||||
</LinearLayout>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
<com.ashokvarma.bottomnavigation.BottomNavigationBar
|
||||
android:layout_gravity="bottom"
|
||||
android:id="@+id/bottomBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="60dp"/>
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</RelativeLayout>
|
@ -6,18 +6,18 @@
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical"
|
||||
tools:context="apps.amine.bou.readerforselfoss.LoginActivity">
|
||||
<android.support.design.widget.AppBarLayout
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:theme="@style/ToolBarStyle"
|
||||
app:popupTheme="?attr/toolbarPopupTheme" />
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@ -45,7 +45,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<android.support.design.widget.TextInputLayout
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/urlLayout"
|
||||
@ -60,7 +60,7 @@
|
||||
android:inputType="textUri"
|
||||
android:maxLines="1" />
|
||||
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<Switch
|
||||
android:text="@string/withLoginSwitch"
|
||||
@ -69,7 +69,7 @@
|
||||
android:id="@+id/withLogin"
|
||||
android:layout_weight="1"/>
|
||||
|
||||
<android.support.design.widget.TextInputLayout
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/loginLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -83,9 +83,9 @@
|
||||
android:inputType="text"
|
||||
android:maxLines="1" />
|
||||
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<android.support.design.widget.TextInputLayout
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/passwordLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -99,7 +99,7 @@
|
||||
android:inputType="textPassword"
|
||||
android:maxLines="1" />
|
||||
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/withHttpLogin"
|
||||
@ -108,20 +108,21 @@
|
||||
android:layout_weight="1"
|
||||
android:text="@string/withHttpLoginSwitch" />
|
||||
|
||||
<android.support.design.widget.TextInputLayout
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/httpLoginInput"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:visibility="gone">
|
||||
|
||||
<EditText
|
||||
android:inputType="text"
|
||||
android:id="@+id/httpLoginView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/prompt_http_login" />
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<android.support.design.widget.TextInputLayout
|
||||
<com.google.android.material.textfield.TextInputLayout
|
||||
android:id="@+id/httpPasswordInput"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
@ -133,7 +134,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/prompt_http_password"
|
||||
android:inputType="textPassword" />
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
</com.google.android.material.textfield.TextInputLayout>
|
||||
|
||||
<Switch
|
||||
android:id="@+id/withSelfhostedCert"
|
||||
|
@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="apps.amine.bou.readerforselfoss.MainActivity">
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
@ -1,11 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<android.support.design.widget.AppBarLayout
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appBarLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@ -13,19 +12,20 @@
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolBar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:popupTheme="?attr/toolbarPopupTheme"
|
||||
app:theme="@style/ToolBarStyle" />
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<android.support.v4.view.ViewPager
|
||||
<androidx.viewpager.widget.ViewPager
|
||||
android:id="@+id/pager"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:paddingTop="20dp"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
@ -37,8 +37,8 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="20dp"
|
||||
android:background="#55000000"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent" />
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/pager" />
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
@ -1,35 +1,33 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<android.support.design.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context="apps.amine.bou.readerforselfoss.SourcesActivity"
|
||||
xmlns:fab="http://schemas.android.com/apk/res-auto">
|
||||
<android.support.design.widget.AppBarLayout
|
||||
tools:context="apps.amine.bou.readerforselfoss.SourcesActivity">
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:theme="@style/ToolBarStyle"
|
||||
app:popupTheme="?attr/toolbarPopupTheme" />
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/recyclerView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scrollbars="vertical"
|
||||
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||
</android.support.v7.widget.RecyclerView>
|
||||
</androidx.recyclerview.widget.RecyclerView>
|
||||
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
@ -44,4 +42,4 @@
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
app:layout_behavior="apps.amine.bou.readerforselfoss.utils.ScrollAwareFABBehavior" />
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.v7.widget.CardView
|
||||
<androidx.cardview.widget.CardView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:card_view="http://schemas.android.com/apk/res-auto"
|
||||
@ -18,7 +18,7 @@
|
||||
card_view:cardUseCompatPadding="true"
|
||||
card_view:layout_constraintBottom_toBottomOf="parent">
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
@ -34,7 +34,7 @@
|
||||
app:srcCompat="@drawable/background_splash"
|
||||
card_view:layout_constraintBottom_toTopOf="@+id/constraintLayout" />
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/constraintLayout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
@ -143,7 +143,7 @@
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</android.support.v7.widget.CardView>
|
||||
</androidx.cardview.widget.CardView>
|
@ -1,4 +1,4 @@
|
||||
<android.support.design.widget.CoordinatorLayout
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
@ -6,12 +6,12 @@
|
||||
android:layout_height="match_parent"
|
||||
android:descendantFocusability="blocksDescendants">
|
||||
|
||||
<android.support.v4.widget.NestedScrollView
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/nestedScrollView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<android.support.constraint.ConstraintLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
@ -70,23 +70,9 @@
|
||||
app:layout_constraintTop_toBottomOf="@+id/source"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="16dp"
|
||||
android:layout_marginRight="16dp"
|
||||
android:layout_marginTop="24dp"
|
||||
android:paddingBottom="48dp"
|
||||
android:textColorLink="?attr/colorAccent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintLeft_toLeftOf="parent"
|
||||
app:layout_constraintRight_toRightOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/source"
|
||||
android:visibility="gone"/>
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</android.support.v4.widget.NestedScrollView>
|
||||
</androidx.core.widget.NestedScrollView>
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
@ -101,16 +87,12 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
android:layout_gravity="bottom"
|
||||
app:floatingItemBackground="?attr/colorAccent"
|
||||
app:floatingMenu="@menu/reader_toolbar" />
|
||||
|
||||
<android.support.design.widget.FloatingActionButton
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_gravity="end|bottom|right"
|
||||
android:layout_marginBottom="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
@ -141,4 +123,4 @@
|
||||
android:progressTint="?attr/colorAccent" />
|
||||
</FrameLayout>
|
||||
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
@ -7,18 +7,11 @@
|
||||
android:minHeight="88dp">
|
||||
|
||||
|
||||
<android.support.constraint.Guideline
|
||||
android:id="@+id/guideline4"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
app:layout_constraintGuide_begin="99dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/itemImage"
|
||||
android:layout_width="88dp"
|
||||
android:layout_height="88dp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/guideline4"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/actionBar"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
@ -26,24 +19,21 @@
|
||||
android:id="@+id/title"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:ellipsize="end"
|
||||
android:fontFamily="sans-serif"
|
||||
android:gravity="start"
|
||||
android:maxLines="3"
|
||||
android:ellipsize="end"
|
||||
android:textAlignment="viewStart"
|
||||
android:textAllCaps="false"
|
||||
android:textSize="16sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@+id/sourceTitleAndDate"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toEndOf="@+id/itemImage"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintVertical_bias="0.0"
|
||||
tools:text="Titre" />
|
||||
|
||||
<TextView
|
||||
@ -53,13 +43,15 @@
|
||||
android:layout_marginBottom="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:gravity="start"
|
||||
android:textAlignment="viewStart"
|
||||
android:textSize="14sp"
|
||||
app:layout_constraintBottom_toTopOf="@+id/guideline4"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/actionBar"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toEndOf="@+id/itemImage"
|
||||
app:layout_constraintTop_toBottomOf="@+id/title"
|
||||
tools:text="Google Actualité Il y a 5h" />
|
||||
|
||||
<RelativeLayout
|
||||
@ -71,8 +63,6 @@
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="@+id/guideline4"
|
||||
app:layout_constraintVertical_bias="1.0"
|
||||
tools:visibility="visible">
|
||||
|
||||
<com.like.LikeButton
|
||||
@ -125,4 +115,4 @@
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -1,15 +1,15 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.design.widget.AppBarLayout
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?attr/actionBarSize"
|
||||
app:theme="@style/ToolBarStyle"
|
||||
app:popupTheme="?attr/toolbarPopupTheme" />
|
||||
|
||||
</android.support.design.widget.AppBarLayout>
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
@ -52,4 +52,4 @@
|
||||
android:layout_width="34dp"
|
||||
android:layout_height="34dp"/>
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -6,13 +6,13 @@
|
||||
android:title="@string/menu_home_search"
|
||||
android:icon="@drawable/ic_action_search"
|
||||
app:showAsAction="ifRoom|collapseActionView"
|
||||
app:actionViewClass="android.support.v7.widget.SearchView" />
|
||||
app:actionViewClass="androidx.appcompat.widget.SearchView" />
|
||||
|
||||
<item android:id="@+id/readAll"
|
||||
android:icon="@drawable/ic_done_all_white_24dp"
|
||||
android:title="@string/readAll"
|
||||
android:orderInCategory="1"
|
||||
app:showAsAction="ifRoom"/>
|
||||
app:showAsAction="always"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/refresh"
|
||||
@ -20,11 +20,6 @@
|
||||
android:orderInCategory="99"
|
||||
android:title="@string/menu_home_refresh" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_share_the_app"
|
||||
android:orderInCategory="102"
|
||||
android:title="@string/menu_share_the_app" />
|
||||
|
||||
<item android:id="@+id/action_disconnect"
|
||||
android:title="@string/action_disconnect"
|
||||
android:orderInCategory="104"
|
||||
|
17
app/src/main/res/menu/reader_menu.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:id="@+id/unsave"
|
||||
android:icon="@drawable/heart_on"
|
||||
android:title="@string/remove_to_favs_reader"
|
||||
android:visible="true"
|
||||
app:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/save"
|
||||
android:icon="@drawable/heart_off"
|
||||
android:title="@string/add_to_favs_reader"
|
||||
android:visible="true"
|
||||
app:showAsAction="ifRoom" />
|
||||
</menu>
|
@ -1,8 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/unread_action"
|
||||
android:icon="@drawable/ic_fiber_new"
|
||||
android:title="@string/unmark"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/more_action"
|
||||
android:icon="@drawable/ic_chrome_reader_mode"
|
||||
|
10
app/src/main/res/menu/settings_theme.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item
|
||||
android:id="@+id/clear"
|
||||
android:icon="@drawable/ic_history"
|
||||
android:title="@string/drawer_action_clear"
|
||||
app:showAsAction="ifRoom" />
|
||||
</menu>
|
@ -19,7 +19,6 @@
|
||||
<string name="action_disconnect">"Disconnect"</string>
|
||||
<string name="title_activity_settings">"Settings"</string>
|
||||
<string name="pref_header_general">"General"</string>
|
||||
<string name="pref_switch_actions_tap_title">"Tap action on the articles"</string>
|
||||
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
|
||||
<string name="add_source_hint_url">"Link"</string>
|
||||
<string name="add_source_hint_name">"Name"</string>
|
||||
@ -64,10 +63,6 @@
|
||||
<string name="switch_unread_count">"Display the unread count as a badge for the bottom bar."</string>
|
||||
<string name="switch_unread_count_title">"Display unread count"</string>
|
||||
<string name="display_all_counts_title">"Display count for favorite and read"</string>
|
||||
<string name="menu_share_the_app">"Invite friends"</string>
|
||||
<string name="invitation_title">"Try this app for your Selfoss RSS feeds !"</string>
|
||||
<string name="invitation_message">"I use this app for my Selfoss RSS feeds. You may like it too !"</string>
|
||||
<string name="invitation_cta">"Try the app"</string>
|
||||
<string name="text_wrong_url">"You seem to be trying to use an invalid URL. Make sure it is correct, and if the problem persists, contact me (via the store contact link). Please note that the app needs you to be using Selfoss. You can't access RSS feeds without it."</string>
|
||||
<string name="pref_general_internal_browser_title">"Open links inside the app"</string>
|
||||
<string name="pref_general_internal_browser_on">"Articles will open inside the app"</string>
|
||||
@ -77,26 +72,12 @@
|
||||
<string name="prefer_article_viewer_off">"Will use the internal browser instead of the article viewer"</string>
|
||||
<string name="pref_general_category_links">"Link handling"</string>
|
||||
<string name="pref_general_category_displaying">"Displaying"</string>
|
||||
<string name="pref_general_category_actions">"Actions"</string>
|
||||
<string name="pref_switch_card_view_on">"The articles will be displayed as cards"</string>
|
||||
<string name="pref_switch_card_view_off">"The articles will be displayed as a list"</string>
|
||||
<string name="pref_switch_actions_tap_on">"Displays the action bar under the article"</string>
|
||||
<string name="pref_switch_actions_tap_off">"When selecting an article it will open in your selected browser"</string>
|
||||
<string name="menu_home_refresh">"Update remote"</string>
|
||||
<string name="refresh_success_response">"The remote is updated, you can now reload the articles list"</string>
|
||||
<string name="refresh_failer_message">"The update didn't work, try again later, or check your selfoss logs."</string>
|
||||
<string name="refresh_in_progress">"Refresh in progress"</string>
|
||||
<string name="new_apk_available_title">"A new APK is available."</string>
|
||||
<string name="new_apk_available_message">"A new APK is available to download on the official repository."</string>
|
||||
<string name="new_apk_available_get">"Download now"</string>
|
||||
<string name="new_apk_available_no">"Ignore version"</string>
|
||||
<string name="intro_hello_title">"Hi there !"</string>
|
||||
<string name="intro_hello_message">"Thanks for downloading the app !"</string>
|
||||
<string name="intro_needs_selfoss_title">"Before you start…"</string>
|
||||
<string name="intro_needs_selfoss_message">"You can't use the app without a Selfoss instance."</string>
|
||||
<string name="intro_needs_selfoss_link">"What is Selfoss ?"</string>
|
||||
<string name="intro_all_set_title">"All set !"</string>
|
||||
<string name="intro_all_set_message">"You are ready to use the app. Don't forget to go to the settings page to configure your app, and where you'll find some useful links."</string>
|
||||
<string name="card_height_title">Full height cards</string>
|
||||
<string name="card_height_on">Cards height will adjust to its content</string>
|
||||
<string name="card_height_off">Card height will be fixed</string>
|
||||
@ -109,7 +90,6 @@
|
||||
<string name="drawer_item_tags">Tags</string>
|
||||
<string name="drawer_item_sources">Sources</string>
|
||||
<string name="drawer_action_edit">edit</string>
|
||||
<string name="cache_drawer_error" tools:keep="@string/cache_drawer_error">Couldn\'t cache your drawer data</string>
|
||||
<string name="no_tags_loaded">No tags loaded</string>
|
||||
<string name="no_sources_loaded">No sources loaded</string>
|
||||
<string name="drawer_loading">Loading …</string>
|
||||
@ -118,19 +98,7 @@
|
||||
<string name="base_url_error">There was an issue when trying to communicate with your Selfoss Instance. If the issue persists, please get in touch with me.</string>
|
||||
<string name="pref_header_theme">Themes</string>
|
||||
<string name="default_theme">Default</string>
|
||||
<string name="teal_orange_theme">Teal/Orange/Light</string>
|
||||
<string name="cyan_pink_theme">Cyan/Pink/Light</string>
|
||||
<string name="grey_orange_theme">Grey/Orange/Light</string>
|
||||
<string name="blue_amber_theme">Blue/Amber/Light</string>
|
||||
<string name="indigo_pink_theme">Indigo/Pink/Light</string>
|
||||
<string name="red_teal_theme">Red/Teal/Light</string>
|
||||
<string name="teal_orange_dark_theme">Teal/Orange/Dark</string>
|
||||
<string name="cyan_pink_dark_theme">Cyan/Pink/Dark</string>
|
||||
<string name="default_dark_theme">Default/Dark</string>
|
||||
<string name="grey_orange_dark_theme">Grey/Orange/Dark</string>
|
||||
<string name="blue_amber_dark_theme">Blue/Amber/Dark</string>
|
||||
<string name="indigo_pink_dark_theme">Indigo/Pink/Dark</string>
|
||||
<string name="red_teal_dark_theme">Red/Teal/Dark</string>
|
||||
<string name="pref_header_debug">Debug</string>
|
||||
<string name="login_debug_title">Activate to log login errors</string>
|
||||
<string name="login_debug_on">Any error on the login page will be logged</string>
|
||||
@ -140,6 +108,7 @@
|
||||
<string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string>
|
||||
<string name="pref_selfoss_category">Selfoss Api</string>
|
||||
<string name="pref_api_items_number_title">Loaded items number</string>
|
||||
<string name="pref_hidden_tags">Hidden Tags</string>
|
||||
<string name="read_debug_title">Read articles appearing as unread ?</string>
|
||||
<string name="read_debug_off">No log when marking an item as read</string>
|
||||
<string name="read_debug_on">Api calls will be logged when marking an article as read</string>
|
||||
@ -158,8 +127,25 @@
|
||||
<string name="reader_action_more">Read more</string>
|
||||
<string name="reader_action_open">Open in browser</string>
|
||||
<string name="reader_action_share">Share</string>
|
||||
<string name="pref_switch_actions_pager_scroll">Mark articles as read when swiping between articles.</string>
|
||||
<string name="prefer_webview_in_article_viewer_title">Use a webview in the article viewer.</string>
|
||||
<string name="prefer_webview_in_article_viewer_on">Using the webview for the content of the article viewer. Urls, gifs and images will be better handled, but may be slower.</string>
|
||||
<string name="prefer_webview_in_article_viewer_off">The content of the article will be displayed as simple text with links and images. Urls may not work, and gifs won\'t load.</string>
|
||||
<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="gdpr_dialog_message">The app does not collect any personal data. Every analytics tools were removed. Crash reports sending is now optional, as is the debug logging. Keep in mind that debugging and crash reports are essential for the app development (You can configure everything in Settings > Debug).</string>
|
||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||
<string name="crash_dialog_text">Something went wrong. Please send the report to the developer.</string>
|
||||
<string name="crash_dialog_comment">You can add any helpful details in the comment bellow. Don\'t include any personal data in your comment. You could send me and email with your debug id, and I\'ll keep you posted when the issue is resolved.</string>
|
||||
<string name="pref_acra_alwaysaccept">Automatically send crash reports</string>
|
||||
<string name="pref_acra_alwaysaccept_enabled">Will send crash reports automatically</string>
|
||||
<string name="pref_acra_alwaysaccept_disabled">Will ask everytime when sending crash reports.</string>
|
||||
<string name="pref_debug_crash_reports">Crash reports</string>
|
||||
<string name="pref_debug_debug_logs">Debug logging (these will be sent without a dialog)</string>
|
||||
<string name="acra_login">Enable logging</string>
|
||||
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
||||
<string name="unmark">Mark item as unread</string>
|
||||
</resources>
|
||||
|
@ -19,7 +19,6 @@
|
||||
<string name="action_disconnect">"Disconnect"</string>
|
||||
<string name="title_activity_settings">"Settings"</string>
|
||||
<string name="pref_header_general">"General"</string>
|
||||
<string name="pref_switch_actions_tap_title">"Tap action on the articles"</string>
|
||||
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
|
||||
<string name="add_source_hint_url">"Link"</string>
|
||||
<string name="add_source_hint_name">"Name"</string>
|
||||
@ -64,10 +63,6 @@
|
||||
<string name="switch_unread_count">"Display the unread count as a badge for the bottom bar."</string>
|
||||
<string name="switch_unread_count_title">"Display unread count"</string>
|
||||
<string name="display_all_counts_title">"Display count for favorite and read"</string>
|
||||
<string name="menu_share_the_app">"Invite friends"</string>
|
||||
<string name="invitation_title">"Try this app for your Selfoss RSS feeds !"</string>
|
||||
<string name="invitation_message">"I use this app for my Selfoss RSS feeds. You may like it too !"</string>
|
||||
<string name="invitation_cta">"Try the app"</string>
|
||||
<string name="text_wrong_url">"You seem to be trying to use an invalid URL. Make sure it is correct, and if the problem persists, contact me (via the store contact link). Please note that the app needs you to be using Selfoss. You can't access RSS feeds without it."</string>
|
||||
<string name="pref_general_internal_browser_title">"Open links inside the app"</string>
|
||||
<string name="pref_general_internal_browser_on">"Articles will open inside the app"</string>
|
||||
@ -77,26 +72,12 @@
|
||||
<string name="prefer_article_viewer_off">"Will use the internal browser instead of the article viewer"</string>
|
||||
<string name="pref_general_category_links">"Link handling"</string>
|
||||
<string name="pref_general_category_displaying">"Displaying"</string>
|
||||
<string name="pref_general_category_actions">"Actions"</string>
|
||||
<string name="pref_switch_card_view_on">"The articles will be displayed as cards"</string>
|
||||
<string name="pref_switch_card_view_off">"The articles will be displayed as a list"</string>
|
||||
<string name="pref_switch_actions_tap_on">"Displays the action bar under the article"</string>
|
||||
<string name="pref_switch_actions_tap_off">"When selecting an article it will open in your selected browser"</string>
|
||||
<string name="menu_home_refresh">"Update remote"</string>
|
||||
<string name="refresh_success_response">"The remote is updated, you can now reload the articles list"</string>
|
||||
<string name="refresh_failer_message">"The update didn't work, try again later, or check your selfoss logs."</string>
|
||||
<string name="refresh_in_progress">"Refresh in progress"</string>
|
||||
<string name="new_apk_available_title">"A new APK is available."</string>
|
||||
<string name="new_apk_available_message">"A new APK is available to download on the official repository."</string>
|
||||
<string name="new_apk_available_get">"Download now"</string>
|
||||
<string name="new_apk_available_no">"Ignore version"</string>
|
||||
<string name="intro_hello_title">"Hi there !"</string>
|
||||
<string name="intro_hello_message">"Thanks for downloading the app !"</string>
|
||||
<string name="intro_needs_selfoss_title">"Before you start…"</string>
|
||||
<string name="intro_needs_selfoss_message">"You can't use the app without a Selfoss instance."</string>
|
||||
<string name="intro_needs_selfoss_link">"What is Selfoss ?"</string>
|
||||
<string name="intro_all_set_title">"All set !"</string>
|
||||
<string name="intro_all_set_message">"You are ready to use the app. Don't forget to go to the settings page to configure your app, and where you'll find some useful links."</string>
|
||||
<string name="card_height_title">Full height cards</string>
|
||||
<string name="card_height_on">Cards height will adjust to its content</string>
|
||||
<string name="card_height_off">Card height will be fixed</string>
|
||||
@ -109,7 +90,6 @@
|
||||
<string name="drawer_item_tags">Tags</string>
|
||||
<string name="drawer_item_sources">Sources</string>
|
||||
<string name="drawer_action_edit">edit</string>
|
||||
<string name="cache_drawer_error" tools:keep="@string/cache_drawer_error">Couldn\'t cache your drawer data</string>
|
||||
<string name="no_tags_loaded">No tags loaded</string>
|
||||
<string name="no_sources_loaded">No sources loaded</string>
|
||||
<string name="drawer_loading">Loading …</string>
|
||||
@ -118,19 +98,7 @@
|
||||
<string name="base_url_error">There was an issue when trying to communicate with your Selfoss Instance. If the issue persists, please get in touch with me.</string>
|
||||
<string name="pref_header_theme">Themes</string>
|
||||
<string name="default_theme">Default</string>
|
||||
<string name="teal_orange_theme">Teal/Orange/Light</string>
|
||||
<string name="cyan_pink_theme">Cyan/Pink/Light</string>
|
||||
<string name="grey_orange_theme">Grey/Orange/Light</string>
|
||||
<string name="blue_amber_theme">Blue/Amber/Light</string>
|
||||
<string name="indigo_pink_theme">Indigo/Pink/Light</string>
|
||||
<string name="red_teal_theme">Red/Teal/Light</string>
|
||||
<string name="teal_orange_dark_theme">Teal/Orange/Dark</string>
|
||||
<string name="cyan_pink_dark_theme">Cyan/Pink/Dark</string>
|
||||
<string name="default_dark_theme">Default/Dark</string>
|
||||
<string name="grey_orange_dark_theme">Grey/Orange/Dark</string>
|
||||
<string name="blue_amber_dark_theme">Blue/Amber/Dark</string>
|
||||
<string name="indigo_pink_dark_theme">Indigo/Pink/Dark</string>
|
||||
<string name="red_teal_dark_theme">Red/Teal/Dark</string>
|
||||
<string name="pref_header_debug">Debug</string>
|
||||
<string name="login_debug_title">Activate to log login errors</string>
|
||||
<string name="login_debug_on">Any error on the login page will be logged</string>
|
||||
@ -140,6 +108,7 @@
|
||||
<string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string>
|
||||
<string name="pref_selfoss_category">Selfoss Api</string>
|
||||
<string name="pref_api_items_number_title">Loaded items number</string>
|
||||
<string name="pref_hidden_tags">Hidden Tags</string>
|
||||
<string name="read_debug_title">Read articles appearing as unread ?</string>
|
||||
<string name="read_debug_off">No log when marking an item as read</string>
|
||||
<string name="read_debug_on">Api calls will be logged when marking an article as read</string>
|
||||
@ -158,8 +127,25 @@
|
||||
<string name="reader_action_more">Read more</string>
|
||||
<string name="reader_action_open">Open in browser</string>
|
||||
<string name="reader_action_share">Share</string>
|
||||
<string name="pref_switch_actions_pager_scroll">Mark articles as read when swiping between articles.</string>
|
||||
<string name="prefer_webview_in_article_viewer_title">Use a webview in the article viewer.</string>
|
||||
<string name="prefer_webview_in_article_viewer_on">Using the webview for the content of the article viewer. Urls, gifs and images will be better handled, but may be slower.</string>
|
||||
<string name="prefer_webview_in_article_viewer_off">The content of the article will be displayed as simple text with links and images. Urls may not work, and gifs won\'t load.</string>
|
||||
<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="gdpr_dialog_message">The app does not collect any personal data. Every analytics tools were removed. Crash reports sending is now optional, as is the debug logging. Keep in mind that debugging and crash reports are essential for the app development (You can configure everything in Settings > Debug).</string>
|
||||
<string name="gdpr_dialog_title">The app does not share any personal data about you.</string>
|
||||
<string name="crash_dialog_text">Something went wrong. Please send the report to the developer.</string>
|
||||
<string name="crash_dialog_comment">You can add any helpful details in the comment bellow. Don\'t include any personal data in your comment. You could send me and email with your debug id, and I\'ll keep you posted when the issue is resolved.</string>
|
||||
<string name="pref_acra_alwaysaccept">Automatically send crash reports</string>
|
||||
<string name="pref_acra_alwaysaccept_enabled">Will send crash reports automatically</string>
|
||||
<string name="pref_acra_alwaysaccept_disabled">Will ask everytime when sending crash reports.</string>
|
||||
<string name="pref_debug_crash_reports">Crash reports</string>
|
||||
<string name="pref_debug_debug_logs">Debug logging (these will be sent without a dialog)</string>
|
||||
<string name="acra_login">Enable logging</string>
|
||||
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
||||
<string name="unmark">Mark item as unread</string>
|
||||
</resources>
|
||||
|