Merge branch 'master' into crowdin_translation
This commit is contained in:
commit
ed290573b2
2
.gitignore
vendored
2
.gitignore
vendored
@ -217,5 +217,3 @@ gradle-app.setting
|
|||||||
release/
|
release/
|
||||||
|
|
||||||
crowdin.properties
|
crowdin.properties
|
||||||
|
|
||||||
publish-version.sh
|
|
@ -1,3 +1,7 @@
|
|||||||
|
**1.7.x**
|
||||||
|
|
||||||
|
- Closing #1. Initial article caching.
|
||||||
|
|
||||||
**1.6.x**
|
**1.6.x**
|
||||||
|
|
||||||
- Handling hidden tags.
|
- Handling hidden tags.
|
||||||
|
@ -56,6 +56,13 @@ android {
|
|||||||
|
|
||||||
// tests
|
// tests
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
|
javaCompileOptions {
|
||||||
|
annotationProcessorOptions {
|
||||||
|
arguments = ["room.schemaLocation":
|
||||||
|
"$projectDir/schemas".toString()]
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
buildTypes {
|
buildTypes {
|
||||||
release {
|
release {
|
||||||
|
@ -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\")"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
@ -35,6 +35,7 @@ import apps.amine.bou.readerforselfoss.api.selfoss.Stats
|
|||||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||||
import apps.amine.bou.readerforselfoss.api.selfoss.Tag
|
import apps.amine.bou.readerforselfoss.api.selfoss.Tag
|
||||||
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
||||||
|
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_1_2
|
||||||
import apps.amine.bou.readerforselfoss.settings.SettingsActivity
|
import apps.amine.bou.readerforselfoss.settings.SettingsActivity
|
||||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||||
import apps.amine.bou.readerforselfoss.themes.Toppings
|
import apps.amine.bou.readerforselfoss.themes.Toppings
|
||||||
@ -45,6 +46,7 @@ import apps.amine.bou.readerforselfoss.utils.customtabs.CustomTabActivityHelper
|
|||||||
import apps.amine.bou.readerforselfoss.utils.drawer.CustomUrlPrimaryDrawerItem
|
import apps.amine.bou.readerforselfoss.utils.drawer.CustomUrlPrimaryDrawerItem
|
||||||
import apps.amine.bou.readerforselfoss.utils.flattenTags
|
import apps.amine.bou.readerforselfoss.utils.flattenTags
|
||||||
import apps.amine.bou.readerforselfoss.utils.longHash
|
import apps.amine.bou.readerforselfoss.utils.longHash
|
||||||
|
import apps.amine.bou.readerforselfoss.utils.maybeHandleSilentException
|
||||||
import apps.amine.bou.readerforselfoss.utils.persistence.toEntity
|
import apps.amine.bou.readerforselfoss.utils.persistence.toEntity
|
||||||
import apps.amine.bou.readerforselfoss.utils.persistence.toView
|
import apps.amine.bou.readerforselfoss.utils.persistence.toView
|
||||||
import co.zsmb.materialdrawerkt.builders.accountHeader
|
import co.zsmb.materialdrawerkt.builders.accountHeader
|
||||||
@ -68,6 +70,7 @@ import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
|
|||||||
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem
|
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem
|
||||||
import kotlinx.android.synthetic.main.activity_home.*
|
import kotlinx.android.synthetic.main.activity_home.*
|
||||||
import kotlinx.android.synthetic.main.fragment_article.*
|
import kotlinx.android.synthetic.main.fragment_article.*
|
||||||
|
import org.acra.ACRA
|
||||||
import retrofit2.Call
|
import retrofit2.Call
|
||||||
import retrofit2.Callback
|
import retrofit2.Callback
|
||||||
import retrofit2.Response
|
import retrofit2.Response
|
||||||
@ -104,6 +107,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
private var displayAccountHeader: Boolean = false
|
private var displayAccountHeader: Boolean = false
|
||||||
private var infiniteScroll: Boolean = false
|
private var infiniteScroll: Boolean = false
|
||||||
private var lastFetchDone: Boolean = false
|
private var lastFetchDone: Boolean = false
|
||||||
|
private var itemsCaching: Boolean = false
|
||||||
private var hiddenTags: List<String> = emptyList()
|
private var hiddenTags: List<String> = emptyList()
|
||||||
|
|
||||||
private lateinit var tabNewBadge: TextBadgeItem
|
private lateinit var tabNewBadge: TextBadgeItem
|
||||||
@ -153,8 +157,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
|
|
||||||
db = Room.databaseBuilder(
|
db = Room.databaseBuilder(
|
||||||
applicationContext,
|
applicationContext,
|
||||||
AppDatabase::class.java!!, "selfoss-database"
|
AppDatabase::class.java, "selfoss-database"
|
||||||
).build()
|
).addMigrations(MIGRATION_1_2).build()
|
||||||
|
|
||||||
|
|
||||||
customTabActivityHelper = CustomTabActivityHelper()
|
customTabActivityHelper = CustomTabActivityHelper()
|
||||||
@ -177,24 +181,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
handleSwipeRefreshLayout()
|
handleSwipeRefreshLayout()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleGDPRDialog(GDPRShown: Boolean) {
|
|
||||||
val sharedEditor = sharedPref.edit()
|
|
||||||
if (!GDPRShown) {
|
|
||||||
val alertDialog = AlertDialog.Builder(this).create()
|
|
||||||
alertDialog.setTitle(getString(R.string.gdpr_dialog_title))
|
|
||||||
alertDialog.setMessage(getString(R.string.gdpr_dialog_message))
|
|
||||||
alertDialog.setButton(
|
|
||||||
AlertDialog.BUTTON_NEUTRAL,
|
|
||||||
"OK"
|
|
||||||
) { dialog, _ ->
|
|
||||||
sharedEditor.putBoolean("GDPR_shown", true)
|
|
||||||
sharedEditor.commit()
|
|
||||||
dialog.dismiss()
|
|
||||||
}
|
|
||||||
alertDialog.show()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun handleSwipeRefreshLayout() {
|
private fun handleSwipeRefreshLayout() {
|
||||||
swipeRefreshLayout.setColorSchemeResources(
|
swipeRefreshLayout.setColorSchemeResources(
|
||||||
R.color.refresh_progress_1,
|
R.color.refresh_progress_1,
|
||||||
@ -347,9 +333,33 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
|
|
||||||
getElementsAccordingToTab()
|
getElementsAccordingToTab()
|
||||||
|
|
||||||
|
|
||||||
handleGDPRDialog(sharedPref.getBoolean("GDPR_shown", false))
|
handleGDPRDialog(sharedPref.getBoolean("GDPR_shown", false))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getAndStoreAllItems() {
|
||||||
|
api.allItems().enqueue(object : Callback<List<Item>> {
|
||||||
|
override fun onFailure(call: Call<List<Item>>, t: Throwable) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResponse(
|
||||||
|
call: Call<List<Item>>,
|
||||||
|
response: Response<List<Item>>
|
||||||
|
) {
|
||||||
|
thread {
|
||||||
|
if (response.body() != null) {
|
||||||
|
val apiItems = (response.body() as ArrayList<Item>).filter {
|
||||||
|
maybeTagFilter != null || filter(it.tags)
|
||||||
|
} as ArrayList<Item>
|
||||||
|
db.itemsDao().deleteAllItems()
|
||||||
|
db.itemsDao()
|
||||||
|
.insertAllItems(*(apiItems.map { it.toEntity() }).toTypedArray())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
override fun onStop() {
|
override fun onStop() {
|
||||||
super.onStop()
|
super.onStop()
|
||||||
customTabActivityHelper.unbindCustomTabsService(this)
|
customTabActivityHelper.unbindCustomTabsService(this)
|
||||||
@ -368,6 +378,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
userIdentifier = sharedPref.getString("unique_id", "")
|
userIdentifier = sharedPref.getString("unique_id", "")
|
||||||
displayAccountHeader = sharedPref.getBoolean("account_header_displaying", false)
|
displayAccountHeader = sharedPref.getBoolean("account_header_displaying", false)
|
||||||
infiniteScroll = sharedPref.getBoolean("infinite_loading", false)
|
infiniteScroll = sharedPref.getBoolean("infinite_loading", false)
|
||||||
|
itemsCaching = sharedPref.getBoolean("items_caching", false)
|
||||||
hiddenTags = if (sharedPref.getString("hidden_tags", "").isNotEmpty()) {
|
hiddenTags = if (sharedPref.getString("hidden_tags", "").isNotEmpty()) {
|
||||||
sharedPref.getString("hidden_tags", "").replace("\\s".toRegex(), "").split(",")
|
sharedPref.getString("hidden_tags", "").replace("\\s".toRegex(), "").split(",")
|
||||||
} else {
|
} else {
|
||||||
@ -726,8 +737,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
thread {
|
thread {
|
||||||
var drawerData = DrawerData(db.drawerDataDao().tags().map { it.toView() },
|
var drawerData = DrawerData(db.drawerDataDao().tags().map { it.toView() },
|
||||||
db.drawerDataDao().sources().map { it.toView() })
|
db.drawerDataDao().sources().map { it.toView() })
|
||||||
handleDrawerData(drawerData, loadedFromCache = true)
|
runOnUiThread {
|
||||||
drawerApiCalls(drawerData)
|
handleDrawerData(drawerData, loadedFromCache = true)
|
||||||
|
drawerApiCalls(drawerData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -817,7 +830,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
private fun handleInfiniteScroll() {
|
private fun handleInfiniteScroll() {
|
||||||
recyclerViewScrollListener = object : RecyclerView.OnScrollListener() {
|
recyclerViewScrollListener = object : RecyclerView.OnScrollListener() {
|
||||||
override fun onScrolled(localRecycler: RecyclerView, dx: Int, dy: Int) {
|
override fun onScrolled(localRecycler: RecyclerView, dx: Int, dy: Int) {
|
||||||
if (localRecycler != null && dy > 0) {
|
if (dy > 0) {
|
||||||
val manager = recyclerView.layoutManager
|
val manager = recyclerView.layoutManager
|
||||||
val lastVisibleItem: Int = when (manager) {
|
val lastVisibleItem: Int = when (manager) {
|
||||||
is StaggeredGridLayoutManager -> manager.findLastCompletelyVisibleItemPositions(
|
is StaggeredGridLayoutManager -> manager.findLastCompletelyVisibleItemPositions(
|
||||||
@ -851,6 +864,15 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
appendResults: Boolean = false,
|
appendResults: Boolean = false,
|
||||||
offsetOverride: Int? = null
|
offsetOverride: Int? = null
|
||||||
) {
|
) {
|
||||||
|
fun doGetAccordingToTab() {
|
||||||
|
when (elementsShown) {
|
||||||
|
UNREAD_SHOWN -> getUnRead(appendResults)
|
||||||
|
READ_SHOWN -> getRead(appendResults)
|
||||||
|
FAV_SHOWN -> getStarred(appendResults)
|
||||||
|
else -> getUnRead(appendResults)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
offset = if (appendResults && offsetOverride === null) {
|
offset = if (appendResults && offsetOverride === null) {
|
||||||
(offset + itemsNumber)
|
(offset + itemsNumber)
|
||||||
} else {
|
} else {
|
||||||
@ -858,12 +880,35 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
}
|
}
|
||||||
firstVisible = if (appendResults) firstVisible else 0
|
firstVisible = if (appendResults) firstVisible else 0
|
||||||
|
|
||||||
when (elementsShown) {
|
if (itemsCaching) {
|
||||||
UNREAD_SHOWN -> getUnRead(appendResults)
|
|
||||||
READ_SHOWN -> getRead(appendResults)
|
if (!swipeRefreshLayout.isRefreshing) {
|
||||||
FAV_SHOWN -> getStarred(appendResults)
|
swipeRefreshLayout.post { swipeRefreshLayout.isRefreshing = true }
|
||||||
else -> getUnRead(appendResults)
|
}
|
||||||
|
|
||||||
|
thread {
|
||||||
|
val dbItems = db.itemsDao().items().map { it.toView() }
|
||||||
|
runOnUiThread {
|
||||||
|
if (dbItems.isNotEmpty()) {
|
||||||
|
items = when (elementsShown) {
|
||||||
|
UNREAD_SHOWN -> ArrayList(dbItems.filter { it.unread })
|
||||||
|
READ_SHOWN -> ArrayList(dbItems.filter { !it.unread })
|
||||||
|
FAV_SHOWN -> ArrayList(dbItems.filter { it.starred })
|
||||||
|
else -> ArrayList(dbItems.filter { it.unread })
|
||||||
|
}
|
||||||
|
handleListResult()
|
||||||
|
doGetAccordingToTab()
|
||||||
|
} else {
|
||||||
|
doGetAccordingToTab()
|
||||||
|
getAndStoreAllItems()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
doGetAccordingToTab()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun filter(tags: String): Boolean {
|
private fun filter(tags: String): Boolean {
|
||||||
@ -877,9 +922,10 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
call: (String?, Long?, String?) -> Call<List<Item>>
|
call: (String?, Long?, String?) -> Call<List<Item>>
|
||||||
) {
|
) {
|
||||||
fun handleItemsResponse(response: Response<List<Item>>) {
|
fun handleItemsResponse(response: Response<List<Item>>) {
|
||||||
val shouldUpdate = (response.body() != items)
|
val shouldUpdate = (response.body()?.toSet() != items.toSet())
|
||||||
if (response.body() != null) {
|
if (response.body() != null) {
|
||||||
if (shouldUpdate) {
|
if (shouldUpdate) {
|
||||||
|
getAndStoreAllItems()
|
||||||
items = response.body() as ArrayList<Item>
|
items = response.body() as ArrayList<Item>
|
||||||
items = items.filter {
|
items = items.filter {
|
||||||
maybeTagFilter != null || filter(it.tags)
|
maybeTagFilter != null || filter(it.tags)
|
||||||
@ -899,9 +945,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
allItems = ArrayList()
|
allItems = ArrayList()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (shouldUpdate) {
|
|
||||||
handleListResult(appendResults)
|
handleListResult(appendResults)
|
||||||
}
|
|
||||||
|
|
||||||
if (!appendResults) mayBeEmpty()
|
if (!appendResults) mayBeEmpty()
|
||||||
swipeRefreshLayout.isRefreshing = false
|
swipeRefreshLayout.isRefreshing = false
|
||||||
@ -1178,7 +1223,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
.toMap()
|
.toMap()
|
||||||
|
|
||||||
fun readAllDebug(e: Throwable) {
|
fun readAllDebug(e: Throwable) {
|
||||||
// TODO: debug
|
ACRA.getErrorReporter().maybeHandleSilentException(e, this@HomeActivity)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ids.isNotEmpty()) {
|
if (ids.isNotEmpty()) {
|
||||||
@ -1266,8 +1311,26 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
|||||||
else -> badgeNew // if !elementsShown then unread are fetched.
|
else -> badgeNew // if !elementsShown then unread are fetched.
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updateItems(adapterItems: ArrayList<Item>) {
|
private fun updateItems(adapterItems: ArrayList<Item>) {
|
||||||
items = adapterItems
|
items = adapterItems
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun handleGDPRDialog(GDPRShown: Boolean) {
|
||||||
|
val sharedEditor = sharedPref.edit()
|
||||||
|
if (!GDPRShown) {
|
||||||
|
val alertDialog = AlertDialog.Builder(this).create()
|
||||||
|
alertDialog.setTitle(getString(R.string.gdpr_dialog_title))
|
||||||
|
alertDialog.setMessage(getString(R.string.gdpr_dialog_message))
|
||||||
|
alertDialog.setButton(
|
||||||
|
AlertDialog.BUTTON_NEUTRAL,
|
||||||
|
"OK"
|
||||||
|
) { dialog, _ ->
|
||||||
|
sharedEditor.putBoolean("GDPR_shown", true)
|
||||||
|
sharedEditor.commit()
|
||||||
|
dialog.dismiss()
|
||||||
|
}
|
||||||
|
alertDialog.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,13 +9,13 @@ import apps.amine.bou.readerforselfoss.utils.Config
|
|||||||
import apps.amine.bou.readerforselfoss.utils.isEmptyOrNullOrNullString
|
import apps.amine.bou.readerforselfoss.utils.isEmptyOrNullOrNullString
|
||||||
import com.google.gson.annotations.SerializedName
|
import com.google.gson.annotations.SerializedName
|
||||||
|
|
||||||
private fun constructUrl(config: Config?, path: String, file: String): String {
|
private fun constructUrl(config: Config?, path: String, file: String?): String {
|
||||||
val baseUriBuilder = Uri.parse(config!!.baseUrl).buildUpon()
|
|
||||||
baseUriBuilder.appendPath(path).appendPath(file)
|
|
||||||
|
|
||||||
return if (file.isEmptyOrNullOrNullString()) {
|
return if (file.isEmptyOrNullOrNullString()) {
|
||||||
""
|
""
|
||||||
} else {
|
} else {
|
||||||
|
val baseUriBuilder = Uri.parse(config!!.baseUrl).buildUpon()
|
||||||
|
baseUriBuilder.appendPath(path).appendPath(file)
|
||||||
|
|
||||||
baseUriBuilder.toString()
|
baseUriBuilder.toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,9 @@ import apps.amine.bou.readerforselfoss.persistence.entities.ItemEntity
|
|||||||
import apps.amine.bou.readerforselfoss.persistence.entities.SourceEntity
|
import apps.amine.bou.readerforselfoss.persistence.entities.SourceEntity
|
||||||
import apps.amine.bou.readerforselfoss.persistence.entities.TagEntity
|
import apps.amine.bou.readerforselfoss.persistence.entities.TagEntity
|
||||||
|
|
||||||
@Database(entities = [TagEntity::class, SourceEntity::class], version = 1)
|
@Database(entities = [TagEntity::class, SourceEntity::class, ItemEntity::class], version = 2)
|
||||||
abstract class AppDatabase : RoomDatabase() {
|
abstract class AppDatabase : RoomDatabase() {
|
||||||
abstract fun drawerDataDao(): DrawerDataDao
|
abstract fun drawerDataDao(): DrawerDataDao
|
||||||
|
|
||||||
|
abstract fun itemsDao(): ItemsDao
|
||||||
}
|
}
|
@ -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`))")
|
||||||
|
}
|
||||||
|
}
|
@ -135,6 +135,7 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
|||||||
return PreferenceFragment.class.getName().equals(fragmentName)
|
return PreferenceFragment.class.getName().equals(fragmentName)
|
||||||
|| GeneralPreferenceFragment.class.getName().equals(fragmentName)
|
|| GeneralPreferenceFragment.class.getName().equals(fragmentName)
|
||||||
|| ArticleViewerPreferenceFragment.class.getName().equals(fragmentName)
|
|| ArticleViewerPreferenceFragment.class.getName().equals(fragmentName)
|
||||||
|
|| OfflinePreferenceFragment.class.getName().equals(fragmentName)
|
||||||
|| DebugPreferenceFragment.class.getName().equals(fragmentName)
|
|| DebugPreferenceFragment.class.getName().equals(fragmentName)
|
||||||
|| LinksPreferenceFragment.class.getName().equals(fragmentName)
|
|| LinksPreferenceFragment.class.getName().equals(fragmentName)
|
||||||
|| ThemePreferenceFragment.class.getName().equals(fragmentName);
|
|| ThemePreferenceFragment.class.getName().equals(fragmentName);
|
||||||
@ -363,6 +364,27 @@ public class SettingsActivity extends AppCompatPreferenceActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
|
||||||
|
public static class OfflinePreferenceFragment extends PreferenceFragment {
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
addPreferencesFromResource(R.xml.pref_offline);
|
||||||
|
setHasOptionsMenu(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
|
int id = item.getItemId();
|
||||||
|
if (id == android.R.id.home) {
|
||||||
|
getActivity().finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
int id = item.getItemId();
|
int id = item.getItemId();
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 523 B |
Binary file not shown.
After Width: | Height: | Size: 361 B |
Binary file not shown.
After Width: | Height: | Size: 660 B |
Binary file not shown.
After Width: | Height: | Size: 982 B |
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
@ -17,7 +17,6 @@
|
|||||||
<string name="action_disconnect">"Disconnect"</string>
|
<string name="action_disconnect">"Disconnect"</string>
|
||||||
<string name="title_activity_settings">"Settings"</string>
|
<string name="title_activity_settings">"Settings"</string>
|
||||||
<string name="pref_header_general">"General"</string>
|
<string name="pref_header_general">"General"</string>
|
||||||
<string name="pref_switch_actions_tap_title">"Tap action on the articles"</string>
|
|
||||||
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
|
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
|
||||||
<string name="add_source_hint_url">"Link"</string>
|
<string name="add_source_hint_url">"Link"</string>
|
||||||
<string name="add_source_hint_name">"Name"</string>
|
<string name="add_source_hint_name">"Name"</string>
|
||||||
@ -71,12 +70,8 @@
|
|||||||
<string name="prefer_article_viewer_off">"Will use the internal browser instead of the article viewer"</string>
|
<string name="prefer_article_viewer_off">"Will use the internal browser instead of the article viewer"</string>
|
||||||
<string name="pref_general_category_links">"Link handling"</string>
|
<string name="pref_general_category_links">"Link handling"</string>
|
||||||
<string name="pref_general_category_displaying">"Displaying"</string>
|
<string name="pref_general_category_displaying">"Displaying"</string>
|
||||||
<string name="pref_general_category_actions">"Actions"</string>
|
|
||||||
<string name="pref_switch_card_view_on">"The articles will be displayed as cards"</string>
|
<string name="pref_switch_card_view_on">"The articles will be displayed as cards"</string>
|
||||||
<string name="pref_switch_card_view_off">"The articles will be displayed as a list"</string>
|
<string name="pref_switch_card_view_off">"The articles will be displayed as a list"</string>
|
||||||
<string name="pref_switch_actions_tap_on">"Displays the action bar under the article"</string>
|
|
||||||
<string
|
|
||||||
name="pref_switch_actions_tap_off">"When selecting an article it will open in your selected browser"</string>
|
|
||||||
<string name="menu_home_refresh">"Update remote"</string>
|
<string name="menu_home_refresh">"Update remote"</string>
|
||||||
<string name="refresh_success_response">"The remote is updated, you can now reload the articles list"</string>
|
<string name="refresh_success_response">"The remote is updated, you can now reload the articles list"</string>
|
||||||
<string
|
<string
|
||||||
@ -94,7 +89,6 @@
|
|||||||
<string name="drawer_item_tags">Tags</string>
|
<string name="drawer_item_tags">Tags</string>
|
||||||
<string name="drawer_item_sources">Sources</string>
|
<string name="drawer_item_sources">Sources</string>
|
||||||
<string name="drawer_action_edit">edit</string>
|
<string name="drawer_action_edit">edit</string>
|
||||||
<string name="cache_drawer_error" tools:keep="@string/cache_drawer_error">Couldn\'t cache your drawer data</string>
|
|
||||||
<string name="no_tags_loaded">No tags loaded</string>
|
<string name="no_tags_loaded">No tags loaded</string>
|
||||||
<string name="no_sources_loaded">No sources loaded</string>
|
<string name="no_sources_loaded">No sources loaded</string>
|
||||||
<string name="drawer_loading">Loading …</string>
|
<string name="drawer_loading">Loading …</string>
|
||||||
@ -154,4 +148,8 @@
|
|||||||
<string name="acra_login">Enable logging</string>
|
<string name="acra_login">Enable logging</string>
|
||||||
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
||||||
<string name="unmark">Mark item as unread</string>
|
<string name="unmark">Mark item as unread</string>
|
||||||
|
<string name="pref_header_offline">Offline and cache</string>
|
||||||
|
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||||
|
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||||
|
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@ -74,9 +74,5 @@
|
|||||||
android:dependency="display_unread_count"
|
android:dependency="display_unread_count"
|
||||||
android:key="display_other_count"
|
android:key="display_other_count"
|
||||||
android:title="@string/display_all_counts_title" />
|
android:title="@string/display_all_counts_title" />
|
||||||
<PreferenceCategory
|
|
||||||
android:title="@string/pref_general_category_actions">
|
|
||||||
|
|
||||||
</PreferenceCategory>
|
|
||||||
|
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
|
@ -11,6 +11,12 @@
|
|||||||
android:icon="@drawable/ic_chrome_reader_mode_black_24"
|
android:icon="@drawable/ic_chrome_reader_mode_black_24"
|
||||||
android:title="@string/pref_header_viewer"/>
|
android:title="@string/pref_header_viewer"/>
|
||||||
|
|
||||||
|
|
||||||
|
<header
|
||||||
|
android:fragment="apps.amine.bou.readerforselfoss.settings.SettingsActivity$OfflinePreferenceFragment"
|
||||||
|
android:icon="@drawable/ic_signal_wifi_statusbar_not_connected"
|
||||||
|
android:title="@string/pref_header_offline"/>
|
||||||
|
|
||||||
<header
|
<header
|
||||||
android:fragment="apps.amine.bou.readerforselfoss.settings.SettingsActivity$DebugPreferenceFragment"
|
android:fragment="apps.amine.bou.readerforselfoss.settings.SettingsActivity$DebugPreferenceFragment"
|
||||||
android:icon="@drawable/ic_bug_report"
|
android:icon="@drawable/ic_bug_report"
|
||||||
|
8
app/src/main/res/xml/pref_offline.xml
Normal file
8
app/src/main/res/xml/pref_offline.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<SwitchPreference
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="items_caching"
|
||||||
|
android:summaryOff="@string/pref_switch_items_caching_off"
|
||||||
|
android:summaryOn="@string/pref_switch_items_caching_on"
|
||||||
|
android:title="@string/pref_switch_items_caching" />
|
||||||
|
</PreferenceScreen>
|
11
build.sh
11
build.sh
@ -1,10 +1,15 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
BASE_VERSION="1.6"
|
BASE_VERSION="1.7"
|
||||||
TODAYS_VERSION="1"
|
TODAYS_VERSION="1"
|
||||||
|
|
||||||
VERSION="${BASE_VERSION//./}$(date '+%y%m%j')$TODAYS_VERSION"
|
VERSION="${BASE_VERSION//./}$(date '+%y%m%j')$TODAYS_VERSION"
|
||||||
|
|
||||||
./version.sh ${VERSION} $@
|
./version.sh ${VERSION} $1
|
||||||
|
|
||||||
./publish-version.sh ${VERSION}
|
if [[ "$@" == *'--publish'* ]]
|
||||||
|
then
|
||||||
|
./publish-version.sh ${VERSION}
|
||||||
|
else
|
||||||
|
echo "Did not publish. If you wanted to do so, call the script with \"--publish\"."
|
||||||
|
fi
|
||||||
|
9
publish-version.sh
Executable file
9
publish-version.sh
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
rm -f version.txt
|
||||||
|
printf "versionName=$1-github\nversionCode=$1" >> version.txt
|
||||||
|
|
||||||
|
# You'll need to change server as your server and define a VERSION_PATH.
|
||||||
|
scp version.txt server:$VERSION_PATH
|
||||||
|
|
||||||
|
rm version.txt
|
Loading…
Reference in New Issue
Block a user