Compare commits
168
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0aead6491 | ||
|
|
722b6cc06d | ||
|
|
6d7c4b40f6 | ||
|
|
f538ed39fc | ||
|
|
65821492ad | ||
|
|
6ede718a9f | ||
|
|
f1757937a4 | ||
|
|
2bd2e0a953 | ||
|
|
b5aef28af0 | ||
|
|
45747a1506 | ||
|
|
c6e2e08bcb | ||
|
|
25bf18661e | ||
|
|
6b088dcd24 | ||
|
|
d2b18e1880 | ||
|
|
eec7c94e98 | ||
|
|
d1f8fcacc0 | ||
|
|
07e4a33cbd | ||
|
|
f6317f566e | ||
|
|
9f51e4e6a5 | ||
|
|
750604a31f | ||
|
|
392eee0ad4 | ||
|
|
37e7b987ee | ||
|
|
9eac51e729 | ||
|
|
fa9cce6783 | ||
|
|
f0d4b63a97 | ||
|
|
83eeb11388 | ||
|
|
01f746f33d | ||
|
|
200851894b | ||
|
|
862e5cf4ab | ||
|
|
0b07f2a407 | ||
|
|
9ba6feef0b | ||
|
|
63a0638522 | ||
|
|
f9a4e6e363 | ||
|
|
6b40fd4bdc | ||
|
|
04c7776466 | ||
|
|
92c335b4e1 | ||
|
|
17251e576b | ||
|
|
62ea782429 | ||
|
|
f99474e3c1 | ||
|
|
57ac8f428f | ||
|
|
9cc1adbf15 | ||
|
|
1d9a440ae7 | ||
|
|
511553806c | ||
|
|
87e7d7c4fe | ||
|
|
ec87089310 | ||
|
|
d8478ebb01 | ||
|
|
600adc81b5 | ||
|
|
ddac2870af | ||
|
|
8d9c8c1394 | ||
|
|
b59c3bcb23 | ||
|
|
7f554adba5 | ||
|
|
21ce061282 | ||
|
|
bdb71e9b14 | ||
|
|
df22e7de15 | ||
|
|
6b3550396b | ||
|
|
c70f1e31a6 | ||
|
|
695670e944 | ||
|
|
1028826788 | ||
|
|
82a8977c96 | ||
|
|
07d9ce1054 | ||
|
|
7da7d49277 | ||
|
|
9b45365441 | ||
|
|
91a7464bce | ||
|
|
51add226eb | ||
|
|
332e9f5108 | ||
|
|
0b91087c07 | ||
|
|
ebbb1ba0f8 | ||
|
|
e9143ae852 | ||
|
|
42e8ecee78 | ||
|
|
4efd76fcbc | ||
|
|
fb1614070e | ||
|
|
c473dd7227 | ||
|
|
76bddb195d | ||
|
|
1e02ad2041 | ||
|
|
f6ab909f8b | ||
|
|
7e520e9bed | ||
|
|
32e2d05014 | ||
|
|
40d9c97f73 | ||
|
|
1aa68d3449 | ||
|
|
aeeac8cccd | ||
|
|
7292edf997 | ||
|
|
f49256c72f | ||
|
|
d02b28b81f | ||
|
|
08117043dd | ||
|
|
63496c993e | ||
|
|
00ef542e49 | ||
|
|
a78c6e6b33 | ||
|
|
363eaf9bf9 | ||
|
|
fec6683701 | ||
|
|
1549edb647 | ||
|
|
3de48ba162 | ||
|
|
a2a3d6f1a7 | ||
|
|
ccab2c7648 | ||
|
|
880dd1db5c | ||
|
|
ed18fea356 | ||
|
|
9816b20bf6 | ||
|
|
0bb2195bff | ||
|
|
ab2d0c4036 | ||
|
|
99fc417109 | ||
|
|
dc304ef8c1 | ||
|
|
c5511880bc | ||
|
|
5fe76d735e | ||
|
|
3064b3b835 | ||
|
|
70dc8af3ce | ||
|
|
53c8c241da | ||
|
|
bdc4f5680b | ||
|
|
ed290573b2 | ||
|
|
1616a97a8a | ||
|
|
d090183007 | ||
|
|
d47c508dee | ||
|
|
ed75f55437 | ||
|
|
5ad3ad4a57 | ||
|
|
aeac1bd1d4 | ||
|
|
4d18085072 | ||
|
|
0c9f8214ca | ||
|
|
a7ce7ce02e | ||
|
|
820986c7f0 | ||
|
|
8079cae745 | ||
|
|
6f067bd258 | ||
|
|
b6ade0f212 | ||
|
|
27dadc1be3 | ||
|
|
95e4162b4c | ||
|
|
f75557585e | ||
|
|
1b4c26919b | ||
|
|
ad085bf129 | ||
|
|
8fcd551105 | ||
|
|
a0954700e2 | ||
|
|
9705560442 | ||
|
|
1f47a13ce5 | ||
|
|
6f0ff2c975 | ||
|
|
76e5477986 | ||
|
|
7f308d5be3 | ||
|
|
54a43c83e8 | ||
|
|
8fe7266c84 | ||
|
|
d7a46b27b7 | ||
|
|
2257d09fdd | ||
|
|
047c5481c4 | ||
|
|
8a6719f934 | ||
|
|
51a692f3be | ||
|
|
b333f93171 | ||
|
|
50bcf18096 | ||
|
|
a089ced03f | ||
|
|
1f18dddf8b | ||
|
|
f5934e240e | ||
|
|
6b8da2eacf | ||
|
|
f4757a67b7 | ||
|
|
6edeb9d840 | ||
|
|
43ce0fd7bc | ||
|
|
5599f5a8fc | ||
|
|
6fd45ceb4f | ||
|
|
05ad8aac29 | ||
|
|
fa4f2476b7 | ||
|
|
00818a94e9 | ||
|
|
5d5250e44a | ||
|
|
3052b33132 | ||
|
|
50de6f8b5b | ||
|
|
f88a2f415f | ||
|
|
96f9813e01 | ||
|
|
fee739cb17 | ||
|
|
b1814c63b9 | ||
|
|
c1d45678f8 | ||
|
|
3d34e59a94 | ||
|
|
f1133bea8b | ||
|
|
ec64c88ff1 | ||
|
|
be66dbba6c | ||
|
|
8926cdbbf5 | ||
|
|
a956870dec | ||
|
|
8ed7951c9b |
@@ -41,6 +41,12 @@ Always check if the web version of your instance is working.
|
||||
* Remember that PR review can take time.
|
||||
|
||||
|
||||
# Install Selfoss (if you don't have an instance)
|
||||
|
||||
I won't provide any selfoss instance url. If you want to help, but to not have one, you'll have to install one, and use it.
|
||||
|
||||
All the details to need are [here](https://selfoss.aditu.de/).
|
||||
|
||||
# Build the project
|
||||
|
||||
You can directly import this project into IntellIJ/Android Studio.
|
||||
|
||||
@@ -1,7 +1,31 @@
|
||||
**1.7.x**
|
||||
|
||||
- Hiding tags with 0 articles
|
||||
|
||||
- Fixed issue with basic auth and images loading
|
||||
|
||||
- Added the ability to justify or left align the reader text
|
||||
|
||||
- Fixed #251
|
||||
|
||||
- Added experimental issue to set a default timeout. Should work for #238.
|
||||
|
||||
- Closing #220.
|
||||
|
||||
- Start of #238. "Add a quick shortcut to open the app on offline mode ?"
|
||||
|
||||
- Closes #216. Issue with selfoss version 2.19.
|
||||
|
||||
- Closes #179. Sync of read/unread/star/unstar items on background task or on app reload with network available.
|
||||
|
||||
- Closes #33. Background sync with settings.
|
||||
|
||||
- Closing #1. Initial article caching.
|
||||
|
||||
- Closing #228 by removing the list action bar. Action buttons are exclusively on the card view from now on.
|
||||
|
||||
- Closing #38. Only doing api calls on network available.
|
||||
|
||||
**1.6.x**
|
||||
|
||||
- Handling hidden tags.
|
||||
|
||||
@@ -18,7 +18,11 @@ Also, the last APK built from source is available [here](https://jenkins.amine-b
|
||||
|
||||
## Want to help ?
|
||||
|
||||
Check the [Contribution guide](https://github.com/aminecmi/ReaderforSelfoss/blob/master/.github/CONTRIBUTING.md)
|
||||
1. **You'll have to have a Selfoss instance running.** You'll find everything you need to install it [here](https://selfoss.aditu.de/).
|
||||
|
||||
2. Check the [Contribution guide](https://github.com/aminecmi/ReaderforSelfoss/blob/master/.github/CONTRIBUTING.md).
|
||||
|
||||
3. Build the project by following [these steps](https://github.com/aminecmi/ReaderforSelfoss/blob/master/.github/CONTRIBUTING.md#build-the-project) (you should have read them after the contribution guide)
|
||||
|
||||
## Useful links
|
||||
|
||||
|
||||
+17
-16
@@ -1,17 +1,17 @@
|
||||
buildscript {
|
||||
}
|
||||
|
||||
ext {
|
||||
configuration = [
|
||||
buildDate: new Date()
|
||||
]
|
||||
// This will make me able to build multiple times a day. May break thinks. I may forget it.
|
||||
todaysBuilds = "1"
|
||||
}
|
||||
|
||||
def gitVersion() {
|
||||
def process = "git describe --abbrev=0 --tags".execute()
|
||||
return process.text.substring(1).replaceAll("\\.", "").trim()
|
||||
def process
|
||||
def maybeTagOfCurrentCommit = 'git describe --contains HEAD'.execute()
|
||||
if (maybeTagOfCurrentCommit.text.isEmpty()) {
|
||||
println "No tag on current commit. Will take the latest one."
|
||||
process = "git for-each-ref refs/tags --sort=-authordate --format='%(refname:short)' --count=1".execute()
|
||||
} else {
|
||||
println "Tag found on current commit"
|
||||
process = 'git describe --contains HEAD'.execute()
|
||||
}
|
||||
return process.text.replaceAll("'", "").substring(1).replaceAll("\\.", "").trim()
|
||||
}
|
||||
|
||||
def versionCodeFromGit() {
|
||||
@@ -67,7 +67,7 @@ android {
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
shrinkResources true
|
||||
shrinkResources false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'),
|
||||
'proguard-rules.pro'
|
||||
}
|
||||
@@ -75,7 +75,6 @@ android {
|
||||
buildConfigField "String", "LOGIN_URL", appLoginUrl
|
||||
buildConfigField "String", "LOGIN_USERNAME", appLoginUsername
|
||||
buildConfigField "String", "LOGIN_PASSWORD", appLoginPassword
|
||||
applicationIdSuffix ".dev"
|
||||
}
|
||||
}
|
||||
flavorDimensions "build"
|
||||
@@ -113,7 +112,7 @@ dependencies {
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2'
|
||||
|
||||
//multidex
|
||||
implementation 'androidx.multidex:multidex:2.0.0'
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
|
||||
// About
|
||||
implementation('com.mikepenz:aboutlibraries:6.2.0@aar') {
|
||||
@@ -152,14 +151,16 @@ dependencies {
|
||||
implementation 'androidx.core:core-ktx:1.0.0'
|
||||
|
||||
// Crash
|
||||
implementation 'ch.acra:acra-http:5.1.3'
|
||||
implementation 'ch.acra:acra-dialog:5.1.3'
|
||||
implementation 'ch.acra:acra-http:5.2.1'
|
||||
implementation 'ch.acra:acra-dialog:5.2.1'
|
||||
|
||||
implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
|
||||
|
||||
implementation "androidx.room:room-runtime:$room_version"
|
||||
kapt "androidx.room:room-compiler:$room_version"
|
||||
|
||||
implementation "android.arch.work:work-runtime-ktx:$work_version"
|
||||
}
|
||||
|
||||
|
||||
@@ -177,4 +178,4 @@ def initAppLoginPropertiesIfNeeded() {
|
||||
entry(key: "appLoginPassword", value: System.getProperty("appLoginPassword"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+4
-17
@@ -30,22 +30,6 @@
|
||||
<fields>;
|
||||
}
|
||||
|
||||
|
||||
##Retrofit
|
||||
#-keep class com.google.gson.** { *; }
|
||||
#-keep class com.google.inject.** { *; }
|
||||
#-keep class org.apache.http.** { *; }
|
||||
#-keep class org.apache.james.mime4j.** { *; }
|
||||
#-keep class javax.inject.** { *; }
|
||||
#-keep class retrofit.** { *; }
|
||||
#-keepclassmembernames interface * {
|
||||
# @retrofit.http.* <methods>;
|
||||
#}
|
||||
#-keep class retrofit.** { *; }
|
||||
#-keep class apps.amine.bou.readerforselfoss.api.selfoss.model.** { *; }
|
||||
#-keepclassmembernames interface * {
|
||||
# @retrofit.http.* <methods>;
|
||||
#}
|
||||
-dontwarn okio.**
|
||||
-dontwarn retrofit2.Platform$Java8
|
||||
-keep class retrofit.** { *; }
|
||||
@@ -75,4 +59,7 @@
|
||||
|
||||
-dontwarn javax.annotation.**
|
||||
|
||||
-keep class android.support.v7.widget.SearchView { *; }
|
||||
-keep class android.support.v7.widget.SearchView { *; }
|
||||
|
||||
# maybe remove later ?
|
||||
-keep class * extends androidx.fragment.app.Fragment
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
{
|
||||
"formatVersion": 1,
|
||||
"database": {
|
||||
"version": 3,
|
||||
"identityHash": "7ad9c4961992c13b670128485ebb3efc",
|
||||
"entities": [
|
||||
{
|
||||
"tableName": "tags",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tag` TEXT NOT NULL, `color` TEXT NOT NULL, `unread` INTEGER NOT NULL, PRIMARY KEY(`tag`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "tag",
|
||||
"columnName": "tag",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "color",
|
||||
"columnName": "color",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unread",
|
||||
"columnName": "unread",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"tag"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "sources",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `title` TEXT NOT NULL, `tags` TEXT NOT NULL, `spout` TEXT NOT NULL, `error` TEXT NOT NULL, `icon` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tags",
|
||||
"columnName": "tags",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "spout",
|
||||
"columnName": "spout",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "error",
|
||||
"columnName": "error",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "icon",
|
||||
"columnName": "icon",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "items",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `datetime` TEXT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `unread` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `thumbnail` TEXT NOT NULL, `icon` TEXT NOT NULL, `link` TEXT NOT NULL, `sourcetitle` TEXT NOT NULL, `tags` TEXT NOT NULL, PRIMARY KEY(`id`))",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "datetime",
|
||||
"columnName": "datetime",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "title",
|
||||
"columnName": "title",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "content",
|
||||
"columnName": "content",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unread",
|
||||
"columnName": "unread",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "thumbnail",
|
||||
"columnName": "thumbnail",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "icon",
|
||||
"columnName": "icon",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "link",
|
||||
"columnName": "link",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "sourcetitle",
|
||||
"columnName": "sourcetitle",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "tags",
|
||||
"columnName": "tags",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": false
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
},
|
||||
{
|
||||
"tableName": "actions",
|
||||
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `articleid` TEXT NOT NULL, `read` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `unstarred` INTEGER NOT NULL)",
|
||||
"fields": [
|
||||
{
|
||||
"fieldPath": "id",
|
||||
"columnName": "id",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "articleId",
|
||||
"columnName": "articleid",
|
||||
"affinity": "TEXT",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "read",
|
||||
"columnName": "read",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unread",
|
||||
"columnName": "unread",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "starred",
|
||||
"columnName": "starred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
},
|
||||
{
|
||||
"fieldPath": "unstarred",
|
||||
"columnName": "unstarred",
|
||||
"affinity": "INTEGER",
|
||||
"notNull": true
|
||||
}
|
||||
],
|
||||
"primaryKey": {
|
||||
"columnNames": [
|
||||
"id"
|
||||
],
|
||||
"autoGenerate": true
|
||||
},
|
||||
"indices": [],
|
||||
"foreignKeys": []
|
||||
}
|
||||
],
|
||||
"views": [],
|
||||
"setupQueries": [
|
||||
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
|
||||
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"7ad9c4961992c13b670128485ebb3efc\")"
|
||||
]
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="apps.amine.bou.readerforselfoss"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
package="apps.amine.bou.readerforselfoss">
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
|
||||
<application
|
||||
@@ -11,15 +11,19 @@
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:theme="@style/NoBar">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:theme="@style/SplashTheme">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<meta-data
|
||||
android:name="android.app.shortcuts"
|
||||
android:resource="@xml/shortcuts" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".LoginActivity"
|
||||
@@ -33,7 +37,7 @@
|
||||
android:parentActivityName=".HomeActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value="apps.amine.bou.readerforselfoss.HomeActivity" />
|
||||
android:value=".HomeActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".SourcesActivity"
|
||||
@@ -51,9 +55,7 @@
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:mimeType="text/plain" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
@@ -72,6 +74,9 @@
|
||||
android:value="true" />
|
||||
|
||||
<meta-data android:name="android.max_aspect" android:value="2.1" />
|
||||
<meta-data
|
||||
android:name="preloaded_fonts"
|
||||
android:resource="@array/preloaded_fonts" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
|
||||
@@ -89,6 +89,7 @@ class AddSourceActivity : AppCompatActivity() {
|
||||
this,
|
||||
this@AddSourceActivity,
|
||||
prefs.getBoolean("isSelfSignedCert", false),
|
||||
prefs.getString("api_timeout", "-1").toLong(),
|
||||
prefs.getBoolean("should_log_everything", false)
|
||||
)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
@@ -108,7 +109,7 @@ class AddSourceActivity : AppCompatActivity() {
|
||||
super.onResume()
|
||||
val config = Config(this)
|
||||
|
||||
if (config.baseUrl.isEmpty() || !config.baseUrl.isBaseUrlValid()) {
|
||||
if (config.baseUrl.isEmpty() || !config.baseUrl.isBaseUrlValid(false, this@AddSourceActivity)) {
|
||||
mustLoginToAddSource()
|
||||
} else {
|
||||
handleSpoutsSpinner(spoutsSpinner, api, progress, formContainer)
|
||||
|
||||
@@ -18,13 +18,15 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.appcompat.widget.SearchView
|
||||
import androidx.recyclerview.widget.StaggeredGridLayoutManager
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import android.util.Log
|
||||
import android.view.Menu
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
import androidx.work.Constraints
|
||||
import androidx.work.ExistingPeriodicWorkPolicy
|
||||
import androidx.work.PeriodicWorkRequestBuilder
|
||||
import androidx.work.WorkManager
|
||||
import apps.amine.bou.readerforselfoss.adapters.ItemCardAdapter
|
||||
import apps.amine.bou.readerforselfoss.adapters.ItemListAdapter
|
||||
import apps.amine.bou.readerforselfoss.adapters.ItemsAdapter
|
||||
@@ -34,8 +36,11 @@ import apps.amine.bou.readerforselfoss.api.selfoss.Source
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Stats
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Tag
|
||||
import apps.amine.bou.readerforselfoss.background.LoadingWorker
|
||||
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity
|
||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_1_2
|
||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_2_3
|
||||
import apps.amine.bou.readerforselfoss.settings.SettingsActivity
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.themes.Toppings
|
||||
@@ -47,6 +52,7 @@ import apps.amine.bou.readerforselfoss.utils.drawer.CustomUrlPrimaryDrawerItem
|
||||
import apps.amine.bou.readerforselfoss.utils.flattenTags
|
||||
import apps.amine.bou.readerforselfoss.utils.longHash
|
||||
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
|
||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
||||
import apps.amine.bou.readerforselfoss.utils.persistence.toEntity
|
||||
import apps.amine.bou.readerforselfoss.utils.persistence.toView
|
||||
import co.zsmb.materialdrawerkt.builders.accountHeader
|
||||
@@ -59,7 +65,6 @@ import com.ashokvarma.bottomnavigation.BottomNavigationItem
|
||||
import com.ashokvarma.bottomnavigation.TextBadgeItem
|
||||
import com.ftinc.scoop.Scoop
|
||||
import com.github.stkent.amplify.tracking.Amplify
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.mikepenz.aboutlibraries.Libs
|
||||
import com.mikepenz.aboutlibraries.LibsBuilder
|
||||
import com.mikepenz.materialdrawer.Drawer
|
||||
@@ -69,11 +74,11 @@ import com.mikepenz.materialdrawer.model.DividerDrawerItem
|
||||
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
|
||||
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem
|
||||
import kotlinx.android.synthetic.main.activity_home.*
|
||||
import kotlinx.android.synthetic.main.fragment_article.*
|
||||
import org.acra.ACRA
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
@@ -110,6 +115,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
private var itemsCaching: Boolean = false
|
||||
private var hiddenTags: List<String> = emptyList()
|
||||
|
||||
private var periodicRefresh = false
|
||||
private var refreshMinutes: Long = 360L
|
||||
private var refreshWhenChargingOnly = false
|
||||
|
||||
private lateinit var tabNewBadge: TextBadgeItem
|
||||
private lateinit var tabArchiveBadge: TextBadgeItem
|
||||
private lateinit var tabStarredBadge: TextBadgeItem
|
||||
@@ -130,10 +139,15 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
private var badgeAll: Int = -1
|
||||
private var badgeFavs: Int = -1
|
||||
|
||||
private var fromTabShortcut: Boolean = false
|
||||
private var offlineShortcut: Boolean = false
|
||||
|
||||
private lateinit var tagsBadge: Map<Long, Int>
|
||||
|
||||
private lateinit var db: AppDatabase
|
||||
|
||||
private lateinit var config: Config
|
||||
|
||||
data class DrawerData(val tags: List<Tag>?, val sources: List<Source>?)
|
||||
|
||||
override fun onStart() {
|
||||
@@ -143,9 +157,17 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
appColors = AppColors(this@HomeActivity)
|
||||
config = Config(this@HomeActivity)
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
fromTabShortcut = intent.getIntExtra("shortcutTab", -1) != -1
|
||||
offlineShortcut = intent.getBooleanExtra("startOffline", false)
|
||||
|
||||
if (fromTabShortcut) {
|
||||
elementsShown = intent.getIntExtra("shortcutTab", UNREAD_SHOWN)
|
||||
}
|
||||
|
||||
setContentView(R.layout.activity_home)
|
||||
|
||||
handleThemeBinding()
|
||||
@@ -158,7 +180,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
db = Room.databaseBuilder(
|
||||
applicationContext,
|
||||
AppDatabase::class.java, "selfoss-database"
|
||||
).addMigrations(MIGRATION_1_2).build()
|
||||
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).build()
|
||||
|
||||
|
||||
customTabActivityHelper = CustomTabActivityHelper()
|
||||
@@ -170,6 +192,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
this,
|
||||
this@HomeActivity,
|
||||
settings.getBoolean("isSelfSignedCert", false),
|
||||
sharedPref.getString("api_timeout", "-1").toLong(),
|
||||
shouldLogEverything
|
||||
)
|
||||
items = ArrayList()
|
||||
@@ -188,6 +211,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
R.color.refresh_progress_3
|
||||
)
|
||||
swipeRefreshLayout.setOnRefreshListener {
|
||||
offlineShortcut = false
|
||||
allItems = ArrayList()
|
||||
lastFetchDone = false
|
||||
handleDrawerItems()
|
||||
@@ -203,7 +227,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
recyclerView: RecyclerView,
|
||||
viewHolder: RecyclerView.ViewHolder
|
||||
): Int =
|
||||
if (elementsShown != UNREAD_SHOWN) {
|
||||
if (elementsShown != UNREAD_SHOWN && elementsShown != READ_SHOWN) {
|
||||
0
|
||||
} else {
|
||||
super.getSwipeDirs(
|
||||
@@ -223,17 +247,21 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
val i = items.elementAtOrNull(position)
|
||||
|
||||
if (i != null) {
|
||||
val adapter = recyclerView.adapter
|
||||
val adapter = recyclerView.adapter as ItemsAdapter<*>
|
||||
|
||||
when (adapter) {
|
||||
is ItemCardAdapter -> adapter.removeItemAtIndex(position)
|
||||
is ItemListAdapter -> adapter.removeItemAtIndex(position)
|
||||
val wasItemUnread = adapter.unreadItemStatusAtIndex(position)
|
||||
|
||||
adapter.handleItemAtIndex(position)
|
||||
|
||||
if (wasItemUnread) {
|
||||
badgeNew--
|
||||
} else {
|
||||
badgeNew++
|
||||
}
|
||||
|
||||
badgeNew--
|
||||
reloadBadgeContent()
|
||||
|
||||
val tagHashes = i.tags.split(",").map { it.longHash() }
|
||||
val tagHashes = i.tags.tags.split(",").map { it.longHash() }
|
||||
tagsBadge = tagsBadge.map {
|
||||
if (tagHashes.contains(it.key)) {
|
||||
(it.key to (it.value - 1))
|
||||
@@ -277,19 +305,19 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
|
||||
val tabNew =
|
||||
BottomNavigationItem(
|
||||
R.drawable.ic_fiber_new_black_24dp,
|
||||
R.drawable.ic_tab_fiber_new_black_24dp,
|
||||
getString(R.string.tab_new)
|
||||
).setActiveColor(appColors.colorAccent)
|
||||
.setBadgeItem(tabNewBadge)
|
||||
val tabArchive =
|
||||
BottomNavigationItem(
|
||||
R.drawable.ic_archive_black_24dp,
|
||||
R.drawable.ic_tab_archive_black_24dp,
|
||||
getString(R.string.tab_read)
|
||||
).setActiveColor(appColors.colorAccentDark)
|
||||
.setBadgeItem(tabArchiveBadge)
|
||||
val tabStarred =
|
||||
BottomNavigationItem(
|
||||
R.drawable.ic_favorite_black_24dp,
|
||||
R.drawable.ic_tab_favorite_black_24dp,
|
||||
getString(R.string.tab_favs)
|
||||
).setActiveColorResource(R.color.pink)
|
||||
.setBadgeItem(tabStarredBadge)
|
||||
@@ -303,6 +331,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
|
||||
bottomBar.setMode(BottomNavigationBar.MODE_SHIFTING)
|
||||
bottomBar.setBackgroundStyle(BottomNavigationBar.BACKGROUND_STYLE_STATIC)
|
||||
|
||||
if (fromTabShortcut) {
|
||||
bottomBar.selectTab(elementsShown - 1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
@@ -333,8 +365,11 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
|
||||
getElementsAccordingToTab()
|
||||
|
||||
|
||||
handleGDPRDialog(sharedPref.getBoolean("GDPR_shown", false))
|
||||
|
||||
handleRecurringTask()
|
||||
|
||||
handleOfflineActions()
|
||||
}
|
||||
|
||||
private fun getAndStoreAllItems() {
|
||||
@@ -349,7 +384,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
thread {
|
||||
if (response.body() != null) {
|
||||
val apiItems = (response.body() as ArrayList<Item>).filter {
|
||||
maybeTagFilter != null || filter(it.tags)
|
||||
maybeTagFilter != null || filter(it.tags.tags)
|
||||
} as ArrayList<Item>
|
||||
db.itemsDao().deleteAllItems()
|
||||
db.itemsDao()
|
||||
@@ -384,6 +419,13 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
periodicRefresh = sharedPref.getBoolean("periodic_refresh", false)
|
||||
refreshWhenChargingOnly = sharedPref.getBoolean("refresh_when_charging", false)
|
||||
refreshMinutes = sharedPref.getString("periodic_refresh_minutes", "360").toLong()
|
||||
|
||||
if (refreshMinutes <= 15) {
|
||||
refreshMinutes = 15
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleThemeBinding() {
|
||||
@@ -437,7 +479,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
|
||||
footer {
|
||||
primaryItem(R.string.drawer_report_bug) {
|
||||
icon = R.drawable.ic_bug_report
|
||||
icon = R.drawable.ic_bug_report_black_24dp
|
||||
iconTintingEnabled = true
|
||||
onClick { _ ->
|
||||
val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(Config.trackerUrl))
|
||||
@@ -447,7 +489,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
}
|
||||
|
||||
primaryItem(R.string.title_activity_settings) {
|
||||
icon = R.drawable.ic_settings
|
||||
icon = R.drawable.ic_settings_black_24dp
|
||||
iconTintingEnabled = true
|
||||
onClick { _ ->
|
||||
startActivityForResult(
|
||||
@@ -490,12 +532,11 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
gd.shape = GradientDrawable.RECTANGLE
|
||||
gd.setSize(30, 30)
|
||||
gd.cornerRadius = 30F
|
||||
drawer.addItem(
|
||||
var drawerItem =
|
||||
PrimaryDrawerItem()
|
||||
.withName(it.tag)
|
||||
.withIdentifier(it.tag.longHash())
|
||||
.withIcon(gd)
|
||||
.withBadge("${it.unread}")
|
||||
.withBadgeStyle(
|
||||
BadgeStyle().withTextColor(Color.WHITE)
|
||||
.withColor(appColors.colorAccent)
|
||||
@@ -506,6 +547,11 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
getElementsAccordingToTab()
|
||||
false
|
||||
}
|
||||
if (it.unread > 0) {
|
||||
drawerItem = drawerItem.withBadge("${it.unread}")
|
||||
}
|
||||
drawer.addItem(
|
||||
drawerItem
|
||||
)
|
||||
|
||||
(it.tag.longHash() to it.unread)
|
||||
@@ -537,12 +583,11 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
gd.shape = GradientDrawable.RECTANGLE
|
||||
gd.setSize(30, 30)
|
||||
gd.cornerRadius = 30F
|
||||
drawer.addItem(
|
||||
var drawerItem =
|
||||
PrimaryDrawerItem()
|
||||
.withName(it.tag)
|
||||
.withIdentifier(it.tag.longHash())
|
||||
.withIcon(gd)
|
||||
.withBadge("${it.unread}")
|
||||
.withBadgeStyle(
|
||||
BadgeStyle().withTextColor(Color.WHITE)
|
||||
.withColor(appColors.colorAccent)
|
||||
@@ -553,6 +598,11 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
getElementsAccordingToTab()
|
||||
false
|
||||
}
|
||||
if (it.unread > 0) {
|
||||
drawerItem = drawerItem.withBadge("${it.unread}")
|
||||
}
|
||||
drawer.addItem(
|
||||
drawerItem
|
||||
)
|
||||
|
||||
(it.tag.longHash() to it.unread)
|
||||
@@ -603,14 +653,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
false
|
||||
}
|
||||
)
|
||||
drawer.addItem(DividerDrawerItem())
|
||||
drawer.addItem(
|
||||
SecondaryDrawerItem()
|
||||
.withName(getString(R.string.drawer_item_tags))
|
||||
.withIdentifier(DRAWER_ID_TAGS)
|
||||
.withSelectable(false)
|
||||
)
|
||||
handleTags(maybeDrawerData.tags)
|
||||
if (hiddenTags.isNotEmpty()) {
|
||||
drawer.addItem(DividerDrawerItem())
|
||||
drawer.addItem(
|
||||
@@ -622,6 +664,14 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
handleHiddenTags(maybeDrawerData.tags)
|
||||
}
|
||||
drawer.addItem(DividerDrawerItem())
|
||||
drawer.addItem(
|
||||
SecondaryDrawerItem()
|
||||
.withName(getString(R.string.drawer_item_tags))
|
||||
.withIdentifier(DRAWER_ID_TAGS)
|
||||
.withSelectable(false)
|
||||
)
|
||||
handleTags(maybeDrawerData.tags)
|
||||
drawer.addItem(DividerDrawerItem())
|
||||
drawer.addItem(
|
||||
SecondaryDrawerItem()
|
||||
.withName(getString(R.string.drawer_item_sources))
|
||||
@@ -639,7 +689,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
PrimaryDrawerItem()
|
||||
.withName(R.string.action_about)
|
||||
.withSelectable(false)
|
||||
.withIcon(R.drawable.ic_info_outline)
|
||||
.withIcon(R.drawable.ic_info_outline_white_24dp)
|
||||
.withIconTintingEnabled(true)
|
||||
.withOnDrawerItemClickListener { _, _, _ ->
|
||||
LibsBuilder()
|
||||
@@ -696,36 +746,44 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
var sources: List<Source>?
|
||||
|
||||
fun sourcesApiCall() {
|
||||
api.sources.enqueue(object : Callback<List<Source>> {
|
||||
override fun onResponse(
|
||||
call: Call<List<Source>>?,
|
||||
response: Response<List<Source>>
|
||||
) {
|
||||
sources = response.body()
|
||||
val apiDrawerData = DrawerData(tags, sources)
|
||||
if ((maybeDrawerData != null && maybeDrawerData != apiDrawerData) || maybeDrawerData == null) {
|
||||
handleDrawerData(apiDrawerData)
|
||||
if (this@HomeActivity.isNetworkAccessible(null, offlineShortcut)) {
|
||||
api.sources.enqueue(object : Callback<List<Source>> {
|
||||
override fun onResponse(
|
||||
call: Call<List<Source>>?,
|
||||
response: Response<List<Source>>
|
||||
) {
|
||||
sources = response.body()
|
||||
val apiDrawerData = DrawerData(tags, sources)
|
||||
if ((maybeDrawerData != null && maybeDrawerData != apiDrawerData) || maybeDrawerData == null) {
|
||||
handleDrawerData(apiDrawerData)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<List<Source>>?, t: Throwable?) {
|
||||
val apiDrawerData = DrawerData(tags, null)
|
||||
if ((maybeDrawerData != null && maybeDrawerData != apiDrawerData) || maybeDrawerData == null) {
|
||||
handleDrawerData(apiDrawerData)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (this@HomeActivity.isNetworkAccessible(null, offlineShortcut)) {
|
||||
api.tags.enqueue(object : Callback<List<Tag>> {
|
||||
override fun onResponse(
|
||||
call: Call<List<Tag>>,
|
||||
response: Response<List<Tag>>
|
||||
) {
|
||||
tags = response.body()
|
||||
sourcesApiCall()
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<List<Source>>?, t: Throwable?) {
|
||||
override fun onFailure(call: Call<List<Tag>>?, t: Throwable?) {
|
||||
sourcesApiCall()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
api.tags.enqueue(object : Callback<List<Tag>> {
|
||||
override fun onResponse(
|
||||
call: Call<List<Tag>>,
|
||||
response: Response<List<Tag>>
|
||||
) {
|
||||
tags = response.body()
|
||||
sourcesApiCall()
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<List<Tag>>?, t: Throwable?) {
|
||||
sourcesApiCall()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
drawer.addItem(
|
||||
@@ -817,11 +875,51 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
override fun onTabSelected(position: Int) {
|
||||
offset = 0
|
||||
lastFetchDone = false
|
||||
when (position) {
|
||||
0 -> getUnRead()
|
||||
1 -> getRead()
|
||||
2 -> getStarred()
|
||||
else -> Unit
|
||||
|
||||
if (itemsCaching) {
|
||||
|
||||
if (!swipeRefreshLayout.isRefreshing) {
|
||||
swipeRefreshLayout.post { swipeRefreshLayout.isRefreshing = true }
|
||||
}
|
||||
|
||||
thread {
|
||||
val dbItems = db.itemsDao().items().map { it.toView() }
|
||||
runOnUiThread {
|
||||
if (dbItems.isNotEmpty()) {
|
||||
items = when (position) {
|
||||
0 -> ArrayList(dbItems.filter { it.unread })
|
||||
1 -> ArrayList(dbItems.filter { !it.unread })
|
||||
2 -> ArrayList(dbItems.filter { it.starred })
|
||||
else -> ArrayList(dbItems.filter { it.unread })
|
||||
}
|
||||
handleListResult()
|
||||
when (position) {
|
||||
0 -> getUnRead()
|
||||
1 -> getRead()
|
||||
2 -> getStarred()
|
||||
else -> Unit
|
||||
}
|
||||
} else {
|
||||
if (this@HomeActivity.isNetworkAccessible(this@HomeActivity.findViewById(R.id.coordLayout), offlineShortcut)) {
|
||||
when (position) {
|
||||