diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts index b548d33..846f050 100644 --- a/androidApp/build.gradle.kts +++ b/androidApp/build.gradle.kts @@ -78,12 +78,6 @@ android { // tests testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" - - javaCompileOptions { - annotationProcessorOptions { - arguments += mapOf("room.schemaLocation" to "$projectDir/schemas") - } - } } buildTypes { getByName("release") { @@ -196,16 +190,14 @@ dependencies { implementation("androidx.core:core-ktx:1.8.0") - implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.5.1") - implementation("androidx.lifecycle:lifecycle-common-java8:2.5.1") - implementation("androidx.lifecycle:lifecycle-runtime:2.5.1") + // implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.5.1") + // implementation("androidx.lifecycle:lifecycle-common-java8:2.5.1") + // implementation("androidx.lifecycle:lifecycle-runtime:2.5.1") implementation("androidx.lifecycle:lifecycle-extensions:2.2.0") - implementation("androidx.room:room-ktx:2.4.0-beta01") - kapt("androidx.room:room-compiler:2.4.0-beta01") - - implementation("android.arch.work:work-runtime-ktx:1.0.1") - // Network information implementation("com.github.ln-12:multiplatform-connectivity-status:1.3.0") + + // SQLDELIGHT + implementation("com.squareup.sqldelight:android-driver:1.5.3") } \ No newline at end of file diff --git a/androidApp/schemas/apps.amine.bou.readerforselfoss.persistence.database.AppDatabase/1.json b/androidApp/schemas/apps.amine.bou.readerforselfoss.persistence.database.AppDatabase/1.json deleted file mode 100644 index b478c9e..0000000 --- a/androidApp/schemas/apps.amine.bou.readerforselfoss.persistence.database.AppDatabase/1.json +++ /dev/null @@ -1,96 +0,0 @@ -{ - "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\")" - ] - } -} \ No newline at end of file diff --git a/androidApp/schemas/apps.amine.bou.readerforselfoss.persistence.database.AppDatabase/2.json b/androidApp/schemas/apps.amine.bou.readerforselfoss.persistence.database.AppDatabase/2.json deleted file mode 100644 index c9d43bc..0000000 --- a/androidApp/schemas/apps.amine.bou.readerforselfoss.persistence.database.AppDatabase/2.json +++ /dev/null @@ -1,176 +0,0 @@ -{ - "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\")" - ] - } -} \ No newline at end of file diff --git a/androidApp/schemas/apps.amine.bou.readerforselfoss.persistence.database.AppDatabase/3.json b/androidApp/schemas/apps.amine.bou.readerforselfoss.persistence.database.AppDatabase/3.json deleted file mode 100644 index 70d1621..0000000 --- a/androidApp/schemas/apps.amine.bou.readerforselfoss.persistence.database.AppDatabase/3.json +++ /dev/null @@ -1,226 +0,0 @@ -{ - "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\")" - ] - } -} \ No newline at end of file diff --git a/androidApp/schemas/apps.amine.bou.readerforselfoss.persistence.database.AppDatabase/4.json b/androidApp/schemas/apps.amine.bou.readerforselfoss.persistence.database.AppDatabase/4.json deleted file mode 100644 index 051cd37..0000000 --- a/androidApp/schemas/apps.amine.bou.readerforselfoss.persistence.database.AppDatabase/4.json +++ /dev/null @@ -1,226 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 4, - "identityHash": "9cf8b03d32f80dfd58160599a1df197d", - "entities": [ - { - "tableName": "tags", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tag` TEXT NOT NULL, `color` TEXT NOT NULL, `unread` INTEGER NOT NULL, PRIMARY KEY(`tag`))", - "fields": [ - { - "fieldPath": "tag", - "columnName": "tag", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "tag" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "sources", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `title` TEXT NOT NULL, `tags` TEXT NOT NULL, `spout` TEXT NOT NULL, `error` TEXT NOT NULL, `icon` TEXT NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "tags", - "columnName": "tags", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "spout", - "columnName": "spout", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "error", - "columnName": "error", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "icon", - "columnName": "icon", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "items", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `datetime` TEXT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `unread` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `thumbnail` TEXT, `icon` TEXT, `link` TEXT NOT NULL, `sourcetitle` TEXT NOT NULL, `tags` TEXT NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "datetime", - "columnName": "datetime", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "starred", - "columnName": "starred", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "thumbnail", - "columnName": "thumbnail", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "icon", - "columnName": "icon", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "link", - "columnName": "link", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "sourcetitle", - "columnName": "sourcetitle", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "tags", - "columnName": "tags", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "actions", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `articleid` TEXT NOT NULL, `read` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `unstarred` INTEGER NOT NULL)", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "articleId", - "columnName": "articleid", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "read", - "columnName": "read", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "starred", - "columnName": "starred", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "unstarred", - "columnName": "unstarred", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"9cf8b03d32f80dfd58160599a1df197d\")" - ] - } -} \ No newline at end of file diff --git a/androidApp/schemas/bou.amine.apps.readerforselfossv2.android.persistence.database.AppDatabase/4.json b/androidApp/schemas/bou.amine.apps.readerforselfossv2.android.persistence.database.AppDatabase/4.json deleted file mode 100644 index 7b7d406..0000000 --- a/androidApp/schemas/bou.amine.apps.readerforselfossv2.android.persistence.database.AppDatabase/4.json +++ /dev/null @@ -1,226 +0,0 @@ -{ - "formatVersion": 1, - "database": { - "version": 4, - "identityHash": "9cf8b03d32f80dfd58160599a1df197d", - "entities": [ - { - "tableName": "tags", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`tag` TEXT NOT NULL, `color` TEXT NOT NULL, `unread` INTEGER NOT NULL, PRIMARY KEY(`tag`))", - "fields": [ - { - "fieldPath": "tag", - "columnName": "tag", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "color", - "columnName": "color", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "tag" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "sources", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `title` TEXT NOT NULL, `tags` TEXT NOT NULL, `spout` TEXT NOT NULL, `error` TEXT NOT NULL, `icon` TEXT NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "tags", - "columnName": "tags", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "spout", - "columnName": "spout", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "error", - "columnName": "error", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "icon", - "columnName": "icon", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "items", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` TEXT NOT NULL, `datetime` TEXT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `unread` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `thumbnail` TEXT, `icon` TEXT, `link` TEXT NOT NULL, `sourcetitle` TEXT NOT NULL, `tags` TEXT NOT NULL, PRIMARY KEY(`id`))", - "fields": [ - { - "fieldPath": "id", - "columnName": "id", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "datetime", - "columnName": "datetime", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "title", - "columnName": "title", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "content", - "columnName": "content", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "unread", - "columnName": "unread", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "starred", - "columnName": "starred", - "affinity": "INTEGER", - "notNull": true - }, - { - "fieldPath": "thumbnail", - "columnName": "thumbnail", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "icon", - "columnName": "icon", - "affinity": "TEXT", - "notNull": false - }, - { - "fieldPath": "link", - "columnName": "link", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "sourcetitle", - "columnName": "sourcetitle", - "affinity": "TEXT", - "notNull": true - }, - { - "fieldPath": "tags", - "columnName": "tags", - "affinity": "TEXT", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": false - }, - "indices": [], - "foreignKeys": [] - }, - { - "tableName": "actions", - "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`articleid` TEXT NOT NULL, `read` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `unstarred` INTEGER NOT NULL, `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL)", - "fields": [ - { - "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 - }, - { - "fieldPath": "id", - "columnName": "id", - "affinity": "INTEGER", - "notNull": true - } - ], - "primaryKey": { - "columnNames": [ - "id" - ], - "autoGenerate": true - }, - "indices": [], - "foreignKeys": [] - } - ], - "views": [], - "setupQueries": [ - "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", - "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '9cf8b03d32f80dfd58160599a1df197d')" - ] - } -} \ No newline at end of file diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/AddSourceActivity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/AddSourceActivity.kt index 0edd1c2..7df13e1 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/AddSourceActivity.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/AddSourceActivity.kt @@ -11,6 +11,7 @@ import bou.amine.apps.readerforselfossv2.android.themes.AppColors import bou.amine.apps.readerforselfossv2.android.themes.Toppings import bou.amine.apps.readerforselfossv2.android.utils.Config import bou.amine.apps.readerforselfossv2.android.utils.isBaseUrlValid +import bou.amine.apps.readerforselfossv2.model.NetworkUnavailableException import bou.amine.apps.readerforselfossv2.repository.Repository import com.ftinc.scoop.Scoop import kotlinx.coroutines.CoroutineScope @@ -109,33 +110,40 @@ class AddSourceActivity : AppCompatActivity(), DIAware { } + fun handleSpoutFailure(networkIssue: Boolean = false) { + Toast.makeText( + this@AddSourceActivity, + if (networkIssue) R.string.cant_get_spouts_no_network else R.string.cant_get_spouts, + Toast.LENGTH_SHORT + ).show() + mProgress.visibility = View.GONE + } + CoroutineScope(Dispatchers.Main).launch { - val items = repository.getSpouts() - if (items != null) { + try { + val items = repository.getSpouts() + if (items != null) { + val itemsStrings = items.map { it.value.name } + for ((key, value) in items) { + spoutsKV[value.name] = key + } - val itemsStrings = items.map { it.value.name } - for ((key, value) in items) { - spoutsKV[value.name] = key + mProgress.visibility = View.GONE + formContainer.visibility = View.VISIBLE + + val spinnerArrayAdapter = + ArrayAdapter( + this@AddSourceActivity, + android.R.layout.simple_spinner_item, + itemsStrings + ) + spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) + spoutsSpinner.adapter = spinnerArrayAdapter + } else { + handleSpoutFailure() } - - mProgress.visibility = View.GONE - formContainer.visibility = View.VISIBLE - - val spinnerArrayAdapter = - ArrayAdapter( - this@AddSourceActivity, - android.R.layout.simple_spinner_item, - itemsStrings - ) - spinnerArrayAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item) - spoutsSpinner.adapter = spinnerArrayAdapter - } else { - Toast.makeText( - this@AddSourceActivity, - R.string.cant_get_spouts, - Toast.LENGTH_SHORT - ).show() - mProgress.visibility = View.GONE + } catch (e: NetworkUnavailableException) { + handleSpoutFailure(networkIssue = true) } } } diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/HomeActivity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/HomeActivity.kt index 4934d5f..722a8e4 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/HomeActivity.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/HomeActivity.kt @@ -18,7 +18,6 @@ import androidx.appcompat.widget.SearchView import androidx.core.view.doOnNextLayout import androidx.drawerlayout.widget.DrawerLayout import androidx.recyclerview.widget.* -import androidx.room.Room import androidx.work.Constraints import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.PeriodicWorkRequestBuilder @@ -28,15 +27,6 @@ import bou.amine.apps.readerforselfossv2.android.adapters.ItemListAdapter import bou.amine.apps.readerforselfossv2.android.adapters.ItemsAdapter import bou.amine.apps.readerforselfossv2.android.background.LoadingWorker import bou.amine.apps.readerforselfossv2.android.databinding.ActivityHomeBinding -import bou.amine.apps.readerforselfossv2.android.model.getIcon -import bou.amine.apps.readerforselfossv2.android.model.getTitleDecoded -import bou.amine.apps.readerforselfossv2.android.persistence.AndroidDeviceDatabase -import bou.amine.apps.readerforselfossv2.android.persistence.AndroidDeviceDatabaseService -import bou.amine.apps.readerforselfossv2.android.persistence.database.AppDatabase -import bou.amine.apps.readerforselfossv2.android.persistence.entities.ActionEntity -import bou.amine.apps.readerforselfossv2.android.persistence.migrations.MIGRATION_1_2 -import bou.amine.apps.readerforselfossv2.android.persistence.migrations.MIGRATION_2_3 -import bou.amine.apps.readerforselfossv2.android.persistence.migrations.MIGRATION_3_4 import bou.amine.apps.readerforselfossv2.android.settings.SettingsActivity import bou.amine.apps.readerforselfossv2.android.themes.AppColors import bou.amine.apps.readerforselfossv2.android.themes.Toppings @@ -44,13 +34,10 @@ import bou.amine.apps.readerforselfossv2.android.utils.Config import bou.amine.apps.readerforselfossv2.android.utils.bottombar.maybeShow import bou.amine.apps.readerforselfossv2.android.utils.bottombar.removeBadge import bou.amine.apps.readerforselfossv2.android.utils.customtabs.CustomTabActivityHelper -import bou.amine.apps.readerforselfossv2.android.utils.persistence.toEntity -import bou.amine.apps.readerforselfossv2.android.utils.persistence.toView -import bou.amine.apps.readerforselfossv2.android.viewmodel.AppViewModel +import bou.amine.apps.readerforselfossv2.dao.ACTION import bou.amine.apps.readerforselfossv2.repository.Repository -import bou.amine.apps.readerforselfossv2.rest.SelfossModel -import bou.amine.apps.readerforselfossv2.utils.ItemType -import bou.amine.apps.readerforselfossv2.utils.longHash +import bou.amine.apps.readerforselfossv2.model.SelfossModel +import bou.amine.apps.readerforselfossv2.utils.* import com.ashokvarma.bottomnavigation.BottomNavigationBar import com.ashokvarma.bottomnavigation.BottomNavigationItem import com.ashokvarma.bottomnavigation.TextBadgeItem @@ -83,8 +70,6 @@ import kotlin.concurrent.thread class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAware { - private lateinit var dataBase: AndroidDeviceDatabase - private lateinit var dbService: AndroidDeviceDatabaseService private val MENU_PREFERENCES = 12302 private val DRAWER_ID_TAGS = 100101L private val DRAWER_ID_HIDDEN_TAGS = 101100L @@ -129,8 +114,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar private lateinit var tagsBadge: Map - private lateinit var db: AppDatabase - private lateinit var config: Config override val di by closestDI() @@ -169,15 +152,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar binding.drawerContainer.addDrawerListener(mDrawerToggle) mDrawerToggle.syncState() - db = Room.databaseBuilder( - applicationContext, - AppDatabase::class.java, "selfoss-database" - ).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).addMigrations(MIGRATION_3_4).build() - customTabActivityHelper = CustomTabActivityHelper() - dataBase = AndroidDeviceDatabase(applicationContext) - handleBottomBar() handleDrawer() @@ -186,6 +162,12 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar handleSettings() getElementsAccordingToTab() + + + CoroutineScope(Dispatchers.Main).launch { + repository.tryToCacheItemsAndGetNewOnes() + } + } private fun handleSwipeRefreshLayout() { @@ -466,6 +448,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar } } + // TODO: refactor this. private fun handleDrawerItems() { tagsBadge = emptyMap() fun handleDrawerData(maybeDrawerData: DrawerData?, loadedFromCache: Boolean = false) { @@ -486,7 +469,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar val drawerItem = PrimaryDrawerItem() .apply { - nameText = it.getTitleDecoded() + nameText = it.tag.getHtmlDecoded() identifier = it.tag.longHash() iconDrawable = gd badgeStyle = BadgeStyle().apply { @@ -562,7 +545,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar } else { for (source in maybeSources) { val item = PrimaryDrawerItem().apply { - nameText = source.getTitleDecoded() + nameText = source.title.getHtmlDecoded() identifier = source.id.toLong() iconUrl = source.getIcon(repository.baseUrl) onDrawerItemClickListener = { _,_,_ -> @@ -650,17 +633,12 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar if (!loadedFromCache) { if (maybeDrawerData.tags != null) { thread { - val tagEntities = maybeDrawerData.tags.map { it.toEntity() } - db.drawerDataDao().deleteAllTags() - db.drawerDataDao().insertAllTags(*tagEntities.toTypedArray()) + repository.resetDBTagsWithData(maybeDrawerData.tags) } } if (maybeDrawerData.sources != null) { thread { - val sourceEntities = - maybeDrawerData.sources.map { it.toEntity() } - db.drawerDataDao().deleteAllSources() - db.drawerDataDao().insertAllSources(*sourceEntities.toTypedArray()) + repository.resetDBSourcesWithData(maybeDrawerData.sources) } } } @@ -718,8 +696,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar ) thread { - val drawerData = DrawerData(db.drawerDataDao().tags().map { it.toView() }, - db.drawerDataDao().sources().map { it.toView() }) + val drawerData = DrawerData(repository.getDBTags().map { it.toView() }, + repository.getDBSources().map { it.toView() }) runOnUiThread { handleDrawerData(drawerData, loadedFromCache = true) drawerApiCalls(drawerData) @@ -894,7 +872,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar ItemCardAdapter( this, items, - db, customTabActivityHelper, internalBrowser, articleViewer, @@ -909,7 +886,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar ItemListAdapter( this, items, - db, customTabActivityHelper, internalBrowser, articleViewer, @@ -1106,23 +1082,23 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener, DIAwar } private fun handleOfflineActions() { - fun doAndReportOnFail(success: Boolean, action: ActionEntity) { + fun doAndReportOnFail(success: Boolean, action: ACTION) { if (success) { thread { - db.actionsDao().delete(action) + repository.deleteDBAction(action) } } } CoroutineScope(Dispatchers.Main).launch { - val actions = db.actionsDao().actions() + val actions = repository.getDBActions() actions.forEach { action -> when { - action.read -> doAndReportOnFail(repository.markAsReadById(action.articleId.toInt()), action) - action.unread -> doAndReportOnFail(repository.unmarkAsReadById(action.articleId.toInt()), action) - action.starred -> doAndReportOnFail(repository.starrById(action.articleId.toInt()), action) - action.unstarred -> doAndReportOnFail(repository.unstarrById(action.articleId.toInt()), action) + action.read -> doAndReportOnFail(repository.markAsReadById(action.articleid.toInt()), action) + action.unread -> doAndReportOnFail(repository.unmarkAsReadById(action.articleid.toInt()), action) + action.starred -> doAndReportOnFail(repository.starrById(action.articleid.toInt()), action) + action.unstarred -> doAndReportOnFail(repository.unstarrById(action.articleid.toInt()), action) } } } diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/MyApp.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/MyApp.kt index be77f62..887fc5a 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/MyApp.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/MyApp.kt @@ -11,13 +11,14 @@ import android.widget.Toast import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.ProcessLifecycleOwner -import androidx.lifecycle.lifecycleScope import androidx.multidex.MultiDexApplication import androidx.preference.PreferenceManager import bou.amine.apps.readerforselfossv2.DI.networkModule import bou.amine.apps.readerforselfossv2.android.utils.Config import bou.amine.apps.readerforselfossv2.android.utils.glide.loadMaybeBasicAuth import bou.amine.apps.readerforselfossv2.android.viewmodel.AppViewModel +import bou.amine.apps.readerforselfossv2.dao.DriverFactory +import bou.amine.apps.readerforselfossv2.dao.ReaderForSelfossDB import bou.amine.apps.readerforselfossv2.repository.Repository import com.bumptech.glide.Glide import com.bumptech.glide.request.RequestOptions @@ -26,18 +27,20 @@ import com.github.ln_12.library.ConnectivityStatus import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader import com.mikepenz.materialdrawer.util.DrawerImageLoader import com.russhwolf.settings.Settings +import io.github.aakira.napier.DebugAntilog +import io.github.aakira.napier.Napier import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import io.github.aakira.napier.DebugAntilog -import io.github.aakira.napier.Napier import org.kodein.di.* class MyApp : MultiDexApplication(), DIAware { override val di by DI.lazy { import(networkModule) - bind() with singleton { Repository(instance(), instance(), connectivityStatus) } + bind() with singleton { DriverFactory(applicationContext) } + bind() with singleton { ReaderForSelfossDB(driverFactory.createDriver()) } + bind() with singleton { Repository(instance(), instance(), connectivityStatus, instance()) } bind() with singleton { ConnectivityStatus(applicationContext) } bind() with singleton { AppViewModel(repository = instance()) } } @@ -45,6 +48,7 @@ class MyApp : MultiDexApplication(), DIAware { private val repository: Repository by instance() private val viewModel: AppViewModel by instance() private val connectivityStatus: ConnectivityStatus by instance() + private val driverFactory: DriverFactory by instance() private lateinit var config: Config private lateinit var settings : Settings @@ -73,6 +77,7 @@ class MyApp : MultiDexApplication(), DIAware { ).show() } } + } private fun handleNotificationChannels() { diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/ReaderActivity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/ReaderActivity.kt index 2839263..125940e 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/ReaderActivity.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/ReaderActivity.kt @@ -8,20 +8,14 @@ import android.view.MenuItem import androidx.appcompat.app.AppCompatActivity import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity -import androidx.room.Room import androidx.viewpager2.adapter.FragmentStateAdapter import androidx.viewpager2.widget.ViewPager2 import bou.amine.apps.readerforselfossv2.android.databinding.ActivityReaderBinding import bou.amine.apps.readerforselfossv2.android.fragments.ArticleFragment -import bou.amine.apps.readerforselfossv2.android.persistence.database.AppDatabase -import bou.amine.apps.readerforselfossv2.android.persistence.migrations.MIGRATION_1_2 -import bou.amine.apps.readerforselfossv2.android.persistence.migrations.MIGRATION_2_3 -import bou.amine.apps.readerforselfossv2.android.persistence.migrations.MIGRATION_3_4 import bou.amine.apps.readerforselfossv2.android.themes.AppColors import bou.amine.apps.readerforselfossv2.android.themes.Toppings -import bou.amine.apps.readerforselfossv2.android.utils.toggleStar import bou.amine.apps.readerforselfossv2.repository.Repository -import bou.amine.apps.readerforselfossv2.rest.SelfossModel +import bou.amine.apps.readerforselfossv2.model.SelfossModel import com.ftinc.scoop.Scoop import com.russhwolf.settings.Settings import kotlinx.coroutines.CoroutineScope @@ -39,7 +33,6 @@ class ReaderActivity : AppCompatActivity(), DIAware { private lateinit var toolbarMenu: Menu - private lateinit var db: AppDatabase private lateinit var binding: ActivityReaderBinding private var activeAlignment: Int = 1 @@ -47,7 +40,7 @@ class ReaderActivity : AppCompatActivity(), DIAware { private val ALIGN_LEFT = 2 override val di by closestDI() - private val repository : Repository by instance() + private val repository: Repository by instance() private fun showMenuItem(willAddToFavorite: Boolean) { if (willAddToFavorite) { @@ -75,11 +68,6 @@ class ReaderActivity : AppCompatActivity(), DIAware { setContentView(view) - db = Room.databaseBuilder( - applicationContext, - AppDatabase::class.java, "selfoss-database" - ).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).addMigrations(MIGRATION_3_4).build() - val scoop = Scoop.getInstance() scoop.bind(this, Toppings.PRIMARY.value, binding.toolBar) scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value) @@ -111,11 +99,11 @@ class ReaderActivity : AppCompatActivity(), DIAware { private fun readItem(item: SelfossModel.Item) { if (markOnScroll) { - CoroutineScope(Dispatchers.IO).launch { - repository.markAsRead(item) - // TODO: Handle failure - } + CoroutineScope(Dispatchers.IO).launch { + repository.markAsRead(item) + // TODO: Handle failure } + } } override fun onSaveInstanceState(oldInstanceState: Bundle) { @@ -128,19 +116,22 @@ class ReaderActivity : AppCompatActivity(), DIAware { override fun getItemCount(): Int = allItems.size - override fun createFragment(position: Int): Fragment = ArticleFragment.newInstance(allItems[position]) + override fun createFragment(position: Int): Fragment = + ArticleFragment.newInstance(allItems[position]) } override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { return when (keyCode) { KeyEvent.KEYCODE_VOLUME_DOWN -> { - val currentFragment = supportFragmentManager.findFragmentByTag("f" + binding.pager.currentItem) as ArticleFragment + val currentFragment = + supportFragmentManager.findFragmentByTag("f" + binding.pager.currentItem) as ArticleFragment currentFragment.scrollDown() true } KeyEvent.KEYCODE_VOLUME_UP -> { - val currentFragment = supportFragmentManager.findFragmentByTag("f" + binding.pager.currentItem) as ArticleFragment + val currentFragment = + supportFragmentManager.findFragmentByTag("f" + binding.pager.currentItem) as ArticleFragment currentFragment.scrollUp() true } @@ -169,19 +160,19 @@ class ReaderActivity : AppCompatActivity(), DIAware { alignmentMenu() binding.pager.registerOnPageChangeCallback( - object : ViewPager2.OnPageChangeCallback() { + object : ViewPager2.OnPageChangeCallback() { - override fun onPageSelected(position: Int) { - super.onPageSelected(position) + override fun onPageSelected(position: Int) { + super.onPageSelected(position) - if (allItems[position].starred) { - canRemoveFromFavorite() - } else { - canFavorite() - } - readItem(allItems[position]) + if (allItems[position].starred) { + canRemoveFromFavorite() + } else { + canFavorite() } + readItem(allItems[position]) } + } ) return true @@ -190,7 +181,7 @@ class ReaderActivity : AppCompatActivity(), DIAware { override fun onOptionsItemSelected(item: MenuItem): Boolean { fun afterSave() { allItems[binding.pager.currentItem] = - allItems[binding.pager.currentItem].toggleStar() + allItems[binding.pager.currentItem].toggleStar() canRemoveFromFavorite() } diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/SourcesActivity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/SourcesActivity.kt index 10988e9..90591e4 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/SourcesActivity.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/SourcesActivity.kt @@ -11,7 +11,7 @@ import bou.amine.apps.readerforselfossv2.android.databinding.ActivitySourcesBind import bou.amine.apps.readerforselfossv2.android.themes.AppColors import bou.amine.apps.readerforselfossv2.android.themes.Toppings import bou.amine.apps.readerforselfossv2.repository.Repository -import bou.amine.apps.readerforselfossv2.rest.SelfossModel +import bou.amine.apps.readerforselfossv2.model.SelfossModel import com.ftinc.scoop.Scoop import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemCardAdapter.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemCardAdapter.kt index aa87278..a7b42d6 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemCardAdapter.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemCardAdapter.kt @@ -10,14 +10,16 @@ import androidx.recyclerview.widget.RecyclerView import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.databinding.CardItemBinding import bou.amine.apps.readerforselfossv2.android.model.* -import bou.amine.apps.readerforselfossv2.android.persistence.database.AppDatabase import bou.amine.apps.readerforselfossv2.android.themes.AppColors import bou.amine.apps.readerforselfossv2.android.utils.* import bou.amine.apps.readerforselfossv2.android.utils.customtabs.CustomTabActivityHelper import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapCenterCrop import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable import bou.amine.apps.readerforselfossv2.repository.Repository -import bou.amine.apps.readerforselfossv2.rest.SelfossModel +import bou.amine.apps.readerforselfossv2.model.SelfossModel +import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded +import bou.amine.apps.readerforselfossv2.utils.getIcon +import bou.amine.apps.readerforselfossv2.utils.getThumbnail import com.amulyakhare.textdrawable.TextDrawable import com.amulyakhare.textdrawable.util.ColorGenerator import com.bumptech.glide.Glide @@ -31,7 +33,6 @@ import org.kodein.di.instance class ItemCardAdapter( override val app: Activity, override var items: ArrayList, - override val db: AppDatabase, private val helper: CustomTabActivityHelper, private val internalBrowser: Boolean, private val articleViewer: Boolean, @@ -58,7 +59,7 @@ class ItemCardAdapter( val itm = items[position] binding.favButton.isSelected = itm.starred - binding.title.text = itm.getTitleDecoded() + binding.title.text = itm.title.getHtmlDecoded() binding.title.setOnTouchListener(LinkOnTouchListener()) @@ -81,13 +82,13 @@ class ItemCardAdapter( } if (itm.getIcon(repository.baseUrl).isEmpty()) { - val color = generator.getColor(itm.getSourceTitle()) + val color = generator.getColor(itm.title.getHtmlDecoded()) val drawable = TextDrawable .builder() .round() - .build(itm.getSourceTitle().toTextDrawableString(c), color) + .build(itm.title.getHtmlDecoded().toTextDrawableString(), color) binding.sourceImage.setImageDrawable(drawable) } else { c.circularBitmapDrawable(config, itm.getIcon(repository.baseUrl), binding.sourceImage) @@ -128,7 +129,7 @@ class ItemCardAdapter( binding.shareBtn.setOnClickListener { val item = items[bindingAdapterPosition] - c.shareLink(item.getLinkDecoded(), item.getTitleDecoded()) + c.shareLink(item.getLinkDecoded(), item.title.getHtmlDecoded()) } binding.browserBtn.setOnClickListener { diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemListAdapter.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemListAdapter.kt index b48d358..27839a4 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemListAdapter.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemListAdapter.kt @@ -7,14 +7,16 @@ import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import bou.amine.apps.readerforselfossv2.android.databinding.ListItemBinding import bou.amine.apps.readerforselfossv2.android.model.* -import bou.amine.apps.readerforselfossv2.android.persistence.database.AppDatabase import bou.amine.apps.readerforselfossv2.android.themes.AppColors import bou.amine.apps.readerforselfossv2.android.utils.* import bou.amine.apps.readerforselfossv2.android.utils.customtabs.CustomTabActivityHelper import bou.amine.apps.readerforselfossv2.android.utils.glide.bitmapCenterCrop import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable import bou.amine.apps.readerforselfossv2.repository.Repository -import bou.amine.apps.readerforselfossv2.rest.SelfossModel +import bou.amine.apps.readerforselfossv2.model.SelfossModel +import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded +import bou.amine.apps.readerforselfossv2.utils.getIcon +import bou.amine.apps.readerforselfossv2.utils.getThumbnail import com.amulyakhare.textdrawable.TextDrawable import com.amulyakhare.textdrawable.util.ColorGenerator import org.kodein.di.DI @@ -24,7 +26,6 @@ import org.kodein.di.instance class ItemListAdapter( override val app: Activity, override var items: ArrayList, - override val db: AppDatabase, private val helper: CustomTabActivityHelper, private val internalBrowser: Boolean, private val articleViewer: Boolean, @@ -47,7 +48,7 @@ class ItemListAdapter( with(holder) { val itm = items[position] - binding.title.text = itm.getTitleDecoded() + binding.title.text = itm.title.getHtmlDecoded() binding.title.setOnTouchListener(LinkOnTouchListener()) @@ -58,13 +59,13 @@ class ItemListAdapter( if (itm.getThumbnail(repository.baseUrl).isEmpty()) { if (itm.getIcon(repository.baseUrl).isEmpty()) { - val color = generator.getColor(itm.getSourceTitle()) + val color = generator.getColor(itm.title.getHtmlDecoded()) val drawable = TextDrawable .builder() .round() - .build(itm.getSourceTitle().toTextDrawableString(c), color) + .build(itm.title.getHtmlDecoded().toTextDrawableString(), color) binding.itemImage.setImageDrawable(drawable) } else { diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemsAdapter.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemsAdapter.kt index 47d5128..7dea71a 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemsAdapter.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/ItemsAdapter.kt @@ -5,11 +5,10 @@ import android.graphics.Color import android.widget.TextView import androidx.recyclerview.widget.RecyclerView import bou.amine.apps.readerforselfossv2.android.R -import bou.amine.apps.readerforselfossv2.android.persistence.database.AppDatabase import bou.amine.apps.readerforselfossv2.android.themes.AppColors import bou.amine.apps.readerforselfossv2.android.utils.Config import bou.amine.apps.readerforselfossv2.repository.Repository -import bou.amine.apps.readerforselfossv2.rest.SelfossModel +import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.utils.ItemType import com.google.android.material.snackbar.Snackbar import kotlinx.coroutines.CoroutineScope @@ -20,7 +19,6 @@ import org.kodein.di.DIAware abstract class ItemsAdapter : RecyclerView.Adapter(), DIAware { abstract var items: ArrayList abstract val repository: Repository - abstract val db: AppDatabase abstract val app: Activity abstract val appColors: AppColors abstract val config: Config diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/SourcesListAdapter.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/SourcesListAdapter.kt index dd7d9e0..187af5a 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/SourcesListAdapter.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/adapters/SourcesListAdapter.kt @@ -10,13 +10,13 @@ import androidx.constraintlayout.widget.ConstraintLayout import androidx.recyclerview.widget.RecyclerView import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.databinding.SourceListItemBinding -import bou.amine.apps.readerforselfossv2.android.model.getIcon -import bou.amine.apps.readerforselfossv2.android.model.getTitleDecoded +import bou.amine.apps.readerforselfossv2.android.model.toTextDrawableString import bou.amine.apps.readerforselfossv2.android.utils.Config import bou.amine.apps.readerforselfossv2.android.utils.glide.circularBitmapDrawable -import bou.amine.apps.readerforselfossv2.android.utils.toTextDrawableString import bou.amine.apps.readerforselfossv2.repository.Repository -import bou.amine.apps.readerforselfossv2.rest.SelfossModel +import bou.amine.apps.readerforselfossv2.model.SelfossModel +import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded +import bou.amine.apps.readerforselfossv2.utils.getIcon import com.amulyakhare.textdrawable.TextDrawable import com.amulyakhare.textdrawable.util.ColorGenerator import kotlinx.coroutines.CoroutineScope @@ -49,19 +49,19 @@ class SourcesListAdapter( config = Config() if (itm.getIcon(repository.baseUrl).isEmpty()) { - val color = generator.getColor(itm.getTitleDecoded()) + val color = generator.getColor(itm.title.getHtmlDecoded()) val drawable = TextDrawable .builder() .round() - .build(itm.getTitleDecoded().toTextDrawableString(c), color) + .build(itm.title.getHtmlDecoded().toTextDrawableString(), color) binding.itemImage.setImageDrawable(drawable) } else { c.circularBitmapDrawable(config, itm.getIcon(repository.baseUrl), binding.itemImage) } - binding.sourceTitle.text = itm.getTitleDecoded() + binding.sourceTitle.text = itm.title.getHtmlDecoded() } override fun getItemCount(): Int = items.size diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/background/background.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/background/background.kt index 03a0097..93f16be 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/background/background.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/background/background.kt @@ -8,22 +8,17 @@ import android.os.Build import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat.PRIORITY_DEFAULT import androidx.core.app.NotificationCompat.PRIORITY_LOW -import androidx.room.Room import androidx.work.Worker import androidx.work.WorkerParameters import bou.amine.apps.readerforselfossv2.android.MainActivity import bou.amine.apps.readerforselfossv2.android.MyApp import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.model.preloadImages -import bou.amine.apps.readerforselfossv2.android.persistence.database.AppDatabase -import bou.amine.apps.readerforselfossv2.android.persistence.entities.ActionEntity -import bou.amine.apps.readerforselfossv2.android.persistence.migrations.MIGRATION_1_2 -import bou.amine.apps.readerforselfossv2.android.persistence.migrations.MIGRATION_2_3 -import bou.amine.apps.readerforselfossv2.android.persistence.migrations.MIGRATION_3_4 import bou.amine.apps.readerforselfossv2.android.utils.Config import bou.amine.apps.readerforselfossv2.android.utils.network.isNetworkAccessible +import bou.amine.apps.readerforselfossv2.dao.ACTION import bou.amine.apps.readerforselfossv2.repository.Repository -import bou.amine.apps.readerforselfossv2.rest.SelfossModel +import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.utils.ItemType import com.russhwolf.settings.Settings import kotlinx.coroutines.CoroutineScope @@ -36,7 +31,6 @@ import kotlin.concurrent.schedule import kotlin.concurrent.thread class LoadingWorker(val context: Context, params: WorkerParameters) : Worker(context, params), DIAware { - lateinit var db: AppDatabase override val di by lazy { (applicationContext as MyApp).di } private val repository : Repository by instance() @@ -63,43 +57,31 @@ override fun doWork(): Result { val notifyNewItems = settings.getBoolean("notify_new_items", false) - db = Room.databaseBuilder( - applicationContext, - AppDatabase::class.java, "selfoss-database" - ).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3) - .addMigrations(MIGRATION_3_4).build() - - val actions = db.actionsDao().actions() + val actions: List = repository.getDBActions() actions.forEach { action -> when { action.read -> doAndReportOnFail( - repository.markAsReadById(action.articleId.toInt()), + repository.markAsReadById(action.articleid.toInt()), action ) action.unread -> doAndReportOnFail( - repository.unmarkAsReadById(action.articleId.toInt()), + repository.unmarkAsReadById(action.articleid.toInt()), action ) action.starred -> doAndReportOnFail( - repository.starrById(action.articleId.toInt()), + repository.starrById(action.articleid.toInt()), action ) action.unstarred -> doAndReportOnFail( - repository.unstarrById(action.articleId.toInt()), + repository.unstarrById(action.articleid.toInt()), action ) } } launch { - try { - val newItems = repository.allItems(ItemType.UNREAD) - handleNewItemsNotification(newItems, notifyNewItems, notificationManager) - val readItems = repository.allItems(ItemType.ALL) - val starredItems = repository.allItems(ItemType.STARRED) - // TODO: save all to DB - } catch (e: Throwable) {} + handleNewItemsNotification(repository.tryToCacheItemsAndGetNewOnes(), notifyNewItems, notificationManager) } } } @@ -154,11 +136,10 @@ override fun doWork(): Result { } } - private fun doAndReportOnFail(result: Boolean, action: ActionEntity) { - // TODO: Failures should be reported + private fun doAndReportOnFail(result: Boolean, action: ACTION) { if (result) { thread { - db.actionsDao().delete(action) + repository.deleteDBAction(action) } } } diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ArticleFragment.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ArticleFragment.kt index abc8f2c..7c9f602 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ArticleFragment.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/fragments/ArticleFragment.kt @@ -19,24 +19,22 @@ import androidx.browser.customtabs.CustomTabsIntent import androidx.core.content.res.ResourcesCompat import androidx.core.widget.NestedScrollView import androidx.fragment.app.Fragment -import androidx.room.Room import bou.amine.apps.readerforselfossv2.android.ImageActivity import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.api.mercury.MercuryApi import bou.amine.apps.readerforselfossv2.android.api.mercury.ParsedContent import bou.amine.apps.readerforselfossv2.android.databinding.FragmentArticleBinding import bou.amine.apps.readerforselfossv2.android.model.* -import bou.amine.apps.readerforselfossv2.android.persistence.database.AppDatabase -import bou.amine.apps.readerforselfossv2.android.persistence.migrations.MIGRATION_1_2 -import bou.amine.apps.readerforselfossv2.android.persistence.migrations.MIGRATION_2_3 -import bou.amine.apps.readerforselfossv2.android.persistence.migrations.MIGRATION_3_4 import bou.amine.apps.readerforselfossv2.android.themes.AppColors import bou.amine.apps.readerforselfossv2.android.utils.* import bou.amine.apps.readerforselfossv2.android.utils.customtabs.CustomTabActivityHelper import bou.amine.apps.readerforselfossv2.android.utils.glide.getBitmapInputStream import bou.amine.apps.readerforselfossv2.android.utils.glide.loadMaybeBasicAuth import bou.amine.apps.readerforselfossv2.repository.Repository -import bou.amine.apps.readerforselfossv2.rest.SelfossModel +import bou.amine.apps.readerforselfossv2.model.SelfossModel +import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded +import bou.amine.apps.readerforselfossv2.utils.getImages +import bou.amine.apps.readerforselfossv2.utils.getThumbnail import bou.amine.apps.readerforselfossv2.utils.isEmptyOrNullOrNullString import com.bumptech.glide.Glide import com.bumptech.glide.load.engine.DiskCacheStrategy @@ -71,7 +69,6 @@ class ArticleFragment : Fragment(), DIAware { private lateinit var allImages : ArrayList private lateinit var fab: FloatingActionButton private lateinit var appColors: AppColors - private lateinit var db: AppDatabase private lateinit var textAlignment: String private lateinit var config: Config private var _binding: FragmentArticleBinding? = null @@ -103,11 +100,6 @@ class ArticleFragment : Fragment(), DIAware { val pi: ParecelableItem = requireArguments().getParcelable(ARG_ITEMS)!! item = pi.toModel() - - db = Room.databaseBuilder( - requireContext(), - AppDatabase::class.java, "selfoss-database" - ).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).addMigrations(MIGRATION_3_4).build() } override fun onCreateView( @@ -120,7 +112,7 @@ class ArticleFragment : Fragment(), DIAware { url = item.getLinkDecoded() contentText = item.content - contentTitle = item.getTitleDecoded() + contentTitle = item.title.getHtmlDecoded() contentImage = item.getThumbnail(repository.baseUrl) contentSource = item.sourceAndDateText(repository.dateUtils) allImages = item.getImages() @@ -134,7 +126,6 @@ class ArticleFragment : Fragment(), DIAware { typeface = try { ResourcesCompat.getFont(requireContext(), resId)!! } catch (e: java.lang.Exception) { - // ACRA.getErrorReporter().maybeHandleSilentException(Throwable("Font loading issue: ${e.message}"), requireContext()) // Just to be sure null } diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/model/AndroidIModelUtils.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/model/AndroidIModelUtils.kt index bff553f..85c9e73 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/model/AndroidIModelUtils.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/model/AndroidIModelUtils.kt @@ -1,43 +1,12 @@ package bou.amine.apps.readerforselfossv2.android.model import android.content.Context -import android.net.Uri -import android.text.Html import android.webkit.URLUtil -import bou.amine.apps.readerforselfossv2.rest.SelfossModel +import bou.amine.apps.readerforselfossv2.model.SelfossModel +import bou.amine.apps.readerforselfossv2.utils.getImages import com.bumptech.glide.Glide import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.request.RequestOptions -import org.jsoup.Jsoup -import java.util.* - - -/** - * Items extension methods - */ -fun SelfossModel.Item.getIcon(baseUrl: String): String { - return constructUrl(baseUrl, "favicons", icon) -} - -fun SelfossModel.Item.getThumbnail(baseUrl: String): String { - return constructUrl(baseUrl, "thumbnails", thumbnail) -} - -fun SelfossModel.Item.getImages() : ArrayList { - val allImages = ArrayList() - - for ( image in Jsoup.parse(content).getElementsByTag("img")) { - val url = image.attr("src") - if (url.lowercase(Locale.US).contains(".jpg") || - url.lowercase(Locale.US).contains(".jpeg") || - url.lowercase(Locale.US).contains(".png") || - url.lowercase(Locale.US).contains(".webp")) - { - allImages.add(url) - } - } - return allImages -} fun SelfossModel.Item.preloadImages(context: Context) : Boolean { val imageUrls = this.getImages() @@ -60,66 +29,13 @@ fun SelfossModel.Item.preloadImages(context: Context) : Boolean { return true } -fun SelfossModel.Item.getTitleDecoded(): String { - return Html.fromHtml(title).toString() -} - -fun SelfossModel.Item.getSourceTitle(): String { - return Html.fromHtml(sourcetitle).toString() -} - -// TODO: maybe find a better way to handle these kind of urls -fun SelfossModel.Item.getLinkDecoded(): String { - var stringUrl: String - stringUrl = - if (link.startsWith("http://news.google.com/news/") || link.startsWith("https://news.google.com/news/")) { - if (link.contains("&url=")) { - link.substringAfter("&url=") - } else { - this.link.replace("&", "&") - } - } else { - this.link.replace("&", "&") +fun String.toTextDrawableString(): String { + val textDrawable = StringBuilder() + for (s in this.split(" ".toRegex()).filter { it.isNotEmpty() }.toTypedArray()) { + try { + textDrawable.append(s[0]) + } catch (e: StringIndexOutOfBoundsException) { } - - // handle :443 => https - if (stringUrl.contains(":443")) { - stringUrl = stringUrl.replace(":443", "").replace("http://", "https://") - } - - // handle url not starting with http - if (stringUrl.startsWith("//")) { - stringUrl = "http:$stringUrl" - } - - return stringUrl -} - - -/** - * Sources extension methods - */ - -fun SelfossModel.Source.getIcon(baseUrl: String): String { - return constructUrl(baseUrl, "favicons", icon) -} - -fun SelfossModel.Source.getTitleDecoded(): String { - return Html.fromHtml(title).toString() -} - - - -/** - * Common methods - */ -private fun constructUrl(baseUrl: String, path: String, file: String?): String { - return if (file == null || file == "null" || file.isEmpty()) { - "" - } else { - val baseUriBuilder = Uri.parse(baseUrl).buildUpon() - baseUriBuilder.appendPath(path).appendPath(file) - - baseUriBuilder.toString() } + return textDrawable.toString() } \ No newline at end of file diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/model/ParecelableItem.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/model/ParecelableItem.kt index b3faf46..2904286 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/model/ParecelableItem.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/model/ParecelableItem.kt @@ -1,10 +1,8 @@ package bou.amine.apps.readerforselfossv2.android.model -import android.os.Build import android.os.Parcel import android.os.Parcelable -import androidx.annotation.RequiresApi -import bou.amine.apps.readerforselfossv2.rest.SelfossModel +import bou.amine.apps.readerforselfossv2.model.SelfossModel import com.google.gson.annotations.SerializedName fun SelfossModel.Item.toParcelable() : ParecelableItem = diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/AndroidDeviceDatabase.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/AndroidDeviceDatabase.kt deleted file mode 100644 index 1b9ca40..0000000 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/AndroidDeviceDatabase.kt +++ /dev/null @@ -1,28 +0,0 @@ -package bou.amine.apps.readerforselfossv2.android.persistence - -import android.content.Context -import androidx.room.Room -import bou.amine.apps.readerforselfossv2.android.persistence.database.AppDatabase -import bou.amine.apps.readerforselfossv2.android.persistence.entities.AndroidItemEntity -import bou.amine.apps.readerforselfossv2.android.persistence.migrations.MIGRATION_1_2 -import bou.amine.apps.readerforselfossv2.android.persistence.migrations.MIGRATION_2_3 -import bou.amine.apps.readerforselfossv2.android.persistence.migrations.MIGRATION_3_4 -import bou.amine.apps.readerforselfossv2.dao.DeviceDatabase - -class AndroidDeviceDatabase(applicationContext: Context): DeviceDatabase { - var db: AppDatabase = Room.databaseBuilder( - applicationContext, - AppDatabase::class.java, "selfoss-database" - ).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).addMigrations(MIGRATION_3_4).build() - - - override suspend fun items(): List = db.itemsDao().items() - - override suspend fun insertAllItems(vararg items: AndroidItemEntity) = db.itemsDao().insertAllItems(*items) - - override suspend fun deleteAllItems() = db.itemsDao().deleteAllItems() - - override suspend fun delete(item: AndroidItemEntity) = db.itemsDao().delete(item) - - override suspend fun updateItem(item: AndroidItemEntity) = db.itemsDao().updateItem(item) -} \ No newline at end of file diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/AndroidDeviceDatabaseService.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/AndroidDeviceDatabaseService.kt deleted file mode 100644 index 1b4fffa..0000000 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/AndroidDeviceDatabaseService.kt +++ /dev/null @@ -1,40 +0,0 @@ -package bou.amine.apps.readerforselfossv2.android.persistence - -import bou.amine.apps.readerforselfossv2.android.persistence.entities.AndroidItemEntity -import bou.amine.apps.readerforselfossv2.android.utils.persistence.toEntity -import bou.amine.apps.readerforselfossv2.rest.SelfossModel -import bou.amine.apps.readerforselfossv2.service.DeviceDataBaseService -import bou.amine.apps.readerforselfossv2.service.SearchService - -class AndroidDeviceDatabaseService(db: AndroidDeviceDatabase, searchService: SearchService) : - DeviceDataBaseService(db, searchService) { - override suspend fun updateDatabase() { - if (itemsCaching) { - if (items.isEmpty()) { - getFromDB() - } - db.deleteAllItems() - db.insertAllItems(*(items.map { it.toEntity() }).toTypedArray()) - } - } - - override suspend fun clearDBItems() { - db.deleteAllItems() - } - - override fun appendNewItems(newItems: List) { - var oldItems = items - if (oldItems != newItems) { - oldItems = oldItems.filter { item -> newItems.find { it.id == item.id } == null } as ArrayList - oldItems.addAll(newItems) - items = oldItems - - sortItems() - getFocusedItems() - } - } - - override fun getFromDB() { - TODO("Not yet implemented") - } -} \ No newline at end of file diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/dao/ActionsDao.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/dao/ActionsDao.kt deleted file mode 100644 index f834c22..0000000 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/dao/ActionsDao.kt +++ /dev/null @@ -1,23 +0,0 @@ -package bou.amine.apps.readerforselfossv2.android.persistence.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import bou.amine.apps.readerforselfossv2.android.persistence.entities.ActionEntity - -@Dao -interface ActionsDao { - @Query("SELECT * FROM actions order by id asc") - suspend fun actions(): List - - @Insert(onConflict = OnConflictStrategy.REPLACE) - fun insertAllActions(vararg actions: ActionEntity) - - @Query("DELETE FROM actions WHERE articleid = :article_id AND read = 1") - fun deleteReadActionForArticle(article_id: String) - - @Delete - fun delete(action: ActionEntity) -} \ No newline at end of file diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/dao/DrawerDataDao.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/dao/DrawerDataDao.kt deleted file mode 100644 index 7b8c98c..0000000 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/dao/DrawerDataDao.kt +++ /dev/null @@ -1,36 +0,0 @@ -package bou.amine.apps.readerforselfossv2.android.persistence.dao - -import androidx.room.Delete -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import bou.amine.apps.readerforselfossv2.android.persistence.entities.SourceEntity -import bou.amine.apps.readerforselfossv2.android.persistence.entities.TagEntity - -@Dao -interface DrawerDataDao { - @Query("SELECT * FROM tags") - fun tags(): List - - @Query("SELECT * FROM sources") - fun sources(): List - - @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) -} \ No newline at end of file diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/dao/ItemsDao.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/dao/ItemsDao.kt deleted file mode 100644 index d12ec74..0000000 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/dao/ItemsDao.kt +++ /dev/null @@ -1,29 +0,0 @@ -package bou.amine.apps.readerforselfossv2.android.persistence.dao - -import androidx.room.Dao -import androidx.room.Delete -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import bou.amine.apps.readerforselfossv2.android.persistence.entities.AndroidItemEntity -import androidx.room.Update - - - -@Dao -interface ItemsDao { - @Query("SELECT * FROM items order by id desc") - suspend fun items(): List - - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertAllItems(vararg items: AndroidItemEntity) - - @Query("DELETE FROM items") - suspend fun deleteAllItems() - - @Delete - suspend fun delete(item: AndroidItemEntity) - - @Update - suspend fun updateItem(item: AndroidItemEntity) -} \ No newline at end of file diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/database/AppDatabase.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/database/AppDatabase.kt deleted file mode 100644 index b9c0bdc..0000000 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/database/AppDatabase.kt +++ /dev/null @@ -1,20 +0,0 @@ -package bou.amine.apps.readerforselfossv2.android.persistence.database - -import androidx.room.RoomDatabase -import androidx.room.Database -import bou.amine.apps.readerforselfossv2.android.persistence.dao.ActionsDao -import bou.amine.apps.readerforselfossv2.android.persistence.dao.DrawerDataDao -import bou.amine.apps.readerforselfossv2.android.persistence.dao.ItemsDao -import bou.amine.apps.readerforselfossv2.android.persistence.entities.ActionEntity -import bou.amine.apps.readerforselfossv2.android.persistence.entities.AndroidItemEntity -import bou.amine.apps.readerforselfossv2.android.persistence.entities.SourceEntity -import bou.amine.apps.readerforselfossv2.android.persistence.entities.TagEntity - -@Database(entities = [TagEntity::class, SourceEntity::class, AndroidItemEntity::class, ActionEntity::class], version = 4) -abstract class AppDatabase : RoomDatabase() { - abstract fun drawerDataDao(): DrawerDataDao - - abstract fun itemsDao(): ItemsDao - - abstract fun actionsDao(): ActionsDao -} \ No newline at end of file diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/entities/ActionEntity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/entities/ActionEntity.kt deleted file mode 100644 index 2a15e82..0000000 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/entities/ActionEntity.kt +++ /dev/null @@ -1,22 +0,0 @@ -package bou.amine.apps.readerforselfossv2.android.persistence.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey - -@Entity(tableName = "actions") -data class ActionEntity( - @ColumnInfo(name = "articleid") - val articleId: String, - @ColumnInfo(name = "read") - val read: Boolean, - @ColumnInfo(name = "unread") - val unread: Boolean, - @ColumnInfo(name = "starred") - var starred: Boolean, - @ColumnInfo(name = "unstarred") - var unstarred: Boolean -) { - @PrimaryKey(autoGenerate = true) - var id: Int = 0 -} \ No newline at end of file diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/entities/AndroidItemEntity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/entities/AndroidItemEntity.kt deleted file mode 100644 index 4888bdc..0000000 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/entities/AndroidItemEntity.kt +++ /dev/null @@ -1,33 +0,0 @@ -package bou.amine.apps.readerforselfossv2.android.persistence.entities - -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey -import bou.amine.apps.readerforselfossv2.rest.SelfossModel - -@Entity(tableName = "items") -data class AndroidItemEntity( - @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 -) \ No newline at end of file diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/entities/DrawerDataEntity.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/entities/DrawerDataEntity.kt deleted file mode 100644 index cca0542..0000000 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/entities/DrawerDataEntity.kt +++ /dev/null @@ -1,33 +0,0 @@ -package bou.amine.apps.readerforselfossv2.android.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 -) \ No newline at end of file diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/migrations/migrations.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/migrations/migrations.kt deleted file mode 100644 index 1636fd5..0000000 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/persistence/migrations/migrations.kt +++ /dev/null @@ -1,34 +0,0 @@ -package bou.amine.apps.readerforselfossv2.android.persistence.migrations - -import androidx.sqlite.db.SupportSQLiteDatabase -import androidx.room.migration.Migration - -val MIGRATION_1_2: Migration = object : Migration(1, 2) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("CREATE TABLE IF NOT EXISTS `items` (`id` TEXT NOT NULL, `datetime` TEXT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `unread` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `thumbnail` TEXT NOT NULL, `icon` TEXT NOT NULL, `link` TEXT NOT NULL, `sourcetitle` TEXT NOT NULL, `tags` TEXT NOT NULL, PRIMARY KEY(`id`))") - } -} - -val MIGRATION_2_3: Migration = object : Migration(2, 3) { - override fun migrate(database: SupportSQLiteDatabase) { - database.execSQL("CREATE TABLE IF NOT EXISTS `actions` (`id` INTEGER NOT NULL, `articleid` TEXT NOT NULL, `read` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `unstarred` INTEGER NOT NULL, PRIMARY KEY(`id`))") - } -} - -val MIGRATION_3_4: Migration = object : Migration(3, 4) { - override fun migrate(database: SupportSQLiteDatabase) { - // @see https://stackoverflow.com/questions/57392015/how-to-migrate-not-null-table-column-into-null-in-android-room-database - // Create the new table - database.execSQL("CREATE TABLE IF NOT EXISTS `itemstmp` (`id` TEXT NOT NULL, `datetime` TEXT NOT NULL, `title` TEXT NOT NULL, `content` TEXT NOT NULL, `unread` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `thumbnail` TEXT, `icon` TEXT, `link` TEXT NOT NULL, `sourcetitle` TEXT NOT NULL, `tags` TEXT NOT NULL, PRIMARY KEY(`id`))") - - // Copy the data - database.execSQL( - "INSERT INTO itemstmp (`id`, `datetime`, `title`, `content`, `unread`, `starred`, `thumbnail`, `icon`, `link`, `sourcetitle`, `tags`) SELECT `id`, `datetime`, `title`, `content`, `unread`, `starred`, `thumbnail`, `icon`, `link`, `sourcetitle`, `tags` FROM items") - - // Remove the old table - database.execSQL("DROP TABLE items") - - // Change the table name to the correct one - database.execSQL("ALTER TABLE itemstmp RENAME TO items") - } -} diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/ItemsUtils.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/ItemsUtils.kt deleted file mode 100644 index 8ae737e..0000000 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/ItemsUtils.kt +++ /dev/null @@ -1,29 +0,0 @@ -package bou.amine.apps.readerforselfossv2.android.utils - -import android.content.Context -import bou.amine.apps.readerforselfossv2.android.model.getSourceTitle -import bou.amine.apps.readerforselfossv2.rest.SelfossModel -import bou.amine.apps.readerforselfossv2.utils.DateUtils -import bou.amine.apps.readerforselfossv2.utils.parseRelativeDate - -fun String.toTextDrawableString(c: Context): String { - val textDrawable = StringBuilder() - for (s in this.split(" ".toRegex()).filter { it.isNotEmpty() }.toTypedArray()) { - try { - textDrawable.append(s[0]) - } catch (e: StringIndexOutOfBoundsException) { - } - } - return textDrawable.toString() -} - -fun SelfossModel.Item.sourceAndDateText(dateUtils: DateUtils): String { - val formattedDate = parseRelativeDate(dateUtils) - - return getSourceTitle() + formattedDate -} - -fun SelfossModel.Item.toggleStar(): SelfossModel.Item { - this.starred = !this.starred - return this -} \ No newline at end of file diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/LinksUtils.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/LinksUtils.kt index 7e0200a..1fc608a 100644 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/LinksUtils.kt +++ b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/LinksUtils.kt @@ -18,9 +18,8 @@ import android.widget.Toast import androidx.browser.customtabs.CustomTabsIntent import bou.amine.apps.readerforselfossv2.android.R import bou.amine.apps.readerforselfossv2.android.ReaderActivity -import bou.amine.apps.readerforselfossv2.android.model.getLinkDecoded import bou.amine.apps.readerforselfossv2.android.utils.customtabs.CustomTabActivityHelper -import bou.amine.apps.readerforselfossv2.rest.SelfossModel +import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.utils.toStringUriWithHttp import okhttp3.HttpUrl.Companion.toHttpUrlOrNull diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/SizeUtils.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/SizeUtils.kt deleted file mode 100644 index 1bf44e3..0000000 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/SizeUtils.kt +++ /dev/null @@ -1,9 +0,0 @@ -package bou.amine.apps.readerforselfossv2.android.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() diff --git a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/persistence/EntitiesUtils.kt b/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/persistence/EntitiesUtils.kt deleted file mode 100644 index d165c97..0000000 --- a/androidApp/src/main/java/bou/amine/apps/readerforselfossv2/android/utils/persistence/EntitiesUtils.kt +++ /dev/null @@ -1,72 +0,0 @@ -package bou.amine.apps.readerforselfossv2.android.utils.persistence - -import bou.amine.apps.readerforselfossv2.android.model.getSourceTitle -import bou.amine.apps.readerforselfossv2.android.model.getTitleDecoded -import bou.amine.apps.readerforselfossv2.android.persistence.entities.AndroidItemEntity -import bou.amine.apps.readerforselfossv2.android.persistence.entities.SourceEntity -import bou.amine.apps.readerforselfossv2.android.persistence.entities.TagEntity -import bou.amine.apps.readerforselfossv2.rest.SelfossModel - -fun TagEntity.toView(): SelfossModel.Tag = - SelfossModel.Tag( - this.tag, - this.color, - this.unread - ) - -fun SourceEntity.toView(): SelfossModel.Source = - SelfossModel.Source( - this.id.toInt(), - this.title, - this.tags.split(","), - this.spout, - this.error, - this.icon - ) - -fun SelfossModel.Source.toEntity(): SourceEntity = - SourceEntity( - this.id.toString(), - this.getTitleDecoded(), - this.tags.joinToString(","), - this.spout, - this.error, - this.icon.orEmpty() - ) - -fun SelfossModel.Tag.toEntity(): TagEntity = - TagEntity( - this.tag, - this.color, - this.unread - ) - -fun AndroidItemEntity.toView(): SelfossModel.Item = - SelfossModel.Item( - this.id.toInt(), - this.datetime, - this.title, - this.content, - this.unread, - this.starred, - this.thumbnail, - this.icon, - this.link, - this.sourcetitle, - this.tags.split(",") - ) - -fun SelfossModel.Item.toEntity(): AndroidItemEntity = - AndroidItemEntity( - this.id.toString(), - this.datetime, - this.getTitleDecoded(), - this.content, - this.unread, - this.starred, - this.thumbnail, - this.icon, - this.link, - this.getSourceTitle(), - this.tags.joinToString(",") - ) \ No newline at end of file diff --git a/androidApp/src/main/res/values-ca-rES/strings.xml b/androidApp/src/main/res/values-ca-rES/strings.xml index 876c3ad..494e902 100644 --- a/androidApp/src/main/res/values-ca-rES/strings.xml +++ b/androidApp/src/main/res/values-ca-rES/strings.xml @@ -168,4 +168,5 @@ The bottom bar will always be displayed The bottom bar can be shown through the floating button Remove source + Can\'t get spouts list because of a network issue. diff --git a/androidApp/src/main/res/values-de-rDE/strings.xml b/androidApp/src/main/res/values-de-rDE/strings.xml index f756edc..11dc5ff 100644 --- a/androidApp/src/main/res/values-de-rDE/strings.xml +++ b/androidApp/src/main/res/values-de-rDE/strings.xml @@ -168,4 +168,5 @@ The bottom bar will always be displayed The bottom bar can be shown through the floating button Remove source + Can\'t get spouts list because of a network issue. diff --git a/androidApp/src/main/res/values-es-rES/strings.xml b/androidApp/src/main/res/values-es-rES/strings.xml index d70e4e0..13d609e 100644 --- a/androidApp/src/main/res/values-es-rES/strings.xml +++ b/androidApp/src/main/res/values-es-rES/strings.xml @@ -168,4 +168,5 @@ The bottom bar will always be displayed The bottom bar can be shown through the floating button Remove source + Can\'t get spouts list because of a network issue. diff --git a/androidApp/src/main/res/values-fa-rIR/strings.xml b/androidApp/src/main/res/values-fa-rIR/strings.xml index ee845ee..89f34d9 100644 --- a/androidApp/src/main/res/values-fa-rIR/strings.xml +++ b/androidApp/src/main/res/values-fa-rIR/strings.xml @@ -168,4 +168,5 @@ The bottom bar will always be displayed The bottom bar can be shown through the floating button Remove source + Can\'t get spouts list because of a network issue. diff --git a/androidApp/src/main/res/values-fr-rFR/strings.xml b/androidApp/src/main/res/values-fr-rFR/strings.xml index 179ff39..1535812 100644 --- a/androidApp/src/main/res/values-fr-rFR/strings.xml +++ b/androidApp/src/main/res/values-fr-rFR/strings.xml @@ -168,4 +168,5 @@ La barre sera affichée La barre sera affichée grâce au bouton Supprimer la source + Can\'t get spouts list because of a network issue. diff --git a/androidApp/src/main/res/values-gl-rES/strings.xml b/androidApp/src/main/res/values-gl-rES/strings.xml index 5bcb627..e92fdde 100644 --- a/androidApp/src/main/res/values-gl-rES/strings.xml +++ b/androidApp/src/main/res/values-gl-rES/strings.xml @@ -168,4 +168,5 @@ A barra inferior mostrarase sempre A barra inferior pode mostrarse a través do botón flotante Eliminar fonte + Can\'t get spouts list because of a network issue. diff --git a/androidApp/src/main/res/values-in-rID/strings.xml b/androidApp/src/main/res/values-in-rID/strings.xml index 4f291bc..8b9716e 100644 --- a/androidApp/src/main/res/values-in-rID/strings.xml +++ b/androidApp/src/main/res/values-in-rID/strings.xml @@ -168,4 +168,5 @@ The bottom bar will always be displayed The bottom bar can be shown through the floating button Remove source + Can\'t get spouts list because of a network issue. diff --git a/androidApp/src/main/res/values-it-rIT/strings.xml b/androidApp/src/main/res/values-it-rIT/strings.xml index 6fc9bdb..36eaebe 100644 --- a/androidApp/src/main/res/values-it-rIT/strings.xml +++ b/androidApp/src/main/res/values-it-rIT/strings.xml @@ -168,4 +168,5 @@ The bottom bar will always be displayed The bottom bar can be shown through the floating button Remove source + Can\'t get spouts list because of a network issue. diff --git a/androidApp/src/main/res/values-ko-rKR/strings.xml b/androidApp/src/main/res/values-ko-rKR/strings.xml index ed31e83..dfc6eac 100644 --- a/androidApp/src/main/res/values-ko-rKR/strings.xml +++ b/androidApp/src/main/res/values-ko-rKR/strings.xml @@ -168,4 +168,5 @@ The bottom bar will always be displayed The bottom bar can be shown through the floating button Remove source + Can\'t get spouts list because of a network issue. diff --git a/androidApp/src/main/res/values-nl-rNL/strings.xml b/androidApp/src/main/res/values-nl-rNL/strings.xml index e8aeeb0..f879ee5 100644 --- a/androidApp/src/main/res/values-nl-rNL/strings.xml +++ b/androidApp/src/main/res/values-nl-rNL/strings.xml @@ -168,4 +168,5 @@ The bottom bar will always be displayed The bottom bar can be shown through the floating button Remove source + Can\'t get spouts list because of a network issue. diff --git a/androidApp/src/main/res/values-pt-rBR/strings.xml b/androidApp/src/main/res/values-pt-rBR/strings.xml index a1d9183..0068b51 100644 --- a/androidApp/src/main/res/values-pt-rBR/strings.xml +++ b/androidApp/src/main/res/values-pt-rBR/strings.xml @@ -168,4 +168,5 @@ The bottom bar will always be displayed The bottom bar can be shown through the floating button Remove source + Can\'t get spouts list because of a network issue. diff --git a/androidApp/src/main/res/values-pt-rPT/strings.xml b/androidApp/src/main/res/values-pt-rPT/strings.xml index 72fbc2c..ec87e51 100644 --- a/androidApp/src/main/res/values-pt-rPT/strings.xml +++ b/androidApp/src/main/res/values-pt-rPT/strings.xml @@ -168,4 +168,5 @@ The bottom bar will always be displayed The bottom bar can be shown through the floating button Remove source + Can\'t get spouts list because of a network issue. diff --git a/androidApp/src/main/res/values-si-rLK/strings.xml b/androidApp/src/main/res/values-si-rLK/strings.xml index 5fb844b..e2dd876 100644 --- a/androidApp/src/main/res/values-si-rLK/strings.xml +++ b/androidApp/src/main/res/values-si-rLK/strings.xml @@ -168,4 +168,5 @@ The bottom bar will always be displayed The bottom bar can be shown through the floating button Remove source + Can\'t get spouts list because of a network issue. diff --git a/androidApp/src/main/res/values-tr-rTR/strings.xml b/androidApp/src/main/res/values-tr-rTR/strings.xml index 7ddd253..c8ea0b6 100644 --- a/androidApp/src/main/res/values-tr-rTR/strings.xml +++ b/androidApp/src/main/res/values-tr-rTR/strings.xml @@ -168,4 +168,5 @@ The bottom bar will always be displayed The bottom bar can be shown through the floating button Remove source + Can\'t get spouts list because of a network issue. diff --git a/androidApp/src/main/res/values-zh-rCN/strings.xml b/androidApp/src/main/res/values-zh-rCN/strings.xml index b64ffe3..f53ae19 100644 --- a/androidApp/src/main/res/values-zh-rCN/strings.xml +++ b/androidApp/src/main/res/values-zh-rCN/strings.xml @@ -168,4 +168,5 @@ 底部栏将始终显示 底部栏可以通过浮动按钮显示 删除源 + Can\'t get spouts list because of a network issue. diff --git a/androidApp/src/main/res/values-zh-rTW/strings.xml b/androidApp/src/main/res/values-zh-rTW/strings.xml index 00d788e..a9782f3 100644 --- a/androidApp/src/main/res/values-zh-rTW/strings.xml +++ b/androidApp/src/main/res/values-zh-rTW/strings.xml @@ -168,4 +168,5 @@ The bottom bar will always be displayed The bottom bar can be shown through the floating button Remove source + Can\'t get spouts list because of a network issue. diff --git a/androidApp/src/main/res/values/strings.xml b/androidApp/src/main/res/values/strings.xml index a83ab53..be2052f 100644 --- a/androidApp/src/main/res/values/strings.xml +++ b/androidApp/src/main/res/values/strings.xml @@ -39,7 +39,8 @@ "Log in to add sources." "Can't get sources list." "Can't create source." - "Can't get spouts list." + "Can't get spouts list because of a network issue." + "Can't get spouts list. There may ben an api issue." "The form is not complete" "Links" "Issue Tracker" diff --git a/build.gradle.kts b/build.gradle.kts index 209e858..8be2b47 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -6,10 +6,13 @@ buildscript { } dependencies { classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10") - classpath("com.android.tools.build:gradle:7.2.1") + classpath("com.android.tools.build:gradle:7.2.2") // sonarquve classpath("org.sonarsource.scanner.gradle:sonarqube-gradle-plugin:3.4.0.2513") + + // SqlDelight + classpath("com.squareup.sqldelight:gradle-plugin:1.5.3") } } diff --git a/shared/build.gradle.kts b/shared/build.gradle.kts index f8242cd..725d301 100644 --- a/shared/build.gradle.kts +++ b/shared/build.gradle.kts @@ -1,6 +1,14 @@ +object SqlDelight { + const val runtime = "com.squareup.sqldelight:runtime:1.5.3" + const val android = "com.squareup.sqldelight:android-driver:1.5.3" + const val native = "com.squareup.sqldelight:native-driver:1.5.3" + +} + plugins { kotlin("multiplatform") id("com.android.library") + id("com.squareup.sqldelight") kotlin("plugin.serialization") version "1.4.10" } @@ -39,6 +47,9 @@ kotlin { // Network information implementation("com.github.ln-12:multiplatform-connectivity-status:1.3.0") + + // Sql + implementation(SqlDelight.runtime) } } val commonTest by getting { @@ -50,6 +61,9 @@ kotlin { val androidMain by getting { dependencies { implementation("io.ktor:ktor-client-android:2.0.1") + + // Sql + implementation(SqlDelight.android) } } val androidTest by getting { @@ -66,6 +80,11 @@ kotlin { iosX64Main.dependsOn(this) iosArm64Main.dependsOn(this) //iosSimulatorArm64Main.dependsOn(this) + + // Sql + dependencies { + implementation(SqlDelight.native) + } } val iosX64Test by getting val iosArm64Test by getting @@ -93,4 +112,12 @@ android { sourceCompatibility = JavaVersion.VERSION_1_8 targetCompatibility = JavaVersion.VERSION_1_8 } -} \ No newline at end of file +} + +sqldelight { + database("ReaderForSelfossDB") { + packageName = "bou.amine.apps.readerforselfossv2.dao" + sourceFolders = listOf("sqldelight") + } +} + diff --git a/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/dao/DeviceDatabase.kt b/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/dao/DeviceDatabase.kt new file mode 100644 index 0000000..f5c98af --- /dev/null +++ b/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/dao/DeviceDatabase.kt @@ -0,0 +1,10 @@ +package bou.amine.apps.readerforselfossv2.dao +import android.content.Context +import com.squareup.sqldelight.android.AndroidSqliteDriver +import com.squareup.sqldelight.db.SqlDriver + +actual class DriverFactory(private val context: Context) { + actual fun createDriver(): SqlDriver { + return AndroidSqliteDriver(ReaderForSelfossDB.Schema, context, "ReaderForSelfossV2-android.db") + } +} \ No newline at end of file diff --git a/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt b/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt index e5fe1d2..064ff81 100644 --- a/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt +++ b/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt @@ -1,6 +1,5 @@ package bou.amine.apps.readerforselfossv2.utils -import android.annotation.SuppressLint import android.text.format.DateUtils import java.time.Instant import java.time.LocalDateTime diff --git a/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt b/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt new file mode 100644 index 0000000..b2c8672 --- /dev/null +++ b/shared/src/androidMain/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt @@ -0,0 +1,51 @@ +package bou.amine.apps.readerforselfossv2.utils + +import android.net.Uri +import android.text.Html +import bou.amine.apps.readerforselfossv2.model.SelfossModel +import org.jsoup.Jsoup +import java.util.* +import kotlin.collections.ArrayList + +actual fun String.getHtmlDecoded(): String { + return Html.fromHtml(this).toString() +} + +actual fun SelfossModel.Item.getIcon(baseUrl: String): String { + return constructUrl(baseUrl, "favicons", icon) +} + +actual fun SelfossModel.Item.getThumbnail(baseUrl: String): String { + return constructUrl(baseUrl, "thumbnails", thumbnail) +} + +actual fun SelfossModel.Item.getImages(): ArrayList { + val allImages = ArrayList() + + for ( image in Jsoup.parse(content).getElementsByTag("img")) { + val url = image.attr("src") + if (url.lowercase(Locale.US).contains(".jpg") || + url.lowercase(Locale.US).contains(".jpeg") || + url.lowercase(Locale.US).contains(".png") || + url.lowercase(Locale.US).contains(".webp")) + { + allImages.add(url) + } + } + return allImages +} + +actual fun SelfossModel.Source.getIcon(baseUrl: String): String { + return constructUrl(baseUrl, "favicons", icon) +} + +actual fun constructUrl(baseUrl: String, path: String, file: String?): String { + return if (file == null || file == "null" || file.isEmpty()) { + "" + } else { + val baseUriBuilder = Uri.parse(baseUrl).buildUpon() + baseUriBuilder.appendPath(path).appendPath(file) + + baseUriBuilder.toString() + } +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/dao/DeviceDatabase.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/dao/DeviceDatabase.kt index fd81b6c..55d046f 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/dao/DeviceDatabase.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/dao/DeviceDatabase.kt @@ -1,9 +1,7 @@ package bou.amine.apps.readerforselfossv2.dao -interface DeviceDatabase { - suspend fun items(): List - suspend fun insertAllItems(vararg items: ItemEntity) - suspend fun deleteAllItems() - suspend fun delete(item: ItemEntity) - suspend fun updateItem(item: ItemEntity) -} +import com.squareup.sqldelight.db.SqlDriver + +expect class DriverFactory { + fun createDriver(): SqlDriver +} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/NetworkUnavailableException.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/NetworkUnavailableException.kt new file mode 100644 index 0000000..a51fb77 --- /dev/null +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/NetworkUnavailableException.kt @@ -0,0 +1,3 @@ +package bou.amine.apps.readerforselfossv2.model + +class NetworkUnavailableException : Exception() \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossModel.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/SelfossModel.kt similarity index 51% rename from shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossModel.kt rename to shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/SelfossModel.kt index b4aa311..15decfb 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossModel.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/model/SelfossModel.kt @@ -1,5 +1,7 @@ -package bou.amine.apps.readerforselfossv2.rest +package bou.amine.apps.readerforselfossv2.model +import bou.amine.apps.readerforselfossv2.utils.DateUtils +import bou.amine.apps.readerforselfossv2.utils.getHtmlDecoded import kotlinx.serialization.Serializable class SelfossModel { @@ -9,11 +11,7 @@ class SelfossModel { val tag: String, val color: String, val unread: Int - ) { - fun getTitleDecoded(): String { - return tag // TODO Html.fromHtml(tag).toString() - } - } + ) @Serializable class SuccessResponse(val success: Boolean) { @@ -71,5 +69,40 @@ class SelfossModel { val link: String, val sourcetitle: String, val tags: List - ) + ) { + // 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/")) { + if (link.contains("&url=")) { + link.substringAfter("&url=") + } else { + this.link.replace("&", "&") + } + } else { + this.link.replace("&", "&") + } + + // handle :443 => https + if (stringUrl.contains(":443")) { + stringUrl = stringUrl.replace(":443", "").replace("http://", "https://") + } + + // handle url not starting with http + if (stringUrl.startsWith("//")) { + stringUrl = "http:$stringUrl" + } + + return stringUrl + } + + fun sourceAndDateText(dateUtils: DateUtils): String = + this.sourcetitle.getHtmlDecoded() + dateUtils.parseRelativeDate(this.datetime) + + fun toggleStar(): Item { + this.starred = !this.starred + return this + } + } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt index 287ef1c..e4e7d90 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/repository/RepositoryImpl.kt @@ -1,10 +1,11 @@ package bou.amine.apps.readerforselfossv2.repository +import bou.amine.apps.readerforselfossv2.dao.* +import bou.amine.apps.readerforselfossv2.model.NetworkUnavailableException import bou.amine.apps.readerforselfossv2.rest.SelfossApi -import bou.amine.apps.readerforselfossv2.rest.SelfossModel +import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.service.ApiDetailsService -import bou.amine.apps.readerforselfossv2.utils.DateUtils -import bou.amine.apps.readerforselfossv2.utils.ItemType +import bou.amine.apps.readerforselfossv2.utils.* import com.github.ln_12.library.ConnectivityStatus import com.russhwolf.settings.Settings import io.github.aakira.napier.Napier @@ -12,7 +13,7 @@ import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -class Repository(private val api: SelfossApi, private val apiDetails: ApiDetailsService, val connectivityStatus: ConnectivityStatus) { +class Repository(private val api: SelfossApi, private val apiDetails: ApiDetailsService, private val connectivityStatus: ConnectivityStatus, private val db: ReaderForSelfossDB) { val settings = Settings() var items = ArrayList() @@ -62,11 +63,18 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails null ) } else { - // TODO: Get items from the database + if (itemsCaching) { + fetchedItems = getDBItems().filter { + displayedItems == ItemType.ALL || + (it.unread && displayedItems == ItemType.UNREAD) || + (it.starred && displayedItems == ItemType.STARRED) + }.map { it.toView() } + } } if (fetchedItems != null) { items = ArrayList(fetchedItems) + sortItems() } return items } @@ -84,17 +92,16 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails searchFilter, null ) - } else { - // TODO: Get items from the database - } + } // When using the db cache, we load everything the first time, so there should be nothing more to load. if (fetchedItems != null) { - appendItems(fetchedItems) + items.addAll(fetchedItems) + sortItems() } return items } - suspend fun allItems(itemType: ItemType): List? { + suspend fun getMaxItemsForBackground(itemType: ItemType): List? { return if (isNetworkAvailable()) { api.getItems( itemType.type, @@ -106,21 +113,11 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails null ) } else { - // TODO: Provide an error message - null + emptyList() } } - private fun appendItems(fetchedItems: List) { - // TODO: Store in DB if enabled by user - val fetchedIDS = fetchedItems.map { it.id } - val tmpItems = ArrayList(items.filterNot { it.id in fetchedIDS }) - tmpItems.addAll(fetchedItems) - sortItems(tmpItems) - items = tmpItems - } - - private fun sortItems(items: ArrayList) { + private fun sortItems() { items.sortByDescending { dateUtils.parseDate(it.datetime) } } @@ -135,38 +132,37 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails success = true } } else { - // TODO: Compute badges from database + // TODO: do this differently, because it's not efficient + val dbItems = getDBItems() + badgeUnread = dbItems.filter { item -> item.unread }.size + badgeStarred = dbItems.filter { item -> item.starred }.size + badgeAll = items.size } return success } suspend fun getTags(): List? { - // TODO: Store in DB return if (isNetworkAvailable()) { api.tags() } else { - // TODO: Compute from database - null + getDBTags().map { it.toView() } } } suspend fun getSpouts(): Map? { - // TODO: Store in DB return if (isNetworkAvailable()) { api.spouts() } else { - // TODO: Compute from database - null + throw NetworkUnavailableException() } } suspend fun getSources(): ArrayList? { - // TODO: Store in DB + return if (isNetworkAvailable()) { api.sources() } else { - // TODO: Compute from database - null + ArrayList(getDBSources().map { it.toView() }) } } @@ -179,8 +175,14 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails return success } - suspend fun markAsReadById(id: Int): Boolean = - isNetworkAvailable() && api.markAsRead(id.toString())?.isSuccess == true + suspend fun markAsReadById(id: Int): Boolean { + return if (isNetworkAvailable()) { + api.markAsRead(id.toString())?.isSuccess == true + } else { + insertDBAction(id.toString(), read = true) + true + } + } suspend fun unmarkAsRead(item: SelfossModel.Item): Boolean { @@ -192,8 +194,14 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails return success } - suspend fun unmarkAsReadById(id: Int): Boolean = - isNetworkAvailable() && api.unmarkAsRead(id.toString())?.isSuccess == true + suspend fun unmarkAsReadById(id: Int): Boolean { + return if (isNetworkAvailable()) { + api.unmarkAsRead(id.toString())?.isSuccess == true + } else { + insertDBAction(id.toString(), unread = true) + true + } + } suspend fun starr(item: SelfossModel.Item): Boolean { val success = starrById(item.id) @@ -204,8 +212,14 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails return success } - suspend fun starrById(id: Int): Boolean = - isNetworkAvailable() && api.starr(id.toString())?.isSuccess == true + suspend fun starrById(id: Int): Boolean { + return if (isNetworkAvailable()) { + api.starr(id.toString())?.isSuccess == true + } else { + insertDBAction(id.toString(), starred = true) + true + } + } suspend fun unstarr(item: SelfossModel.Item): Boolean { val success = unstarrById(item.id) @@ -216,9 +230,14 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails return success } - suspend fun unstarrById(id: Int): Boolean = - isNetworkAvailable() && api.unstarr(id.toString())?.isSuccess == true - + suspend fun unstarrById(id: Int): Boolean { + return if (isNetworkAvailable()) { + api.unstarr(id.toString())?.isSuccess == true + } else { + insertDBAction(id.toString(), starred = true) + true + } + } suspend fun markAllAsRead(items: ArrayList): Boolean { var success = false @@ -233,35 +252,47 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails } private fun markAsReadLocally(item: SelfossModel.Item) { - // TODO: Mark also in the database if (item.unread) { item.unread = false badgeUnread -= 1 } + + CoroutineScope(Dispatchers.Main).launch { + updateDBItem(item) + } } private fun unmarkAsReadLocally(item: SelfossModel.Item) { - // TODO: Mark also in the database if (!item.unread) { item.unread = true badgeUnread += 1 } + + CoroutineScope(Dispatchers.Main).launch { + updateDBItem(item) + } } private fun starrLocally(item: SelfossModel.Item) { - // TODO: Mark also in the database if (!item.starred) { item.starred = true badgeStarred += 1 } + + CoroutineScope(Dispatchers.Main).launch { + updateDBItem(item) + } } private fun unstarrLocally(item: SelfossModel.Item) { - // TODO: Mark also in the database if (item.starred) { item.starred = false badgeStarred -= 1 } + + CoroutineScope(Dispatchers.Main).launch { + updateDBItem(item) + } } suspend fun createSource( @@ -287,7 +318,6 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails } suspend fun deleteSource(id: Int): Boolean { - // TODO: Store in DB var success = false if (isNetworkAvailable()) { val response = api.deleteSource(id) @@ -342,5 +372,61 @@ class Repository(private val api: SelfossApi, private val apiDetails: ApiDetails fun isNetworkAvailable() = isConnectionAvailable.value && !offlineOverride - // TODO: Handle offline actions + fun getDBActions(): List = + db.actionsQueries.actions().executeAsList() + + fun deleteDBAction(action: ACTION) = + db.actionsQueries.deleteAction(action.id) + + fun getDBTags(): List = db.tagsQueries.tags().executeAsList() + + fun getDBSources(): List = db.sourcesQueries.sources().executeAsList() + + fun resetDBTagsWithData(tagEntities: List) { + db.tagsQueries.deleteAllTags() + + db.tagsQueries.transaction { + tagEntities.forEach { tag -> + db.tagsQueries.insertTag(tag.toEntity()) + } + } + } + + fun resetDBSourcesWithData(sources: List) { + db.sourcesQueries.deleteAllSources() + + db.sourcesQueries.transaction { + sources.forEach { source -> + db.sourcesQueries.insertSource(source.toEntity()) + } + } + } + + private fun insertDBItems(items: List) { + db.itemsQueries.transaction { + items.forEach { item -> + db.itemsQueries.insertItem(item.toEntity()) + } + } + } + + private fun getDBItems(): List = db.itemsQueries.items().executeAsList() + + private fun insertDBAction(articleid: String, read: Boolean = false, unread: Boolean = false, starred: Boolean = false, unstarred: Boolean = false) = + db.actionsQueries.insertAction(articleid, read, unread, starred, unstarred) + + private fun updateDBItem(item: SelfossModel.Item) = + db.itemsQueries.updateItem(item.datetime, item.title.getHtmlDecoded(), item.content, item.unread, item.starred, item.thumbnail, item.icon, item.link, item.sourcetitle, item.tags.joinToString(","), item.id.toString()) + + + suspend fun tryToCacheItemsAndGetNewOnes(): List? { + try { + val newItems = getMaxItemsForBackground(ItemType.UNREAD) + val allItems = getMaxItemsForBackground(ItemType.ALL) + val starredItems = getMaxItemsForBackground(ItemType.STARRED) + insertDBItems(newItems.orEmpty() + allItems.orEmpty() + starredItems.orEmpty()) + return newItems + } catch (e: Throwable) {} + return emptyList() + } } \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt index 90ba822..0047453 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/rest/SelfossApi.kt @@ -1,5 +1,6 @@ package bou.amine.apps.readerforselfossv2.rest +import bou.amine.apps.readerforselfossv2.model.SelfossModel import bou.amine.apps.readerforselfossv2.service.ApiDetailsService import io.ktor.client.* import io.ktor.client.call.* diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/service/DeviceDataBaseService.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/service/DeviceDataBaseService.kt deleted file mode 100644 index f871a65..0000000 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/service/DeviceDataBaseService.kt +++ /dev/null @@ -1,34 +0,0 @@ -package bou.amine.apps.readerforselfossv2.service - -import bou.amine.apps.readerforselfossv2.dao.DeviceDatabase -import bou.amine.apps.readerforselfossv2.utils.parseDate -import bou.amine.apps.readerforselfossv2.rest.SelfossModel - -abstract class DeviceDataBaseService(val db: DeviceDatabase, private val searchService: SearchService) { - var itemsCaching = false - var items: ArrayList = arrayListOf() - get() { - return ArrayList(field) - } - set(value) { - field = ArrayList(value) - } - - abstract suspend fun updateDatabase() - abstract suspend fun clearDBItems() - abstract fun appendNewItems(items: List) - abstract fun getFromDB() - - fun sortItems() { - val tmpItems = ArrayList(items.sortedByDescending { it.parseDate(searchService.dateUtils) }) - items = tmpItems - } - - // This filtered items from items val. Do not use - fun getFocusedItems() {} - fun computeBadges() { - searchService.badgeUnread = items.filter { item -> item.unread }.size - searchService.badgeStarred = items.filter { item -> item.starred }.size - searchService.badgeAll = items.size - } -} \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt index 16b95c2..95518ba 100644 --- a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt @@ -1,14 +1,11 @@ package bou.amine.apps.readerforselfossv2.utils -import bou.amine.apps.readerforselfossv2.rest.SelfossModel +import bou.amine.apps.readerforselfossv2.model.SelfossModel fun SelfossModel.Item.parseDate(dateUtils: DateUtils): Long = dateUtils.parseDate(this.datetime) -fun SelfossModel.Item.parseRelativeDate(dateUtils: DateUtils): String = - dateUtils.parseRelativeDate(this.datetime) - expect class DateUtils(apiMajorVersion: Int) { fun parseDate(dateString: String): Long diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/EntityUtils.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/EntityUtils.kt new file mode 100644 index 0000000..9407a79 --- /dev/null +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/EntityUtils.kt @@ -0,0 +1,70 @@ +package bou.amine.apps.readerforselfossv2.utils + +import bou.amine.apps.readerforselfossv2.dao.ITEM +import bou.amine.apps.readerforselfossv2.dao.SOURCE +import bou.amine.apps.readerforselfossv2.dao.TAG +import bou.amine.apps.readerforselfossv2.model.SelfossModel + +fun TAG.toView(): SelfossModel.Tag = + SelfossModel.Tag( + this.name, + this.color, + this.unread.toInt() + ) + +fun SOURCE.toView(): SelfossModel.Source = + SelfossModel.Source( + this.id.toInt(), + this.title, + this.tags.split(","), + this.spout, + this.error, + this.icon + ) + +fun SelfossModel.Source.toEntity(): SOURCE = + SOURCE( + this.id.toString(), + this.title.getHtmlDecoded(), + this.tags.joinToString(","), + this.spout, + this.error, + this.icon.orEmpty() + ) + +fun SelfossModel.Tag.toEntity(): TAG = + TAG( + this.tag, + this.color, + this.unread.toLong() + ) + +fun ITEM.toView(): SelfossModel.Item = + SelfossModel.Item( + this.id.toInt(), + this.datetime, + this.title, + this.content, + this.unread, + this.starred, + this.thumbnail, + this.icon, + this.link, + this.sourcetitle, + this.tags.split(",") + ) + +fun SelfossModel.Item.toEntity(): ITEM = + ITEM( + this.id.toString(), + this.datetime, + this.title.getHtmlDecoded(), + this.content, + this.unread, + this.starred, + this.thumbnail, + this.icon, + this.link, + this.title.getHtmlDecoded(), + this.tags.joinToString(",") + ) \ No newline at end of file diff --git a/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt new file mode 100644 index 0000000..2751b66 --- /dev/null +++ b/shared/src/commonMain/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt @@ -0,0 +1,15 @@ +package bou.amine.apps.readerforselfossv2.utils + +import bou.amine.apps.readerforselfossv2.model.SelfossModel + +expect fun String.getHtmlDecoded(): String + +expect fun SelfossModel.Item.getIcon(baseUrl: String): String + +expect fun SelfossModel.Item.getThumbnail(baseUrl: String): String + +expect fun SelfossModel.Item.getImages(): ArrayList + +expect fun SelfossModel.Source.getIcon(baseUrl: String): String + +expect fun constructUrl(baseUrl: String, path: String, file: String?): String \ No newline at end of file diff --git a/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Actions.sq b/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Actions.sq new file mode 100644 index 0000000..8831df4 --- /dev/null +++ b/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Actions.sq @@ -0,0 +1,18 @@ +CREATE TABLE `ACTION` ( + `id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + `articleid` TEXT NOT NULL, + `read` INTEGER AS Boolean DEFAULT 0 NOT NULL, + `unread` INTEGER AS Boolean DEFAULT 0 NOT NULL, + `starred` INTEGER AS Boolean DEFAULT 0 NOT NULL, + `unstarred` INTEGER AS Boolean DEFAULT 0 NOT NULL +); + +actions: +SELECT * +FROM `ACTION`; + +insertAction: +INSERT OR REPLACE INTO `ACTION` (`articleid`, `read`, `unread`, `starred`, `unstarred`) VALUES (?, ?, ?, ?, ?); + +deleteAction: +DELETE FROM `ACTION` WHERE id = ?; \ No newline at end of file diff --git a/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Items.sq b/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Items.sq new file mode 100644 index 0000000..24fa055 --- /dev/null +++ b/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Items.sq @@ -0,0 +1,30 @@ +CREATE TABLE ITEM ( + `id` TEXT NOT NULL, + `datetime` TEXT NOT NULL, + `title` TEXT NOT NULL, + `content` TEXT NOT NULL, + `unread` INTEGER AS Boolean DEFAULT 0 NOT NULL, + `starred` INTEGER AS Boolean DEFAULT 0 NOT NULL, + `thumbnail` TEXT, + `icon` TEXT, + `link` TEXT NOT NULL, + `sourcetitle` TEXT NOT NULL, + `tags` TEXT NOT NULL, + PRIMARY KEY(`id`) +); + +CREATE INDEX item_title ON ITEM(`title`); +CREATE INDEX item_source ON ITEM(`sourcetitle`); + + +items: +SELECT * FROM ITEM ORDER BY `id` DESC; + +insertItem: +INSERT OR REPLACE INTO ITEM VALUES ?; + +deleteItem: +DELETE FROM ITEM WHERE `id` = ?; + +updateItem: +UPDATE ITEM SET `datetime` = ?, `title` = ?, `content` = ?, `unread` = ?, `starred` = ?, `thumbnail` = ?, `icon` = ?, `link` = ?, `sourcetitle` = ?, `tags` = ? WHERE `id` = ?; \ No newline at end of file diff --git a/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Sources.sq b/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Sources.sq new file mode 100644 index 0000000..7bd882b --- /dev/null +++ b/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Sources.sq @@ -0,0 +1,21 @@ +CREATE TABLE SOURCE ( + `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`) +); + +sources: +SELECT * FROM SOURCE; + +insertSource: +INSERT OR REPLACE INTO SOURCE VALUES ?; + +deleteAllSources: +DELETE FROM SOURCE; + +deleteSource: +DELETE FROM SOURCE WHERE `id` = ?; \ No newline at end of file diff --git a/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Tags.sq b/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Tags.sq new file mode 100644 index 0000000..b442fcd --- /dev/null +++ b/shared/src/commonMain/sqldelight/bou/amine/apps/readerforselfossv2/dao/Tags.sq @@ -0,0 +1,20 @@ +CREATE TABLE TAG ( + `name` TEXT NOT NULL, + `color` TEXT NOT NULL, + `unread` INTEGER NOT NULL, + PRIMARY KEY(`name`) +); + +CREATE INDEX tag_name ON TAG(`name`); + +tags: +SELECT * FROM TAG; + +insertTag: +INSERT OR REPLACE INTO TAG VALUES ?; + +deleteAllTags: +DELETE FROM TAG; + +deleteTag: +DELETE FROM TAG WHERE `name` = ? AND `color` = ?; \ No newline at end of file diff --git a/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/dao/DriverFactory.kt b/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/dao/DriverFactory.kt new file mode 100644 index 0000000..0357cc6 --- /dev/null +++ b/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/dao/DriverFactory.kt @@ -0,0 +1,10 @@ +package bou.amine.apps.readerforselfossv2.dao + +import com.squareup.sqldelight.db.SqlDriver +import com.squareup.sqldelight.drivers.native.NativeSqliteDriver + +actual class DriverFactory { + actual fun createDriver(): SqlDriver { + return NativeSqliteDriver(ReaderForSelfossDB.Schema, "ReaderForSelfossV2-IOS.db") + } +} \ No newline at end of file diff --git a/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt b/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt new file mode 100644 index 0000000..69a287b --- /dev/null +++ b/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt @@ -0,0 +1,12 @@ +package bou.amine.apps.readerforselfossv2.utils + +actual class DateUtils actual constructor(apiMajorVersion: Int) { + actual fun parseDate(dateString: String): Long { + TODO("Not yet implemented") + } + + actual fun parseRelativeDate(dateString: String): String { + TODO("Not yet implemented") + } + +} \ No newline at end of file diff --git a/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt b/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt new file mode 100644 index 0000000..9f66d23 --- /dev/null +++ b/shared/src/iosArm64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt @@ -0,0 +1,27 @@ +package bou.amine.apps.readerforselfossv2.utils + +import bou.amine.apps.readerforselfossv2.model.SelfossModel + +actual fun String.getHtmlDecoded(): String { + TODO("Not yet implemented") +} + +actual fun SelfossModel.Item.getIcon(baseUrl: String): String { + TODO("Not yet implemented") +} + +actual fun SelfossModel.Item.getThumbnail(baseUrl: String): String { + TODO("Not yet implemented") +} + +actual fun SelfossModel.Item.getImages(): ArrayList { + TODO("Not yet implemented") +} + +actual fun SelfossModel.Source.getIcon(baseUrl: String): String { + TODO("Not yet implemented") +} + +actual fun constructUrl(baseUrl: String, path: String, file: String?): String { + TODO("Not yet implemented") +} \ No newline at end of file diff --git a/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/dao/DriverFactory.kt b/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/dao/DriverFactory.kt new file mode 100644 index 0000000..0357cc6 --- /dev/null +++ b/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/dao/DriverFactory.kt @@ -0,0 +1,10 @@ +package bou.amine.apps.readerforselfossv2.dao + +import com.squareup.sqldelight.db.SqlDriver +import com.squareup.sqldelight.drivers.native.NativeSqliteDriver + +actual class DriverFactory { + actual fun createDriver(): SqlDriver { + return NativeSqliteDriver(ReaderForSelfossDB.Schema, "ReaderForSelfossV2-IOS.db") + } +} \ No newline at end of file diff --git a/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt b/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt new file mode 100644 index 0000000..69a287b --- /dev/null +++ b/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/DateUtils.kt @@ -0,0 +1,12 @@ +package bou.amine.apps.readerforselfossv2.utils + +actual class DateUtils actual constructor(apiMajorVersion: Int) { + actual fun parseDate(dateString: String): Long { + TODO("Not yet implemented") + } + + actual fun parseRelativeDate(dateString: String): String { + TODO("Not yet implemented") + } + +} \ No newline at end of file diff --git a/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt b/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt new file mode 100644 index 0000000..9f66d23 --- /dev/null +++ b/shared/src/iosX64Main/kotlin/bou/amine/apps/readerforselfossv2/utils/ModelConverters.kt @@ -0,0 +1,27 @@ +package bou.amine.apps.readerforselfossv2.utils + +import bou.amine.apps.readerforselfossv2.model.SelfossModel + +actual fun String.getHtmlDecoded(): String { + TODO("Not yet implemented") +} + +actual fun SelfossModel.Item.getIcon(baseUrl: String): String { + TODO("Not yet implemented") +} + +actual fun SelfossModel.Item.getThumbnail(baseUrl: String): String { + TODO("Not yet implemented") +} + +actual fun SelfossModel.Item.getImages(): ArrayList { + TODO("Not yet implemented") +} + +actual fun SelfossModel.Source.getIcon(baseUrl: String): String { + TODO("Not yet implemented") +} + +actual fun constructUrl(baseUrl: String, path: String, file: String?): String { + TODO("Not yet implemented") +} \ No newline at end of file