Compare commits
24 Commits
image
...
dateFormat
Author | SHA1 | Date | |
---|---|---|---|
1c57435f54 | |||
4bb20a75d7 | |||
1f20e19a97 | |||
9b372a45ce | |||
f4f8503037 | |||
5b70ae138e | |||
626c9e2797 | |||
05cd96afc0 | |||
c8faa8984f | |||
a025efbf3b | |||
e62e04e13b | |||
e6b5ea4e67 | |||
c3148c6744 | |||
193f538d29 | |||
7f45db0473 | |||
d89423b9ac | |||
25fd869c01 | |||
d1d956b77a | |||
41c14362a8 | |||
6fa8c901fc | |||
db124ab9de | |||
953940690d | |||
918661be2d | |||
7b8a5c9a56 |
18
CHANGELOG.md
@ -26,6 +26,22 @@
|
||||
|
||||
- Closing #38. Only doing api calls on network available.
|
||||
|
||||
- Closing #298 and #287. Issues with Listview rendering
|
||||
|
||||
- Closing #290. Fixing back button issue in Settings
|
||||
|
||||
- Closing #300. Fixing issues when displaying some special characters.
|
||||
|
||||
- Closing #310. Some feeds don't have icons nor thumbnails.
|
||||
|
||||
- Closing #178. Expending images on tap.
|
||||
|
||||
- Closing #323. Old issue with textview not having the right color.
|
||||
|
||||
- Closing #324. Svg images loading crashes the app.
|
||||
|
||||
- Closing #322. App crashed because of svg images.
|
||||
|
||||
**1.6.x**
|
||||
|
||||
- Handling hidden tags.
|
||||
@ -46,6 +62,8 @@
|
||||
|
||||
- Fixes #215, #208.
|
||||
|
||||
- Fixes #328.
|
||||
|
||||
**1.5.7.x**
|
||||
|
||||
- Added confirmation to the mark as read and update menues.
|
||||
|
@ -24,25 +24,26 @@ def versionNameFromGit() {
|
||||
return gitVersion()
|
||||
}
|
||||
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
apply plugin: 'com.android.application'
|
||||
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
apply plugin: 'kotlin-android-extensions'
|
||||
apply plugin: 'kotlin-kapt'
|
||||
|
||||
android {
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
compileSdkVersion 28
|
||||
buildToolsVersion '28.0.3'
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion '30.0.3'
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
defaultConfig {
|
||||
applicationId "apps.amine.bou.readerforselfoss"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 28
|
||||
targetSdkVersion 30
|
||||
versionCode versionCodeFromGit()
|
||||
versionName versionNameFromGit()
|
||||
|
||||
@ -85,23 +86,23 @@ android {
|
||||
|
||||
dependencies {
|
||||
// Testing
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0-beta01'
|
||||
androidTestImplementation 'androidx.test:runner:1.2.0-beta01'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0-alpha02'
|
||||
androidTestImplementation 'androidx.test:runner:1.3.1-alpha02'
|
||||
// Espresso-contrib for DatePicker, RecyclerView, Drawer actions, Accessibility checks, CountingIdlingResource
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.2.0-beta01'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-contrib:3.4.0-alpha02'
|
||||
// Espresso-intents for validation and stubbing of Intents
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.2.0-beta01'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.4.0-alpha02'
|
||||
implementation fileTree(include: ['*.jar'], dir: 'libs')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
// Android Support
|
||||
implementation "androidx.appcompat:appcompat:$androidx_version"
|
||||
implementation "com.google.android.material:material:1.1.0-alpha06"
|
||||
implementation "androidx.recyclerview:recyclerview:1.1.0-alpha05"
|
||||
implementation "androidx.appcompat:appcompat:1.3.0-alpha02"
|
||||
implementation 'com.google.android.material:material:1.3.0-beta01'
|
||||
implementation 'androidx.recyclerview:recyclerview:1.2.0-beta01'
|
||||
implementation "androidx.legacy:legacy-support-v4:$android_version"
|
||||
implementation "androidx.vectordrawable:vectordrawable:1.1.0-beta01"
|
||||
implementation "androidx.browser:browser:$android_version"
|
||||
implementation 'androidx.vectordrawable:vectordrawable:1.2.0-alpha02'
|
||||
implementation "androidx.browser:browser:1.3.0"
|
||||
implementation "androidx.cardview:cardview:$android_version"
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha5'
|
||||
implementation 'androidx.constraintlayout:constraintlayout:2.1.0-alpha2'
|
||||
implementation 'org.jsoup:jsoup:1.13.1'
|
||||
|
||||
//multidex
|
||||
@ -141,13 +142,13 @@ dependencies {
|
||||
//PhotoView
|
||||
implementation 'com.github.chrisbanes:PhotoView:2.0.0'
|
||||
|
||||
implementation 'androidx.core:core-ktx:1.1.0-beta01'
|
||||
implementation 'androidx.core:core-ktx:1.5.0-alpha05'
|
||||
|
||||
implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
|
||||
implementation "androidx.lifecycle:lifecycle-livedata:2.3.0-rc01"
|
||||
implementation "androidx.lifecycle:lifecycle-common-java8:2.3.0-rc01"
|
||||
|
||||
implementation "androidx.room:room-runtime:$room_version"
|
||||
kapt "androidx.room:room-compiler:$room_version"
|
||||
implementation "androidx.room:room-runtime:2.3.0-alpha04"
|
||||
kapt "androidx.room:room-compiler:2.3.0-alpha04"
|
||||
|
||||
implementation "android.arch.work:work-runtime-ktx:$work_version"
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,226 @@
|
||||
{
|
||||
"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\")"
|
||||
]
|
||||
}
|
||||
}
|
@ -23,11 +23,11 @@ import apps.amine.bou.readerforselfoss.themes.Toppings
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import apps.amine.bou.readerforselfoss.utils.isBaseUrlValid
|
||||
import com.ftinc.scoop.Scoop
|
||||
import kotlinx.android.synthetic.main.activity_add_source.*
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import android.graphics.PorterDuff
|
||||
import apps.amine.bou.readerforselfoss.databinding.ActivityAddSourceBinding
|
||||
|
||||
|
||||
|
||||
@ -37,50 +37,53 @@ class AddSourceActivity : AppCompatActivity() {
|
||||
private lateinit var api: SelfossApi
|
||||
|
||||
private lateinit var appColors: AppColors
|
||||
private lateinit var binding: ActivityAddSourceBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
appColors = AppColors(this@AddSourceActivity)
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityAddSourceBinding.inflate(layoutInflater)
|
||||
val view = binding.root
|
||||
|
||||
setContentView(R.layout.activity_add_source)
|
||||
setContentView(view)
|
||||
|
||||
val scoop = Scoop.getInstance()
|
||||
scoop.bind(this, Toppings.PRIMARY.value, toolbar)
|
||||
scoop.bind(this, Toppings.PRIMARY.value, binding.toolbar)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
|
||||
}
|
||||
|
||||
val drawable = nameInput.background
|
||||
val drawable = binding.nameInput.background
|
||||
drawable.setColorFilter(appColors.colorAccent, PorterDuff.Mode.SRC_ATOP)
|
||||
|
||||
|
||||
// TODO: clean
|
||||
if(Build.VERSION.SDK_INT > 16) {
|
||||
nameInput.background = drawable
|
||||
binding.nameInput.background = drawable
|
||||
} else{
|
||||
nameInput.setBackgroundDrawable(drawable)
|
||||
binding.nameInput.setBackgroundDrawable(drawable)
|
||||
}
|
||||
|
||||
val drawable1 = sourceUri.background
|
||||
val drawable1 = binding.sourceUri.background
|
||||
drawable1.setColorFilter(appColors.colorAccent, PorterDuff.Mode.SRC_ATOP)
|
||||
|
||||
if(Build.VERSION.SDK_INT > 16) {
|
||||
sourceUri.background = drawable1
|
||||
binding.sourceUri.background = drawable1
|
||||
} else{
|
||||
sourceUri.setBackgroundDrawable(drawable1)
|
||||
binding.sourceUri.setBackgroundDrawable(drawable1)
|
||||
}
|
||||
|
||||
val drawable2 = tags.background
|
||||
val drawable2 = binding.tags.background
|
||||
drawable2.setColorFilter(appColors.colorAccent, PorterDuff.Mode.SRC_ATOP)
|
||||
|
||||
if(Build.VERSION.SDK_INT > 16) {
|
||||
tags.background = drawable2
|
||||
binding.tags.background = drawable2
|
||||
} else{
|
||||
tags.setBackgroundDrawable(drawable2)
|
||||
binding.tags.setBackgroundDrawable(drawable2)
|
||||
}
|
||||
|
||||
setSupportActionBar(toolbar)
|
||||
setSupportActionBar(binding.toolbar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||
|
||||
@ -92,18 +95,18 @@ class AddSourceActivity : AppCompatActivity() {
|
||||
this,
|
||||
this@AddSourceActivity,
|
||||
settings.getBoolean("isSelfSignedCert", false),
|
||||
prefs.getString("api_timeout", "-1").toLong()
|
||||
prefs.getString("api_timeout", "-1")!!.toLong()
|
||||
)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
mustLoginToAddSource()
|
||||
}
|
||||
|
||||
maybeGetDetailsFromIntentSharing(intent, sourceUri, nameInput)
|
||||
maybeGetDetailsFromIntentSharing(intent, binding.sourceUri, binding.nameInput)
|
||||
|
||||
saveBtn.setTextColor(appColors.colorAccent)
|
||||
binding.saveBtn.setTextColor(appColors.colorAccent)
|
||||
|
||||
saveBtn.setOnClickListener {
|
||||
handleSaveSource(tags, nameInput.text.toString(), sourceUri.text.toString(), api!!)
|
||||
binding.saveBtn.setOnClickListener {
|
||||
handleSaveSource(binding.tags, binding.nameInput.text.toString(), binding.sourceUri.text.toString(), api)
|
||||
}
|
||||
}
|
||||
|
||||
@ -114,7 +117,7 @@ class AddSourceActivity : AppCompatActivity() {
|
||||
if (config.baseUrl.isEmpty() || !config.baseUrl.isBaseUrlValid(this@AddSourceActivity)) {
|
||||
mustLoginToAddSource()
|
||||
} else {
|
||||
handleSpoutsSpinner(spoutsSpinner, api, progress, formContainer)
|
||||
handleSpoutsSpinner(binding.spoutsSpinner, api, binding.progress, binding.formContainer)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,10 +29,12 @@ import apps.amine.bou.readerforselfoss.adapters.ItemListAdapter
|
||||
import apps.amine.bou.readerforselfoss.adapters.ItemsAdapter
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.*
|
||||
import apps.amine.bou.readerforselfoss.background.LoadingWorker
|
||||
import apps.amine.bou.readerforselfoss.databinding.ActivityHomeBinding
|
||||
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity
|
||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_1_2
|
||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_2_3
|
||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_3_4
|
||||
import apps.amine.bou.readerforselfoss.settings.SettingsActivity
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.themes.Toppings
|
||||
@ -63,10 +65,10 @@ import com.mikepenz.materialdrawer.holder.StringHolder
|
||||
import com.mikepenz.materialdrawer.model.DividerDrawerItem
|
||||
import com.mikepenz.materialdrawer.model.PrimaryDrawerItem
|
||||
import com.mikepenz.materialdrawer.model.SecondaryDrawerItem
|
||||
import kotlinx.android.synthetic.main.activity_home.*
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.concurrent.thread
|
||||
|
||||
@ -100,6 +102,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
private var infiniteScroll: Boolean = false
|
||||
private var lastFetchDone: Boolean = false
|
||||
private var itemsCaching: Boolean = false
|
||||
private var updateSources: Boolean = true
|
||||
private var hiddenTags: List<String> = emptyList()
|
||||
|
||||
private var periodicRefresh = false
|
||||
@ -119,6 +122,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
private var firstVisible: Int = 0
|
||||
private lateinit var recyclerViewScrollListener: RecyclerView.OnScrollListener
|
||||
private lateinit var settings: SharedPreferences
|
||||
private lateinit var binding: ActivityHomeBinding
|
||||
|
||||
private var recyclerAdapter: RecyclerView.Adapter<*>? = null
|
||||
|
||||
@ -147,6 +151,8 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
config = Config(this@HomeActivity)
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityHomeBinding.inflate(layoutInflater)
|
||||
val view = binding.root
|
||||
|
||||
fromTabShortcut = intent.getIntExtra("shortcutTab", -1) != -1
|
||||
offlineShortcut = intent.getBooleanExtra("startOffline", false)
|
||||
@ -155,16 +161,16 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
elementsShown = intent.getIntExtra("shortcutTab", UNREAD_SHOWN)
|
||||
}
|
||||
|
||||
setContentView(R.layout.activity_home)
|
||||
setContentView(view)
|
||||
|
||||
handleThemeBinding()
|
||||
|
||||
setSupportActionBar(toolBar)
|
||||
setSupportActionBar(binding.toolBar)
|
||||
|
||||
db = Room.databaseBuilder(
|
||||
applicationContext,
|
||||
AppDatabase::class.java, "selfoss-database"
|
||||
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).build()
|
||||
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).addMigrations(MIGRATION_3_4).build()
|
||||
|
||||
|
||||
customTabActivityHelper = CustomTabActivityHelper()
|
||||
@ -176,7 +182,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
this,
|
||||
this@HomeActivity,
|
||||
settings.getBoolean("isSelfSignedCert", false),
|
||||
sharedPref.getString("api_timeout", "-1").toLong()
|
||||
sharedPref.getString("api_timeout", "-1")!!.toLong()
|
||||
)
|
||||
items = ArrayList()
|
||||
allItems = ArrayList()
|
||||
@ -185,15 +191,19 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
handleDrawer()
|
||||
|
||||
handleSwipeRefreshLayout()
|
||||
|
||||
handleSharedPrefs()
|
||||
|
||||
getElementsAccordingToTab()
|
||||
}
|
||||
|
||||
private fun handleSwipeRefreshLayout() {
|
||||
swipeRefreshLayout.setColorSchemeResources(
|
||||
binding.swipeRefreshLayout.setColorSchemeResources(
|
||||
R.color.refresh_progress_1,
|
||||
R.color.refresh_progress_2,
|
||||
R.color.refresh_progress_3
|
||||
)
|
||||
swipeRefreshLayout.setOnRefreshListener {
|
||||
binding.swipeRefreshLayout.setOnRefreshListener {
|
||||
offlineShortcut = false
|
||||
allItems = ArrayList()
|
||||
lastFetchDone = false
|
||||
@ -230,7 +240,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
val i = items.elementAtOrNull(position)
|
||||
|
||||
if (i != null) {
|
||||
val adapter = recyclerView.adapter as ItemsAdapter<*>
|
||||
val adapter = binding.recyclerView.adapter as ItemsAdapter<*>
|
||||
|
||||
val wasItemUnread = adapter.unreadItemStatusAtIndex(position)
|
||||
|
||||
@ -268,7 +278,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
}
|
||||
}
|
||||
|
||||
ItemTouchHelper(simpleItemTouchCallback).attachToRecyclerView(recyclerView)
|
||||
ItemTouchHelper(simpleItemTouchCallback).attachToRecyclerView(binding.recyclerView)
|
||||
}
|
||||
|
||||
private fun handleBottomBar() {
|
||||
@ -305,18 +315,17 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
).setActiveColorResource(R.color.pink)
|
||||
.setBadgeItem(tabStarredBadge)
|
||||
|
||||
bottomBar
|
||||
binding.bottomBar
|
||||
.addItem(tabNew)
|
||||
.addItem(tabArchive)
|
||||
.addItem(tabStarred)
|
||||
.setFirstSelectedPosition(0)
|
||||
.initialise()
|
||||
|
||||
bottomBar.setMode(BottomNavigationBar.MODE_SHIFTING)
|
||||
bottomBar.setBackgroundStyle(BottomNavigationBar.BACKGROUND_STYLE_STATIC)
|
||||
binding.bottomBar.setMode(BottomNavigationBar.MODE_SHIFTING)
|
||||
binding.bottomBar.setBackgroundStyle(BottomNavigationBar.BACKGROUND_STYLE_STATIC)
|
||||
|
||||
if (fromTabShortcut) {
|
||||
bottomBar.selectTab(elementsShown - 1)
|
||||
binding.bottomBar.selectTab(elementsShown - 1)
|
||||
}
|
||||
}
|
||||
|
||||
@ -330,8 +339,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
|
||||
editor = settings.edit()
|
||||
|
||||
handleSharedPrefs()
|
||||
|
||||
handleDrawerItems()
|
||||
|
||||
handleThemeUpdate()
|
||||
@ -339,22 +346,20 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
reloadLayoutManager()
|
||||
|
||||
if (!infiniteScroll) {
|
||||
recyclerView.setHasFixedSize(true)
|
||||
binding.recyclerView.setHasFixedSize(true)
|
||||
} else {
|
||||
handleInfiniteScroll()
|
||||
}
|
||||
|
||||
handleBottomBarActions()
|
||||
|
||||
getElementsAccordingToTab()
|
||||
|
||||
handleRecurringTask()
|
||||
|
||||
handleOfflineActions()
|
||||
}
|
||||
|
||||
private fun getAndStoreAllItems() {
|
||||
api.allItems().enqueue(object : Callback<List<Item>> {
|
||||
api.allNewItems().enqueue(object : Callback<List<Item>> {
|
||||
override fun onFailure(call: Call<List<Item>>, t: Throwable) {
|
||||
}
|
||||
|
||||
@ -362,18 +367,48 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
call: Call<List<Item>>,
|
||||
response: Response<List<Item>>
|
||||
) {
|
||||
thread {
|
||||
if (response.body() != null) {
|
||||
val apiItems = (response.body() as ArrayList<Item>).filter {
|
||||
maybeTagFilter != null || filter(it.tags.tags)
|
||||
} as ArrayList<Item>
|
||||
db.itemsDao().deleteAllItems()
|
||||
db.itemsDao()
|
||||
.insertAllItems(*(apiItems.map { it.toEntity() }).toTypedArray())
|
||||
}
|
||||
}
|
||||
enqueueArticles(response, true)
|
||||
}
|
||||
})
|
||||
|
||||
api.allReadItems().enqueue(object : Callback<List<Item>> {
|
||||
override fun onFailure(call: Call<List<Item>>, t: Throwable) {
|
||||
}
|
||||
|
||||
override fun onResponse(
|
||||
call: Call<List<Item>>,
|
||||
response: Response<List<Item>>
|
||||
) {
|
||||
enqueueArticles(response, false)
|
||||
}
|
||||
})
|
||||
|
||||
api.allStarredItems().enqueue(object : Callback<List<Item>> {
|
||||
override fun onFailure(call: Call<List<Item>>, t: Throwable) {
|
||||
}
|
||||
|
||||
override fun onResponse(
|
||||
call: Call<List<Item>>,
|
||||
response: Response<List<Item>>
|
||||
) {
|
||||
enqueueArticles(response, false)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun enqueueArticles(response: Response<List<Item>>, clearDatabase: Boolean) {
|
||||
thread {
|
||||
if (response.body() != null) {
|
||||
val apiItems = (response.body() as ArrayList<Item>).filter {
|
||||
maybeTagFilter != null || filter(it.tags.tags)
|
||||
} as ArrayList<Item>
|
||||
if (clearDatabase) {
|
||||
db.itemsDao().deleteAllItems()
|
||||
}
|
||||
db.itemsDao()
|
||||
.insertAllItems(*(apiItems.map { it.toEntity() }).toTypedArray())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
@ -388,19 +423,20 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
displayUnreadCount = sharedPref.getBoolean("display_unread_count", true)
|
||||
displayAllCount = sharedPref.getBoolean("display_other_count", false)
|
||||
fullHeightCards = sharedPref.getBoolean("full_height_cards", false)
|
||||
itemsNumber = sharedPref.getString("prefer_api_items_number", "200").toInt()
|
||||
userIdentifier = sharedPref.getString("unique_id", "")
|
||||
itemsNumber = sharedPref.getString("prefer_api_items_number", "200")!!.toInt()
|
||||
userIdentifier = sharedPref.getString("unique_id", "")!!
|
||||
displayAccountHeader = sharedPref.getBoolean("account_header_displaying", false)
|
||||
infiniteScroll = sharedPref.getBoolean("infinite_loading", false)
|
||||
itemsCaching = sharedPref.getBoolean("items_caching", false)
|
||||
hiddenTags = if (sharedPref.getString("hidden_tags", "").isNotEmpty()) {
|
||||
sharedPref.getString("hidden_tags", "").replace("\\s".toRegex(), "").split(",")
|
||||
updateSources = sharedPref.getBoolean("update_sources", true)
|
||||
hiddenTags = if (sharedPref.getString("hidden_tags", "")!!.isNotEmpty()) {
|
||||
sharedPref.getString("hidden_tags", "")!!.replace("\\s".toRegex(), "").split(",")
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
periodicRefresh = sharedPref.getBoolean("periodic_refresh", false)
|
||||
refreshWhenChargingOnly = sharedPref.getBoolean("refresh_when_charging", false)
|
||||
refreshMinutes = sharedPref.getString("periodic_refresh_minutes", "360").toLong()
|
||||
refreshMinutes = sharedPref.getString("periodic_refresh_minutes", "360")!!.toLong()
|
||||
|
||||
if (refreshMinutes <= 15) {
|
||||
refreshMinutes = 15
|
||||
@ -409,7 +445,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
|
||||
private fun handleThemeBinding() {
|
||||
val scoop = Scoop.getInstance()
|
||||
scoop.bind(this, Toppings.PRIMARY.value, toolBar)
|
||||
scoop.bind(this, Toppings.PRIMARY.value, binding.toolBar)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
|
||||
}
|
||||
@ -432,24 +468,24 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
|
||||
drawer = drawer {
|
||||
rootViewRes = R.id.drawer_layout
|
||||
toolbar = toolBar
|
||||
toolbar = binding.toolBar
|
||||
actionBarDrawerToggleEnabled = true
|
||||
actionBarDrawerToggleAnimated = true
|
||||
showOnFirstLaunch = true
|
||||
onSlide { _, p1 ->
|
||||
bottomBar.alpha = (1 - p1)
|
||||
binding.bottomBar.alpha = (1 - p1)
|
||||
}
|
||||
onClosed {
|
||||
bottomBar.show()
|
||||
binding.bottomBar.show()
|
||||
}
|
||||
onOpened {
|
||||
bottomBar.hide()
|
||||
binding.bottomBar.hide()
|
||||
}
|
||||
|
||||
if (displayAccountHeader) {
|
||||
accountHeader {
|
||||
background = R.drawable.bg
|
||||
profile(settings.getString("url", "")) {
|
||||
profile(settings.getString("url", "")!!) {
|
||||
iconDrawable = resources.getDrawable(R.mipmap.ic_launcher)
|
||||
}
|
||||
selectionListEnabledForSingleProfile = false
|
||||
@ -603,7 +639,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
} else {
|
||||
for (tag in maybeSources) {
|
||||
val item = PrimaryDrawerItem()
|
||||
.withName(tag.title)
|
||||
.withName(tag.getTitleDecoded())
|
||||
.withIdentifier(tag.id.toLong())
|
||||
.withOnDrawerItemClickListener { _, _, _ ->
|
||||
allItems = ArrayList()
|
||||
@ -683,13 +719,6 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
.withIconTintingEnabled(true)
|
||||
.withOnDrawerItemClickListener { _, _, _ ->
|
||||
LibsBuilder()
|
||||
.withActivityStyle(
|
||||
if (appColors.isDarkTheme) {
|
||||
Libs.ActivityStyle.DARK
|
||||
} else {
|
||||
Libs.ActivityStyle.LIGHT_DARK_TOOLBAR
|
||||
}
|
||||
)
|
||||
.withAboutIconShown(true)
|
||||
.withAboutVersionShown(true)
|
||||
.start(this@HomeActivity)
|
||||
@ -702,6 +731,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
if (maybeDrawerData.tags != null) {
|
||||
thread {
|
||||
val tagEntities = maybeDrawerData.tags.map { it.toEntity() }
|
||||
db.drawerDataDao().deleteAllTags()
|
||||
db.drawerDataDao().insertAllTags(*tagEntities.toTypedArray())
|
||||
}
|
||||
}
|
||||
@ -709,6 +739,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
thread {
|
||||
val sourceEntities =
|
||||
maybeDrawerData.sources.map { it.toEntity() }
|
||||
db.drawerDataDao().deleteAllSources()
|
||||
db.drawerDataDao().insertAllSources(*sourceEntities.toTypedArray())
|
||||
}
|
||||
}
|
||||
@ -736,7 +767,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
var sources: List<Source>?
|
||||
|
||||
fun sourcesApiCall() {
|
||||
if (this@HomeActivity.isNetworkAccessible(null, offlineShortcut)) {
|
||||
if (this@HomeActivity.isNetworkAccessible(null, offlineShortcut) && updateSources) {
|
||||
api.sources.enqueue(object : Callback<List<Source>> {
|
||||
override fun onResponse(
|
||||
call: Call<List<Source>>?,
|
||||
@ -759,7 +790,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
}
|
||||
}
|
||||
|
||||
if (this@HomeActivity.isNetworkAccessible(null, offlineShortcut)) {
|
||||
if (this@HomeActivity.isNetworkAccessible(null, offlineShortcut) && updateSources) {
|
||||
api.tags.enqueue(object : Callback<List<Tag>> {
|
||||
override fun onResponse(
|
||||
call: Call<List<Tag>>,
|
||||
@ -793,7 +824,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
}
|
||||
|
||||
private fun reloadLayoutManager() {
|
||||
val currentManager = recyclerView.layoutManager
|
||||
val currentManager = binding.recyclerView.layoutManager
|
||||
val layoutManager: RecyclerView.LayoutManager
|
||||
|
||||
// This will only update the layout manager if settings changed
|
||||
@ -804,7 +835,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
this,
|
||||
calculateNoOfColumns()
|
||||
)
|
||||
recyclerView.layoutManager = layoutManager
|
||||
binding.recyclerView.layoutManager = layoutManager
|
||||
}
|
||||
is GridLayoutManager ->
|
||||
if (shouldBeCardView) {
|
||||
@ -814,7 +845,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
)
|
||||
layoutManager.gapStrategy =
|
||||
StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
|
||||
recyclerView.layoutManager = layoutManager
|
||||
binding.recyclerView.layoutManager = layoutManager
|
||||
}
|
||||
else ->
|
||||
if (currentManager == null) {
|
||||
@ -823,7 +854,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
this,
|
||||
calculateNoOfColumns()
|
||||
)
|
||||
recyclerView.layoutManager = layoutManager
|
||||
binding.recyclerView.layoutManager = layoutManager
|
||||
} else {
|
||||
layoutManager = StaggeredGridLayoutManager(
|
||||
calculateNoOfColumns(),
|
||||
@ -831,7 +862,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
)
|
||||
layoutManager.gapStrategy =
|
||||
StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
|
||||
recyclerView.layoutManager = layoutManager
|
||||
binding.recyclerView.layoutManager = layoutManager
|
||||
}
|
||||
} else {
|
||||
}
|
||||
@ -839,11 +870,11 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
}
|
||||
|
||||
private fun handleBottomBarActions() {
|
||||
bottomBar.setTabSelectedListener(object : BottomNavigationBar.OnTabSelectedListener {
|
||||
binding.bottomBar.setTabSelectedListener(object : BottomNavigationBar.OnTabSelectedListener {
|
||||
override fun onTabUnselected(position: Int) = Unit
|
||||
|
||||
override fun onTabReselected(position: Int) {
|
||||
val layoutManager = recyclerView.adapter
|
||||
val layoutManager = binding.recyclerView.adapter
|
||||
|
||||
when (layoutManager) {
|
||||
is StaggeredGridLayoutManager ->
|
||||
@ -868,12 +899,14 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
|
||||
if (itemsCaching) {
|
||||
|
||||
if (!swipeRefreshLayout.isRefreshing) {
|
||||
swipeRefreshLayout.post { swipeRefreshLayout.isRefreshing = true }
|
||||
if (!binding.swipeRefreshLayout.isRefreshing) {
|
||||
binding.swipeRefreshLayout.post { binding.swipeRefreshLayout.isRefreshing = true }
|
||||
}
|
||||
|
||||
thread {
|
||||
val dbItems = db.itemsDao().items().map { it.toView() }
|
||||
val dbItems = db.itemsDao().items().map { it.toView() }.sortedByDescending {
|
||||
SimpleDateFormat(dateTimeFormatter).parse(it.datetime)
|
||||
}
|
||||
runOnUiThread {
|
||||
if (dbItems.isNotEmpty()) {
|
||||
items = when (position) {
|
||||
@ -919,7 +952,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
recyclerViewScrollListener = object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(localRecycler: RecyclerView, dx: Int, dy: Int) {
|
||||
if (dy > 0) {
|
||||
val manager = recyclerView.layoutManager
|
||||
val manager = binding.recyclerView.layoutManager
|
||||
val lastVisibleItem: Int = when (manager) {
|
||||
is StaggeredGridLayoutManager -> manager.findLastCompletelyVisibleItemPositions(
|
||||
null
|
||||
@ -935,17 +968,17 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
}
|
||||
}
|
||||
|
||||
recyclerView.clearOnScrollListeners()
|
||||
recyclerView.addOnScrollListener(recyclerViewScrollListener)
|
||||
binding.recyclerView.clearOnScrollListeners()
|
||||
binding.recyclerView.addOnScrollListener(recyclerViewScrollListener)
|
||||
}
|
||||
|
||||
private fun mayBeEmpty() =
|
||||
if (items.isEmpty()) {
|
||||
emptyText.visibility = View.VISIBLE
|
||||
recyclerView.visibility = View.GONE
|
||||
binding.emptyText.visibility = View.VISIBLE
|
||||
binding.recyclerView.visibility = View.GONE
|
||||
} else {
|
||||
emptyText.visibility = View.GONE
|
||||
recyclerView.visibility = View.VISIBLE
|
||||
binding.emptyText.visibility = View.GONE
|
||||
binding.recyclerView.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
private fun getElementsAccordingToTab(
|
||||
@ -970,12 +1003,14 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
|
||||
if (itemsCaching) {
|
||||
|
||||
if (!swipeRefreshLayout.isRefreshing) {
|
||||
swipeRefreshLayout.post { swipeRefreshLayout.isRefreshing = true }
|
||||
if (!binding.swipeRefreshLayout.isRefreshing) {
|
||||
binding.swipeRefreshLayout.post { binding.swipeRefreshLayout.isRefreshing = true }
|
||||
}
|
||||
|
||||
thread {
|
||||
val dbItems = db.itemsDao().items().map { it.toView() }
|
||||
val dbItems = db.itemsDao().items().map { it.toView() }.sortedByDescending {
|
||||
SimpleDateFormat(dateTimeFormatter).parse(it.datetime)
|
||||
}
|
||||
runOnUiThread {
|
||||
if (dbItems.isNotEmpty()) {
|
||||
items = when (elementsShown) {
|
||||
@ -1039,11 +1074,11 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
handleListResult(appendResults)
|
||||
|
||||
if (!appendResults) mayBeEmpty()
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
}
|
||||
|
||||
if (!swipeRefreshLayout.isRefreshing) {
|
||||
swipeRefreshLayout.post { swipeRefreshLayout.isRefreshing = true }
|
||||
if (!binding.swipeRefreshLayout.isRefreshing) {
|
||||
binding.swipeRefreshLayout.post { binding.swipeRefreshLayout.isRefreshing = true }
|
||||
}
|
||||
|
||||
if (this@HomeActivity.isNetworkAccessible(this@HomeActivity.findViewById(R.id.coordLayout), offlineShortcut)) {
|
||||
@ -1057,7 +1092,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<List<Item>>, t: Throwable) {
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
Toast.makeText(
|
||||
this@HomeActivity,
|
||||
toastMessage,
|
||||
@ -1066,7 +1101,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
}
|
||||
})
|
||||
} else {
|
||||
swipeRefreshLayout.post { swipeRefreshLayout.isRefreshing = false }
|
||||
binding.swipeRefreshLayout.post { binding.swipeRefreshLayout.isRefreshing = false }
|
||||
}
|
||||
}
|
||||
|
||||
@ -1111,7 +1146,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
|
||||
private fun handleListResult(appendResults: Boolean = false) {
|
||||
if (appendResults) {
|
||||
val oldManager = recyclerView.layoutManager
|
||||
val oldManager = binding.recyclerView.layoutManager
|
||||
firstVisible = when (oldManager) {
|
||||
is StaggeredGridLayoutManager ->
|
||||
oldManager.findFirstCompletelyVisibleItemPositions(null).last()
|
||||
@ -1156,14 +1191,14 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
updateItems(it)
|
||||
}
|
||||
|
||||
recyclerView.addItemDecoration(
|
||||
binding.recyclerView.addItemDecoration(
|
||||
DividerItemDecoration(
|
||||
this@HomeActivity,
|
||||
DividerItemDecoration.VERTICAL
|
||||
)
|
||||
)
|
||||
}
|
||||
recyclerView.adapter = recyclerAdapter
|
||||
binding.recyclerView.adapter = recyclerAdapter
|
||||
} else {
|
||||
if (!appendResults) {
|
||||
(recyclerAdapter as ItemsAdapter<*>).updateAllItems(items)
|
||||
@ -1314,7 +1349,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
R.id.readAll -> {
|
||||
if (elementsShown == UNREAD_SHOWN) {
|
||||
needsConfirmation(R.string.readAll, R.string.markall_dialog_message) {
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
val ids = allItems.map { it.id }
|
||||
val itemsByTag: Map<Long, Int> =
|
||||
allItems.flattenTags()
|
||||
@ -1349,7 +1384,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
|
||||
}
|
||||
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
}
|
||||
|
||||
override fun onFailure(call: Call<SuccessResponse>, t: Throwable) {
|
||||
@ -1358,7 +1393,7 @@ class HomeActivity : AppCompatActivity(), SearchView.OnQueryTextListener {
|
||||
R.string.all_posts_not_read,
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
swipeRefreshLayout.isRefreshing = false
|
||||
binding.swipeRefreshLayout.isRefreshing = false
|
||||
}
|
||||
})
|
||||
items = ArrayList()
|
||||
|
@ -5,27 +5,31 @@ import android.view.MenuItem
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import androidx.fragment.app.FragmentStatePagerAdapter
|
||||
import apps.amine.bou.readerforselfoss.databinding.ActivityImageBinding
|
||||
import apps.amine.bou.readerforselfoss.fragments.ImageFragment
|
||||
import kotlinx.android.synthetic.main.activity_reader.*
|
||||
|
||||
class ImageActivity : AppCompatActivity() {
|
||||
private lateinit var allImages : ArrayList<String>
|
||||
private var position : Int = 0
|
||||
|
||||
private lateinit var binding: ActivityImageBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityImageBinding.inflate(layoutInflater)
|
||||
val view = binding.root
|
||||
|
||||
setContentView(R.layout.activity_image)
|
||||
setContentView(view)
|
||||
|
||||
setSupportActionBar(toolBar)
|
||||
setSupportActionBar(binding.toolBar)
|
||||
supportActionBar?.setDisplayShowTitleEnabled(false)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
|
||||
allImages = intent.getStringArrayListExtra("allImages")
|
||||
allImages = intent.getStringArrayListExtra("allImages") as ArrayList<String>
|
||||
position = intent.getIntExtra("position", 0)
|
||||
|
||||
pager.adapter = ScreenSlidePagerAdapter(supportFragmentManager)
|
||||
pager.currentItem = position
|
||||
binding.pager.adapter = ScreenSlidePagerAdapter(supportFragmentManager)
|
||||
binding.pager.currentItem = position
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
|
@ -17,13 +17,13 @@ import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.databinding.ActivityLoginBinding
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import apps.amine.bou.readerforselfoss.utils.isBaseUrlValid
|
||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
||||
import com.mikepenz.aboutlibraries.Libs
|
||||
import com.mikepenz.aboutlibraries.LibsBuilder
|
||||
import kotlinx.android.synthetic.main.activity_login.*
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
@ -39,24 +39,27 @@ class LoginActivity : AppCompatActivity() {
|
||||
private lateinit var editor: SharedPreferences.Editor
|
||||
private lateinit var userIdentifier: String
|
||||
private lateinit var appColors: AppColors
|
||||
private lateinit var binding: ActivityLoginBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
appColors = AppColors(this@LoginActivity)
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityLoginBinding.inflate(layoutInflater)
|
||||
val view = binding.root
|
||||
|
||||
setContentView(R.layout.activity_login)
|
||||
setContentView(view)
|
||||
|
||||
setSupportActionBar(toolbar)
|
||||
setSupportActionBar(binding.toolbar)
|
||||
|
||||
handleBaseUrlFail()
|
||||
|
||||
settings = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||
userIdentifier = settings.getString("unique_id", "")
|
||||
userIdentifier = settings.getString("unique_id", "")!!
|
||||
|
||||
editor = settings.edit()
|
||||
|
||||
if (settings.getString("url", "").isNotEmpty()) {
|
||||
if (settings.getString("url", "")!!.isNotEmpty()) {
|
||||
goToMain()
|
||||
}
|
||||
|
||||
@ -65,14 +68,14 @@ class LoginActivity : AppCompatActivity() {
|
||||
|
||||
private fun handleActions() {
|
||||
|
||||
withSelfhostedCert.setOnCheckedChangeListener { _, b ->
|
||||
binding.withSelfhostedCert.setOnCheckedChangeListener { _, b ->
|
||||
isWithSelfSignedCert = !isWithSelfSignedCert
|
||||
val visi: Int = if (b) View.VISIBLE else View.GONE
|
||||
|
||||
warningText.visibility = visi
|
||||
binding.warningText.visibility = visi
|
||||
}
|
||||
|
||||
passwordView.setOnEditorActionListener(
|
||||
binding.passwordView.setOnEditorActionListener(
|
||||
TextView.OnEditorActionListener { _, id, _ ->
|
||||
if (id == R.id.loginView || id == EditorInfo.IME_NULL) {
|
||||
attemptLogin()
|
||||
@ -82,22 +85,22 @@ class LoginActivity : AppCompatActivity() {
|
||||
}
|
||||
)
|
||||
|
||||
signInButton.setOnClickListener { attemptLogin() }
|
||||
binding.signInButton.setOnClickListener { attemptLogin() }
|
||||
|
||||
withLogin.setOnCheckedChangeListener { _, b ->
|
||||
binding.withLogin.setOnCheckedChangeListener { _, b ->
|
||||
isWithLogin = !isWithLogin
|
||||
val visi: Int = if (b) View.VISIBLE else View.GONE
|
||||
|
||||
loginLayout.visibility = visi
|
||||
passwordLayout.visibility = visi
|
||||
binding.loginLayout.visibility = visi
|
||||
binding.passwordLayout.visibility = visi
|
||||
}
|
||||
|
||||
withHttpLogin.setOnCheckedChangeListener { _, b ->
|
||||
binding.withHttpLogin.setOnCheckedChangeListener { _, b ->
|
||||
isWithHTTPLogin = !isWithHTTPLogin
|
||||
val visi: Int = if (b) View.VISIBLE else View.GONE
|
||||
|
||||
httpLoginInput.visibility = visi
|
||||
httpPasswordInput.visibility = visi
|
||||
binding.httpLoginInput.visibility = visi
|
||||
binding.httpPasswordInput.visibility = visi
|
||||
}
|
||||
}
|
||||
|
||||
@ -124,25 +127,25 @@ class LoginActivity : AppCompatActivity() {
|
||||
private fun attemptLogin() {
|
||||
|
||||
// Reset errors.
|
||||
urlView.error = null
|
||||
loginView.error = null
|
||||
httpLoginView.error = null
|
||||
passwordView.error = null
|
||||
httpPasswordView.error = null
|
||||
binding.urlView.error = null
|
||||
binding.loginView.error = null
|
||||
binding.httpLoginView.error = null
|
||||
binding.passwordView.error = null
|
||||
binding.httpPasswordView.error = null
|
||||
|
||||
// Store values at the time of the login attempt.
|
||||
val url = urlView.text.toString()
|
||||
val login = loginView.text.toString()
|
||||
val httpLogin = httpLoginView.text.toString()
|
||||
val password = passwordView.text.toString()
|
||||
val httpPassword = httpPasswordView.text.toString()
|
||||
val url = binding.urlView.text.toString()
|
||||
val login = binding.loginView.text.toString()
|
||||
val httpLogin = binding.httpLoginView.text.toString()
|
||||
val password = binding.passwordView.text.toString()
|
||||
val httpPassword = binding.httpPasswordView.text.toString()
|
||||
|
||||
var cancel = false
|
||||
var focusView: View? = null
|
||||
|
||||
if (!url.isBaseUrlValid(this@LoginActivity)) {
|
||||
urlView.error = getString(R.string.login_url_problem)
|
||||
focusView = urlView
|
||||
binding.urlView.error = getString(R.string.login_url_problem)
|
||||
focusView = binding.urlView
|
||||
cancel = true
|
||||
inValidCount++
|
||||
if (inValidCount == 3) {
|
||||
@ -161,28 +164,28 @@ class LoginActivity : AppCompatActivity() {
|
||||
|
||||
if (isWithLogin) {
|
||||
if (TextUtils.isEmpty(password)) {
|
||||
passwordView.error = getString(R.string.error_invalid_password)
|
||||
focusView = passwordView
|
||||
binding.passwordView.error = getString(R.string.error_invalid_password)
|
||||
focusView = binding.passwordView
|
||||
cancel = true
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(login)) {
|
||||
loginView.error = getString(R.string.error_field_required)
|
||||
focusView = loginView
|
||||
binding.loginView.error = getString(R.string.error_field_required)
|
||||
focusView = binding.loginView
|
||||
cancel = true
|
||||
}
|
||||
}
|
||||
|
||||
if (isWithHTTPLogin) {
|
||||
if (TextUtils.isEmpty(httpPassword)) {
|
||||
httpPasswordView.error = getString(R.string.error_invalid_password)
|
||||
focusView = httpPasswordView
|
||||
binding.httpPasswordView.error = getString(R.string.error_invalid_password)
|
||||
focusView = binding.httpPasswordView
|
||||
cancel = true
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(httpLogin)) {
|
||||
httpLoginView.error = getString(R.string.error_field_required)
|
||||
focusView = httpLoginView
|
||||
binding.httpLoginView.error = getString(R.string.error_field_required)
|
||||
focusView = binding.httpLoginView
|
||||
cancel = true
|
||||
}
|
||||
}
|
||||
@ -216,11 +219,11 @@ class LoginActivity : AppCompatActivity() {
|
||||
editor.remove("password")
|
||||
editor.remove("httpPassword")
|
||||
editor.apply()
|
||||
urlView.error = getString(R.string.wrong_infos)
|
||||
loginView.error = getString(R.string.wrong_infos)
|
||||
passwordView.error = getString(R.string.wrong_infos)
|
||||
httpLoginView.error = getString(R.string.wrong_infos)
|
||||
httpPasswordView.error = getString(R.string.wrong_infos)
|
||||
binding.urlView.error = getString(R.string.wrong_infos)
|
||||
binding.loginView.error = getString(R.string.wrong_infos)
|
||||
binding.passwordView.error = getString(R.string.wrong_infos)
|
||||
binding.httpLoginView.error = getString(R.string.wrong_infos)
|
||||
binding.httpPasswordView.error = getString(R.string.wrong_infos)
|
||||
showProgress(false)
|
||||
}
|
||||
|
||||
@ -248,28 +251,28 @@ class LoginActivity : AppCompatActivity() {
|
||||
private fun showProgress(show: Boolean) {
|
||||
val shortAnimTime = resources.getInteger(android.R.integer.config_shortAnimTime)
|
||||
|
||||
loginForm.visibility = if (show) View.GONE else View.VISIBLE
|
||||
loginForm
|
||||
binding.loginForm.visibility = if (show) View.GONE else View.VISIBLE
|
||||
binding.loginForm
|
||||
.animate()
|
||||
.setDuration(shortAnimTime.toLong())
|
||||
.alpha(
|
||||
if (show) 0F else 1F
|
||||
).setListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
loginForm.visibility = if (show) View.GONE else View.VISIBLE
|
||||
binding.loginForm.visibility = if (show) View.GONE else View.VISIBLE
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
loginProgress.visibility = if (show) View.VISIBLE else View.GONE
|
||||
loginProgress
|
||||
binding.loginProgress.visibility = if (show) View.VISIBLE else View.GONE
|
||||
binding.loginProgress
|
||||
.animate()
|
||||
.setDuration(shortAnimTime.toLong())
|
||||
.alpha(
|
||||
if (show) 1F else 0F
|
||||
).setListener(object : AnimatorListenerAdapter() {
|
||||
override fun onAnimationEnd(animation: Animator) {
|
||||
loginProgress.visibility = if (show) View.VISIBLE else View.GONE
|
||||
binding.loginProgress.visibility = if (show) View.VISIBLE else View.GONE
|
||||
}
|
||||
}
|
||||
)
|
||||
@ -284,7 +287,6 @@ class LoginActivity : AppCompatActivity() {
|
||||
return when (item.itemId) {
|
||||
R.id.about -> {
|
||||
LibsBuilder()
|
||||
.withActivityStyle(Libs.ActivityStyle.LIGHT_DARK_TOOLBAR)
|
||||
.withAboutIconShown(true)
|
||||
.withAboutVersionShown(true)
|
||||
.start(this)
|
||||
|
@ -4,12 +4,18 @@ import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import apps.amine.bou.readerforselfoss.databinding.ActivityAddSourceBinding
|
||||
import apps.amine.bou.readerforselfoss.databinding.ActivityMainBinding
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
private lateinit var binding: ActivityMainBinding
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_main)
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
val view = binding.root
|
||||
setContentView(view)
|
||||
|
||||
val intent = Intent(this, LoginActivity::class.java)
|
||||
|
||||
|
@ -3,12 +3,15 @@ package apps.amine.bou.readerforselfoss
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.preference.PreferenceManager
|
||||
import android.widget.ImageView
|
||||
import androidx.multidex.MultiDexApplication
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.ApiVersion
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import apps.amine.bou.readerforselfoss.utils.glide.loadMaybeBasicAuth
|
||||
import com.bumptech.glide.Glide
|
||||
@ -16,17 +19,27 @@ import com.bumptech.glide.request.RequestOptions
|
||||
import com.ftinc.scoop.Scoop
|
||||
import com.mikepenz.materialdrawer.util.AbstractDrawerImageLoader
|
||||
import com.mikepenz.materialdrawer.util.DrawerImageLoader
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
import java.util.UUID.randomUUID
|
||||
|
||||
var dateTimeFormatter = "yyyy-MM-dd HH:mm:ss"
|
||||
|
||||
class MyApp : MultiDexApplication() {
|
||||
private lateinit var config: Config
|
||||
private lateinit var api: SelfossApi
|
||||
private lateinit var settings: SharedPreferences
|
||||
private lateinit var sharedPref: SharedPreferences
|
||||
|
||||
private var apiVersionMajor: Int = 0
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
config = Config(baseContext)
|
||||
|
||||
val prefs = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||
if (prefs.getString("unique_id", "").isEmpty()) {
|
||||
if (prefs.getString("unique_id", "")!!.isEmpty()) {
|
||||
val editor = prefs.edit()
|
||||
editor.putString("unique_id", randomUUID().toString())
|
||||
editor.apply()
|
||||
@ -39,6 +52,19 @@ class MyApp : MultiDexApplication() {
|
||||
tryToHandleBug()
|
||||
|
||||
handleNotificationChannels()
|
||||
|
||||
sharedPref = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
apiVersionMajor = sharedPref.getInt("apiVersionMajor", 0)
|
||||
settings = getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||
|
||||
api = SelfossApi(
|
||||
this,
|
||||
null,
|
||||
settings.getBoolean("isSelfSignedCert", false),
|
||||
sharedPref.getString("api_timeout", "-1")!!.toLong()
|
||||
)
|
||||
|
||||
getApiMajorVersion()
|
||||
}
|
||||
|
||||
private fun handleNotificationChannels() {
|
||||
@ -103,4 +129,24 @@ class MyApp : MultiDexApplication() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getApiMajorVersion() {
|
||||
api.apiVersion.enqueue(object : Callback<ApiVersion> {
|
||||
override fun onFailure(call: Call<ApiVersion>, t: Throwable) {
|
||||
if (apiVersionMajor >= 4) {
|
||||
dateTimeFormatter = "yyyy-MM-dd'T'HH:mm:ssXXX"
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResponse(call: Call<ApiVersion>, response: Response<ApiVersion>) {
|
||||
val version = response.body() as ApiVersion
|
||||
apiVersionMajor = version.getApiMajorVersion()
|
||||
sharedPref.edit().putInt("apiVersionMajor", apiVersionMajor).commit()
|
||||
|
||||
if (apiVersionMajor >= 4) {
|
||||
dateTimeFormatter = "yyyy-MM-dd'T'HH:mm:ssXXX"
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -20,11 +20,14 @@ import androidx.room.Room
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.databinding.ActivityImageBinding
|
||||
import apps.amine.bou.readerforselfoss.databinding.ActivityReaderBinding
|
||||
import apps.amine.bou.readerforselfoss.fragments.ArticleFragment
|
||||
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity
|
||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_1_2
|
||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_2_3
|
||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_3_4
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.themes.Toppings
|
||||
import apps.amine.bou.readerforselfoss.transformers.DepthPageTransformer
|
||||
@ -34,7 +37,6 @@ import apps.amine.bou.readerforselfoss.utils.persistence.toEntity
|
||||
import apps.amine.bou.readerforselfoss.utils.succeeded
|
||||
import apps.amine.bou.readerforselfoss.utils.toggleStar
|
||||
import com.ftinc.scoop.Scoop
|
||||
import kotlinx.android.synthetic.main.activity_reader.*
|
||||
import me.relex.circleindicator.CircleIndicator
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
@ -53,6 +55,7 @@ class ReaderActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var db: AppDatabase
|
||||
private lateinit var prefs: SharedPreferences
|
||||
private lateinit var binding: ActivityReaderBinding
|
||||
|
||||
private var activeAlignment: Int = 1
|
||||
val JUSTIFY = 1
|
||||
@ -75,21 +78,23 @@ class ReaderActivity : AppCompatActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityReaderBinding.inflate(layoutInflater)
|
||||
val view = binding.root
|
||||
|
||||
setContentView(R.layout.activity_reader)
|
||||
setContentView(view)
|
||||
|
||||
db = Room.databaseBuilder(
|
||||
applicationContext,
|
||||
AppDatabase::class.java, "selfoss-database"
|
||||
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).build()
|
||||
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).addMigrations(MIGRATION_3_4).build()
|
||||
|
||||
val scoop = Scoop.getInstance()
|
||||
scoop.bind(this, Toppings.PRIMARY.value, toolBar)
|
||||
scoop.bind(this, Toppings.PRIMARY.value, binding.toolBar)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
|
||||
}
|
||||
|
||||
setSupportActionBar(toolBar)
|
||||
setSupportActionBar(binding.toolBar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||
|
||||
@ -99,7 +104,7 @@ class ReaderActivity : AppCompatActivity() {
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
editor = prefs.edit()
|
||||
|
||||
userIdentifier = prefs.getString("unique_id", "")
|
||||
userIdentifier = prefs.getString("unique_id", "")!!
|
||||
markOnScroll = prefs.getBoolean("mark_on_scroll", false)
|
||||
activeAlignment = prefs.getInt("text_align", JUSTIFY)
|
||||
|
||||
@ -107,7 +112,7 @@ class ReaderActivity : AppCompatActivity() {
|
||||
this,
|
||||
this@ReaderActivity,
|
||||
settings.getBoolean("isSelfSignedCert", false),
|
||||
prefs.getString("api_timeout", "-1").toLong()
|
||||
prefs.getString("api_timeout", "-1")!!.toLong()
|
||||
)
|
||||
|
||||
if (allItems.isEmpty()) {
|
||||
@ -118,9 +123,9 @@ class ReaderActivity : AppCompatActivity() {
|
||||
|
||||
readItem(allItems[currentItem])
|
||||
|
||||
pager.adapter =
|
||||
binding.pager.adapter =
|
||||
ScreenSlidePagerAdapter(supportFragmentManager, AppColors(this@ReaderActivity))
|
||||
pager.currentItem = currentItem
|
||||
binding.pager.currentItem = currentItem
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
@ -128,10 +133,10 @@ class ReaderActivity : AppCompatActivity() {
|
||||
|
||||
notifyAdapter()
|
||||
|
||||
pager.setPageTransformer(true, DepthPageTransformer())
|
||||
(indicator as CircleIndicator).setViewPager(pager)
|
||||
binding.pager.setPageTransformer(true, DepthPageTransformer())
|
||||
(binding.indicator as CircleIndicator).setViewPager(binding.pager)
|
||||
|
||||
pager.addOnPageChangeListener(
|
||||
binding.pager.addOnPageChangeListener(
|
||||
object : ViewPager.SimpleOnPageChangeListener() {
|
||||
|
||||
override fun onPageSelected(position: Int) {
|
||||
@ -141,7 +146,7 @@ class ReaderActivity : AppCompatActivity() {
|
||||
} else {
|
||||
canFavorite()
|
||||
}
|
||||
readItem(allItems[pager.currentItem])
|
||||
readItem(allItems[binding.pager.currentItem])
|
||||
}
|
||||
}
|
||||
)
|
||||
@ -180,19 +185,19 @@ class ReaderActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun notifyAdapter() {
|
||||
(pager.adapter as ScreenSlidePagerAdapter).notifyDataSetChanged()
|
||||
(binding.pager.adapter as ScreenSlidePagerAdapter).notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
if (markOnScroll) {
|
||||
pager.clearOnPageChangeListeners()
|
||||
binding.pager.clearOnPageChangeListeners()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(oldInstanceState: Bundle) {
|
||||
super.onSaveInstanceState(oldInstanceState)
|
||||
oldInstanceState!!.clear()
|
||||
oldInstanceState.clear()
|
||||
}
|
||||
|
||||
private inner class ScreenSlidePagerAdapter(fm: FragmentManager, val appColors: AppColors) :
|
||||
@ -244,14 +249,14 @@ class ReaderActivity : AppCompatActivity() {
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
fun afterSave() {
|
||||
allItems[pager.currentItem] =
|
||||
allItems[pager.currentItem].toggleStar()
|
||||
allItems[binding.pager.currentItem] =
|
||||
allItems[binding.pager.currentItem].toggleStar()
|
||||
notifyAdapter()
|
||||
canRemoveFromFavorite()
|
||||
}
|
||||
|
||||
fun afterUnsave() {
|
||||
allItems[pager.currentItem] = allItems[pager.currentItem].toggleStar()
|
||||
allItems[binding.pager.currentItem] = allItems[binding.pager.currentItem].toggleStar()
|
||||
notifyAdapter()
|
||||
canFavorite()
|
||||
}
|
||||
@ -263,7 +268,7 @@ class ReaderActivity : AppCompatActivity() {
|
||||
}
|
||||
R.id.save -> {
|
||||
if (this@ReaderActivity.isNetworkAccessible(null)) {
|
||||
api.starrItem(allItems[pager.currentItem].id)
|
||||
api.starrItem(allItems[binding.pager.currentItem].id)
|
||||
.enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<SuccessResponse>,
|
||||
@ -285,14 +290,14 @@ class ReaderActivity : AppCompatActivity() {
|
||||
})
|
||||
} else {
|
||||
thread {
|
||||
db.actionsDao().insertAllActions(ActionEntity(allItems[pager.currentItem].id, false, false, true, false))
|
||||
db.actionsDao().insertAllActions(ActionEntity(allItems[binding.pager.currentItem].id, false, false, true, false))
|
||||
afterSave()
|
||||
}
|
||||
}
|
||||
}
|
||||
R.id.unsave -> {
|
||||
if (this@ReaderActivity.isNetworkAccessible(null)) {
|
||||
api.unstarrItem(allItems[pager.currentItem].id)
|
||||
api.unstarrItem(allItems[binding.pager.currentItem].id)
|
||||
.enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
call: Call<SuccessResponse>,
|
||||
@ -314,7 +319,7 @@ class ReaderActivity : AppCompatActivity() {
|
||||
})
|
||||
} else {
|
||||
thread {
|
||||
db.actionsDao().insertAllActions(ActionEntity(allItems[pager.currentItem].id, false, false, false, true))
|
||||
db.actionsDao().insertAllActions(ActionEntity(allItems[binding.pager.currentItem].id, false, false, false, true))
|
||||
afterUnsave()
|
||||
}
|
||||
}
|
||||
|
@ -12,12 +12,13 @@ import android.widget.Toast
|
||||
import apps.amine.bou.readerforselfoss.adapters.SourcesListAdapter
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Source
|
||||
import apps.amine.bou.readerforselfoss.databinding.ActivityImageBinding
|
||||
import apps.amine.bou.readerforselfoss.databinding.ActivitySourcesBinding
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.themes.Toppings
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
||||
import com.ftinc.scoop.Scoop
|
||||
import kotlinx.android.synthetic.main.activity_sources.*
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
@ -25,31 +26,34 @@ import retrofit2.Response
|
||||
class SourcesActivity : AppCompatActivity() {
|
||||
|
||||
private lateinit var appColors: AppColors
|
||||
private lateinit var binding: ActivitySourcesBinding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
appColors = AppColors(this@SourcesActivity)
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivitySourcesBinding.inflate(layoutInflater)
|
||||
val view = binding.root
|
||||
|
||||
setContentView(R.layout.activity_sources)
|
||||
setContentView(view)
|
||||
|
||||
val scoop = Scoop.getInstance()
|
||||
scoop.bind(this, Toppings.PRIMARY.value, toolbar)
|
||||
scoop.bind(this, Toppings.PRIMARY.value, binding.toolbar)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
scoop.bindStatusBar(this, Toppings.PRIMARY_DARK.value)
|
||||
}
|
||||
|
||||
setSupportActionBar(toolbar)
|
||||
setSupportActionBar(binding.toolbar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||
|
||||
fab.rippleColor = appColors.colorAccentDark
|
||||
fab.backgroundTintList = ColorStateList.valueOf(appColors.colorAccent)
|
||||
binding.fab.rippleColor = appColors.colorAccentDark
|
||||
binding.fab.backgroundTintList = ColorStateList.valueOf(appColors.colorAccent)
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
recyclerView.clearOnScrollListeners()
|
||||
binding.recyclerView.clearOnScrollListeners()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
@ -64,12 +68,12 @@ class SourcesActivity : AppCompatActivity() {
|
||||
this,
|
||||
this@SourcesActivity,
|
||||
settings.getBoolean("isSelfSignedCert", false),
|
||||
prefs.getString("api_timeout", "-1").toLong()
|
||||
prefs.getString("api_timeout", "-1")!!.toLong()
|
||||
)
|
||||
var items: ArrayList<Source> = ArrayList()
|
||||
|
||||
recyclerView.setHasFixedSize(true)
|
||||
recyclerView.layoutManager = mLayoutManager
|
||||
binding.recyclerView.setHasFixedSize(true)
|
||||
binding.recyclerView.layoutManager = mLayoutManager
|
||||
|
||||
if (this@SourcesActivity.isNetworkAccessible(this@SourcesActivity.findViewById(R.id.recyclerView))) {
|
||||
api.sources.enqueue(object : Callback<List<Source>> {
|
||||
@ -81,7 +85,7 @@ class SourcesActivity : AppCompatActivity() {
|
||||
items = response.body() as ArrayList<Source>
|
||||
}
|
||||
val mAdapter = SourcesListAdapter(this@SourcesActivity, items, api)
|
||||
recyclerView.adapter = mAdapter
|
||||
binding.recyclerView.adapter = mAdapter
|
||||
mAdapter.notifyDataSetChanged()
|
||||
if (items.isEmpty()) {
|
||||
Toast.makeText(
|
||||
@ -102,7 +106,7 @@ class SourcesActivity : AppCompatActivity() {
|
||||
})
|
||||
}
|
||||
|
||||
fab.setOnClickListener {
|
||||
binding.fab.setOnClickListener {
|
||||
startActivity(Intent(this@SourcesActivity, AddSourceActivity::class.java))
|
||||
}
|
||||
}
|
||||
|
@ -9,10 +9,12 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView.ScaleType
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.databinding.CardItemBinding
|
||||
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
@ -33,7 +35,6 @@ import com.amulyakhare.textdrawable.util.ColorGenerator
|
||||
import com.bumptech.glide.Glide
|
||||
import com.like.LikeButton
|
||||
import com.like.OnLikeListener
|
||||
import kotlinx.android.synthetic.main.card_item.view.*
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
@ -59,68 +60,79 @@ class ItemCardAdapter(
|
||||
c.resources.getDimension(R.dimen.card_image_max_height).toInt()
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val v = LayoutInflater.from(c).inflate(R.layout.card_item, parent, false) as CardView
|
||||
return ViewHolder(v)
|
||||
val binding = CardItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
return ViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val itm = items[position]
|
||||
with(holder) {
|
||||
val itm = items[position]
|
||||
|
||||
|
||||
holder.mView.favButton.isLiked = itm.starred
|
||||
holder.mView.title.text = itm.getTitleDecoded()
|
||||
holder.mView.title.setOnTouchListener(LinkOnTouchListener())
|
||||
binding.favButton.isLiked = itm.starred
|
||||
binding.title.text = itm.getTitleDecoded()
|
||||
binding.title.setTextColor(ContextCompat.getColor(
|
||||
c,
|
||||
appColors.textColor
|
||||
))
|
||||
binding.title.setOnTouchListener(LinkOnTouchListener())
|
||||
|
||||
holder.mView.title.setLinkTextColor(appColors.colorAccent)
|
||||
binding.title.setLinkTextColor(appColors.colorAccent)
|
||||
|
||||
holder.mView.sourceTitleAndDate.text = itm.sourceAndDateText()
|
||||
binding.sourceTitleAndDate.text = itm.sourceAndDateText()
|
||||
|
||||
if (!fullHeightCards) {
|
||||
holder.mView.itemImage.maxHeight = imageMaxHeight
|
||||
holder.mView.itemImage.scaleType = ScaleType.CENTER_CROP
|
||||
binding.sourceTitleAndDate.setTextColor(ContextCompat.getColor(
|
||||
c,
|
||||
appColors.textColor
|
||||
))
|
||||
|
||||
if (!fullHeightCards) {
|
||||
binding.itemImage.maxHeight = imageMaxHeight
|
||||
binding.itemImage.scaleType = ScaleType.CENTER_CROP
|
||||
}
|
||||
|
||||
if (itm.getThumbnail(c).isEmpty()) {
|
||||
binding.itemImage.visibility = View.GONE
|
||||
Glide.with(c).clear(binding.itemImage)
|
||||
binding.itemImage.setImageDrawable(null)
|
||||
} else {
|
||||
binding.itemImage.visibility = View.VISIBLE
|
||||
c.bitmapCenterCrop(config, itm.getThumbnail(c), binding.itemImage)
|
||||
}
|
||||
|
||||
if (itm.getIcon(c).isEmpty()) {
|
||||
val color = generator.getColor(itm.getSourceTitle())
|
||||
|
||||
val drawable =
|
||||
TextDrawable
|
||||
.builder()
|
||||
.round()
|
||||
.build(itm.getSourceTitle().toTextDrawableString(c), color)
|
||||
binding.sourceImage.setImageDrawable(drawable)
|
||||
} else {
|
||||
c.circularBitmapDrawable(config, itm.getIcon(c), binding.sourceImage)
|
||||
}
|
||||
|
||||
binding.favButton.isLiked = itm.starred
|
||||
}
|
||||
|
||||
if (itm.getThumbnail(c).isEmpty()) {
|
||||
holder.mView.itemImage.visibility = View.GONE
|
||||
Glide.with(c).clear(holder.mView.itemImage)
|
||||
holder.mView.itemImage.setImageDrawable(null)
|
||||
} else {
|
||||
holder.mView.itemImage.visibility = View.VISIBLE
|
||||
c.bitmapCenterCrop(config, itm.getThumbnail(c), holder.mView.itemImage)
|
||||
}
|
||||
|
||||
if (itm.getIcon(c).isEmpty()) {
|
||||
val color = generator.getColor(itm.sourcetitle)
|
||||
|
||||
val drawable =
|
||||
TextDrawable
|
||||
.builder()
|
||||
.round()
|
||||
.build(itm.sourcetitle.toTextDrawableString(c), color)
|
||||
holder.mView.sourceImage.setImageDrawable(drawable)
|
||||
} else {
|
||||
c.circularBitmapDrawable(config, itm.getIcon(c), holder.mView.sourceImage)
|
||||
}
|
||||
|
||||
holder.mView.favButton.isLiked = itm.starred
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return items.size
|
||||
}
|
||||
|
||||
inner class ViewHolder(val mView: CardView) : RecyclerView.ViewHolder(mView) {
|
||||
inner class ViewHolder(val binding: CardItemBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
init {
|
||||
mView.setCardBackgroundColor(appColors.cardBackgroundColor)
|
||||
binding.root.setCardBackgroundColor(appColors.cardBackgroundColor)
|
||||
handleClickListeners()
|
||||
handleCustomTabActions()
|
||||
}
|
||||
|
||||
private fun handleClickListeners() {
|
||||
|
||||
mView.favButton.setOnLikeListener(object : OnLikeListener {
|
||||
binding.favButton.setOnLikeListener(object : OnLikeListener {
|
||||
override fun liked(likeButton: LikeButton) {
|
||||
val (id) = items[adapterPosition]
|
||||
val (id) = items[bindingAdapterPosition]
|
||||
if (c.isNetworkAccessible(null)) {
|
||||
api.starrItem(id).enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
@ -133,7 +145,7 @@ class ItemCardAdapter(
|
||||
call: Call<SuccessResponse>,
|
||||
t: Throwable
|
||||
) {
|
||||
mView.favButton.isLiked = false
|
||||
binding.favButton.isLiked = false
|
||||
Toast.makeText(
|
||||
c,
|
||||
R.string.cant_mark_favortie,
|
||||
@ -149,7 +161,7 @@ class ItemCardAdapter(
|
||||
}
|
||||
|
||||
override fun unLiked(likeButton: LikeButton) {
|
||||
val (id) = items[adapterPosition]
|
||||
val (id) = items[bindingAdapterPosition]
|
||||
if (c.isNetworkAccessible(null)) {
|
||||
api.unstarrItem(id).enqueue(object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
@ -162,7 +174,7 @@ class ItemCardAdapter(
|
||||
call: Call<SuccessResponse>,
|
||||
t: Throwable
|
||||
) {
|
||||
mView.favButton.isLiked = true
|
||||
binding.favButton.isLiked = true
|
||||
Toast.makeText(
|
||||
c,
|
||||
R.string.cant_unmark_favortie,
|
||||
@ -178,13 +190,13 @@ class ItemCardAdapter(
|
||||
}
|
||||
})
|
||||
|
||||
mView.shareBtn.setOnClickListener {
|
||||
val item = items[adapterPosition]
|
||||
binding.shareBtn.setOnClickListener {
|
||||
val item = items[bindingAdapterPosition]
|
||||
c.shareLink(item.getLinkDecoded(), item.getTitleDecoded())
|
||||
}
|
||||
|
||||
mView.browserBtn.setOnClickListener {
|
||||
c.openInBrowserAsNewTask(items[adapterPosition])
|
||||
binding.browserBtn.setOnClickListener {
|
||||
c.openInBrowserAsNewTask(items[bindingAdapterPosition])
|
||||
}
|
||||
}
|
||||
|
||||
@ -192,11 +204,11 @@ class ItemCardAdapter(
|
||||
val customTabsIntent = c.buildCustomTabsIntent()
|
||||
helper.bindCustomTabsService(app)
|
||||
|
||||
mView.setOnClickListener {
|
||||
binding.root.setOnClickListener {
|
||||
c.openItemUrl(
|
||||
items,
|
||||
adapterPosition,
|
||||
items[adapterPosition].getLinkDecoded(),
|
||||
bindingAdapterPosition,
|
||||
items[bindingAdapterPosition].getLinkDecoded(),
|
||||
customTabsIntent,
|
||||
internalBrowser,
|
||||
articleViewer,
|
||||
|
@ -13,10 +13,12 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.TextView
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.ContextCompat
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.databinding.ListItemBinding
|
||||
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
@ -34,7 +36,6 @@ import com.amulyakhare.textdrawable.TextDrawable
|
||||
import com.amulyakhare.textdrawable.util.ColorGenerator
|
||||
import com.like.LikeButton
|
||||
import com.like.OnLikeListener
|
||||
import kotlinx.android.synthetic.main.list_item.view.*
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
@ -58,49 +59,57 @@ class ItemListAdapter(
|
||||
private val c: Context = app.baseContext
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val v = LayoutInflater.from(c).inflate(
|
||||
R.layout.list_item,
|
||||
parent,
|
||||
false
|
||||
) as ConstraintLayout
|
||||
return ViewHolder(v)
|
||||
val binding = ListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
return ViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val itm = items[position]
|
||||
with(holder) {
|
||||
val itm = items[position]
|
||||
|
||||
|
||||
holder.mView.title.text = itm.getTitleDecoded()
|
||||
binding.title.text = itm.getTitleDecoded()
|
||||
|
||||
holder.mView.title.setOnTouchListener(LinkOnTouchListener())
|
||||
binding.title.setTextColor(ContextCompat.getColor(
|
||||
c,
|
||||
appColors.textColor
|
||||
))
|
||||
|
||||
holder.mView.title.setLinkTextColor(appColors.colorAccent)
|
||||
binding.title.setOnTouchListener(LinkOnTouchListener())
|
||||
|
||||
holder.mView.sourceTitleAndDate.text = itm.sourceAndDateText()
|
||||
binding.title.setLinkTextColor(appColors.colorAccent)
|
||||
|
||||
if (itm.getThumbnail(c).isEmpty()) {
|
||||
binding.sourceTitleAndDate.text = itm.sourceAndDateText()
|
||||
|
||||
if (itm.getIcon(c).isEmpty()) {
|
||||
val color = generator.getColor(itm.sourcetitle)
|
||||
binding.sourceTitleAndDate.setTextColor(ContextCompat.getColor(
|
||||
c,
|
||||
appColors.textColor
|
||||
))
|
||||
|
||||
val drawable =
|
||||
TextDrawable
|
||||
.builder()
|
||||
.round()
|
||||
.build(itm.sourcetitle.toTextDrawableString(c), color)
|
||||
if (itm.getThumbnail(c).isEmpty()) {
|
||||
|
||||
holder.mView.itemImage.setImageDrawable(drawable)
|
||||
if (itm.getIcon(c).isEmpty()) {
|
||||
val color = generator.getColor(itm.getSourceTitle())
|
||||
|
||||
val drawable =
|
||||
TextDrawable
|
||||
.builder()
|
||||
.round()
|
||||
.build(itm.getSourceTitle().toTextDrawableString(c), color)
|
||||
|
||||
binding.itemImage.setImageDrawable(drawable)
|
||||
} else {
|
||||
c.circularBitmapDrawable(config, itm.getIcon(c), binding.itemImage)
|
||||
}
|
||||
} else {
|
||||
c.circularBitmapDrawable(config, itm.getIcon(c), holder.mView.itemImage)
|
||||
c.bitmapCenterCrop(config, itm.getThumbnail(c), binding.itemImage)
|
||||
}
|
||||
} else {
|
||||
c.bitmapCenterCrop(config, itm.getThumbnail(c), holder.mView.itemImage)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = items.size
|
||||
|
||||
inner class ViewHolder(val mView: ConstraintLayout) : RecyclerView.ViewHolder(mView) {
|
||||
inner class ViewHolder(val binding: ListItemBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
init {
|
||||
handleCustomTabActions()
|
||||
@ -110,11 +119,11 @@ class ItemListAdapter(
|
||||
val customTabsIntent = c.buildCustomTabsIntent()
|
||||
helper.bindCustomTabsService(app)
|
||||
|
||||
mView.setOnClickListener {
|
||||
binding.root.setOnClickListener {
|
||||
c.openItemUrl(
|
||||
items,
|
||||
adapterPosition,
|
||||
items[adapterPosition].getLinkDecoded(),
|
||||
bindingAdapterPosition,
|
||||
items[bindingAdapterPosition].getLinkDecoded(),
|
||||
customTabsIntent,
|
||||
internalBrowser,
|
||||
articleViewer,
|
||||
|
@ -12,13 +12,13 @@ import apps.amine.bou.readerforselfoss.R
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Source
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.databinding.SourceListItemBinding
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import apps.amine.bou.readerforselfoss.utils.glide.circularBitmapDrawable
|
||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
||||
import apps.amine.bou.readerforselfoss.utils.toTextDrawableString
|
||||
import com.amulyakhare.textdrawable.TextDrawable
|
||||
import com.amulyakhare.textdrawable.util.ColorGenerator
|
||||
import kotlinx.android.synthetic.main.source_list_item.view.*
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
@ -31,14 +31,11 @@ class SourcesListAdapter(
|
||||
private val c: Context = app.baseContext
|
||||
private val generator: ColorGenerator = ColorGenerator.MATERIAL
|
||||
private lateinit var config: Config
|
||||
private lateinit var binding: SourceListItemBinding
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
val v = LayoutInflater.from(c).inflate(
|
||||
R.layout.source_list_item,
|
||||
parent,
|
||||
false
|
||||
) as ConstraintLayout
|
||||
return ViewHolder(v)
|
||||
binding = SourceListItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
|
||||
return ViewHolder(binding.root)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
@ -46,19 +43,19 @@ class SourcesListAdapter(
|
||||
config = Config(c)
|
||||
|
||||
if (itm.getIcon(c).isEmpty()) {
|
||||
val color = generator.getColor(itm.title)
|
||||
val color = generator.getColor(itm.getTitleDecoded())
|
||||
|
||||
val drawable =
|
||||
TextDrawable
|
||||
.builder()
|
||||
.round()
|
||||
.build(itm.title.toTextDrawableString(c), color)
|
||||
holder.mView.itemImage.setImageDrawable(drawable)
|
||||
.build(itm.getTitleDecoded().toTextDrawableString(c), color)
|
||||
binding.itemImage.setImageDrawable(drawable)
|
||||
} else {
|
||||
c.circularBitmapDrawable(config, itm.getIcon(c), holder.mView.itemImage)
|
||||
c.circularBitmapDrawable(config, itm.getIcon(c), binding.itemImage)
|
||||
}
|
||||
|
||||
holder.mView.sourceTitle.text = itm.title
|
||||
binding.sourceTitle.text = itm.getTitleDecoded()
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = items.size
|
||||
|
@ -28,17 +28,17 @@ class ParsedContent(
|
||||
}
|
||||
|
||||
constructor(source: Parcel) : this(
|
||||
title = source.readString(),
|
||||
title = source.readString().orEmpty(),
|
||||
content = source.readString(),
|
||||
date_published = source.readString(),
|
||||
date_published = source.readString().orEmpty(),
|
||||
lead_image_url = source.readString(),
|
||||
dek = source.readString(),
|
||||
url = source.readString(),
|
||||
domain = source.readString(),
|
||||
excerpt = source.readString(),
|
||||
dek = source.readString().orEmpty(),
|
||||
url = source.readString().orEmpty(),
|
||||
domain = source.readString().orEmpty(),
|
||||
excerpt = source.readString().orEmpty(),
|
||||
total_pages = source.readInt(),
|
||||
rendered_pages = source.readInt(),
|
||||
next_page_url = source.readString()
|
||||
next_page_url = source.readString().orEmpty()
|
||||
)
|
||||
|
||||
override fun describeContents() = 0
|
||||
|
@ -172,6 +172,15 @@ class SelfossApi(
|
||||
fun allItems(): Call<List<Item>> =
|
||||
service.allItems(userName, password)
|
||||
|
||||
fun allNewItems(): Call<List<Item>> =
|
||||
getItems("unread", null, null, null, 200, 0)
|
||||
|
||||
fun allReadItems(): Call<List<Item>> =
|
||||
getItems("read", null, null, null, 200, 0)
|
||||
|
||||
fun allStarredItems(): Call<List<Item>> =
|
||||
getItems("read", null, null, null, 200, 0)
|
||||
|
||||
private fun getItems(
|
||||
type: String,
|
||||
tag: String?,
|
||||
@ -206,6 +215,9 @@ class SelfossApi(
|
||||
fun update(): Call<String> =
|
||||
service.update(userName, password)
|
||||
|
||||
val apiVersion: Call<ApiVersion>
|
||||
get() = service.version()
|
||||
|
||||
val sources: Call<List<Source>>
|
||||
get() = service.sources(userName, password)
|
||||
|
||||
|
@ -48,6 +48,19 @@ data class Spout(
|
||||
@SerializedName("description") val description: String
|
||||
)
|
||||
|
||||
data class ApiVersion(
|
||||
@SerializedName("version") val version: String,
|
||||
@SerializedName("apiversion") val apiversion: String
|
||||
) {
|
||||
fun getApiMajorVersion() : Int {
|
||||
var versionNumber = 0
|
||||
if (apiversion != null) {
|
||||
versionNumber = apiversion.substringBefore(".").toInt()
|
||||
}
|
||||
return versionNumber
|
||||
}
|
||||
}
|
||||
|
||||
data class Source(
|
||||
@SerializedName("id") val id: String,
|
||||
@SerializedName("title") val title: String,
|
||||
@ -64,6 +77,10 @@ data class Source(
|
||||
}
|
||||
return constructUrl(config, "favicons", icon)
|
||||
}
|
||||
|
||||
fun getTitleDecoded(): String {
|
||||
return Html.fromHtml(title).toString()
|
||||
}
|
||||
}
|
||||
|
||||
data class Item(
|
||||
@ -90,17 +107,17 @@ data class Item(
|
||||
}
|
||||
|
||||
constructor(source: Parcel) : this(
|
||||
id = source.readString(),
|
||||
datetime = source.readString(),
|
||||
title = source.readString(),
|
||||
content = source.readString(),
|
||||
id = source.readString().orEmpty(),
|
||||
datetime = source.readString().orEmpty(),
|
||||
title = source.readString().orEmpty(),
|
||||
content = source.readString().orEmpty(),
|
||||
unread = 0.toByte() != source.readByte(),
|
||||
starred = 0.toByte() != source.readByte(),
|
||||
thumbnail = source.readString(),
|
||||
icon = source.readString(),
|
||||
link = source.readString(),
|
||||
sourcetitle = source.readString(),
|
||||
tags = source.readParcelable(ClassLoader.getSystemClassLoader())
|
||||
link = source.readString().orEmpty(),
|
||||
sourcetitle = source.readString().orEmpty(),
|
||||
tags = if (source.readParcelable<SelfossTagType>(ClassLoader.getSystemClassLoader()) != null) source.readParcelable(ClassLoader.getSystemClassLoader())!! else SelfossTagType("")
|
||||
)
|
||||
|
||||
override fun describeContents() = 0
|
||||
@ -137,7 +154,14 @@ data class Item(
|
||||
var allImages = ArrayList<String>()
|
||||
|
||||
for ( image in Jsoup.parse(content).getElementsByTag("img")) {
|
||||
allImages.add(image.attr("src"))
|
||||
val url = image.attr("src")
|
||||
if (url.toLowerCase().contains(".jpg") ||
|
||||
url.toLowerCase().contains(".jpeg") ||
|
||||
url.toLowerCase().contains(".png") ||
|
||||
url.toLowerCase().contains(".webp"))
|
||||
{
|
||||
allImages.add(url)
|
||||
}
|
||||
}
|
||||
return allImages
|
||||
}
|
||||
@ -145,7 +169,7 @@ data class Item(
|
||||
fun preloadImages(context: Context) : Boolean {
|
||||
val imageUrls = this.getImages()
|
||||
|
||||
val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL)
|
||||
val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL).timeout(10000)
|
||||
|
||||
|
||||
try {
|
||||
@ -153,7 +177,7 @@ data class Item(
|
||||
if ( URLUtil.isValidUrl(url)) {
|
||||
val image = Glide.with(context).asBitmap()
|
||||
.apply(glideOptions)
|
||||
.load(url).submit().get()
|
||||
.load(url).submit()
|
||||
}
|
||||
}
|
||||
} catch (e : Error) {
|
||||
@ -167,6 +191,10 @@ data class Item(
|
||||
return Html.fromHtml(title).toString()
|
||||
}
|
||||
|
||||
fun getSourceTitle(): String {
|
||||
return Html.fromHtml(sourcetitle).toString()
|
||||
}
|
||||
|
||||
// TODO: maybe find a better way to handle these kind of urls
|
||||
fun getLinkDecoded(): String {
|
||||
var stringUrl: String
|
||||
@ -208,7 +236,7 @@ data class SelfossTagType(val tags: String) : Parcelable {
|
||||
}
|
||||
|
||||
constructor(source: Parcel) : this(
|
||||
tags = source.readString()
|
||||
tags = source.readString().orEmpty()
|
||||
)
|
||||
|
||||
override fun describeContents() = 0
|
||||
|
@ -103,6 +103,9 @@ internal interface SelfossService {
|
||||
@Query("password") password: String
|
||||
): Call<List<Source>>
|
||||
|
||||
@GET("api/about")
|
||||
fun version(): Call<ApiVersion>
|
||||
|
||||
@DELETE("source/{id}")
|
||||
fun deleteSource(
|
||||
@Path("id") id: String,
|
||||
|
@ -19,6 +19,7 @@ import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity
|
||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_1_2
|
||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_2_3
|
||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_3_4
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import apps.amine.bou.readerforselfoss.utils.network.isNetworkAccessible
|
||||
import apps.amine.bou.readerforselfoss.utils.persistence.toEntity
|
||||
@ -56,16 +57,16 @@ class LoadingWorker(val context: Context, params: WorkerParameters) : Worker(con
|
||||
db = Room.databaseBuilder(
|
||||
applicationContext,
|
||||
AppDatabase::class.java, "selfoss-database"
|
||||
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).build()
|
||||
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).addMigrations(MIGRATION_3_4).build()
|
||||
|
||||
val api = SelfossApi(
|
||||
this.context,
|
||||
null,
|
||||
settings.getBoolean("isSelfSignedCert", false),
|
||||
sharedPref.getString("api_timeout", "-1").toLong()
|
||||
sharedPref.getString("api_timeout", "-1")!!.toLong()
|
||||
)
|
||||
|
||||
api.allItems().enqueue(object : Callback<List<Item>> {
|
||||
api.allNewItems().enqueue(object : Callback<List<Item>> {
|
||||
override fun onFailure(call: Call<List<Item>>, t: Throwable) {
|
||||
Timer("", false).schedule(4000) {
|
||||
notificationManager.cancel(1)
|
||||
@ -76,42 +77,38 @@ class LoadingWorker(val context: Context, params: WorkerParameters) : Worker(con
|
||||
call: Call<List<Item>>,
|
||||
response: Response<List<Item>>
|
||||
) {
|
||||
thread {
|
||||
if (response.body() != null) {
|
||||
val apiItems = (response.body() as ArrayList<Item>)
|
||||
db.itemsDao().deleteAllItems()
|
||||
db.itemsDao()
|
||||
.insertAllItems(*(apiItems.map { it.toEntity() }).toTypedArray())
|
||||
|
||||
val newSize = apiItems.filter { it.unread }.size
|
||||
if (notifyNewItems && newSize > 0) {
|
||||
|
||||
val intent = Intent(context, MainActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
}
|
||||
val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, 0)
|
||||
|
||||
val newItemsNotification = NotificationCompat.Builder(applicationContext, Config.newItemsChannelId)
|
||||
.setContentTitle(context.getString(R.string.new_items_notification_title))
|
||||
.setContentText(context.getString(R.string.new_items_notification_text, newSize))
|
||||
.setPriority(PRIORITY_DEFAULT)
|
||||
.setChannelId(Config.newItemsChannelId)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true)
|
||||
.setSmallIcon(R.drawable.ic_tab_fiber_new_black_24dp)
|
||||
|
||||
Timer("", false).schedule(4000) {
|
||||
notificationManager.notify(2, newItemsNotification.build())
|
||||
}
|
||||
}
|
||||
apiItems.map {it.preloadImages(context)}
|
||||
}
|
||||
Timer("", false).schedule(4000) {
|
||||
notificationManager.cancel(1)
|
||||
}
|
||||
}
|
||||
storeItems(response, true, notifyNewItems, notificationManager)
|
||||
}
|
||||
})
|
||||
api.allReadItems().enqueue(object : Callback<List<Item>> {
|
||||
override fun onFailure(call: Call<List<Item>>, t: Throwable) {
|
||||
Timer("", false).schedule(4000) {
|
||||
notificationManager.cancel(1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResponse(
|
||||
call: Call<List<Item>>,
|
||||
response: Response<List<Item>>
|
||||
) {
|
||||
storeItems(response, false, notifyNewItems, notificationManager)
|
||||
}
|
||||
})
|
||||
api.allStarredItems().enqueue(object : Callback<List<Item>> {
|
||||
override fun onFailure(call: Call<List<Item>>, t: Throwable) {
|
||||
Timer("", false).schedule(4000) {
|
||||
notificationManager.cancel(1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResponse(
|
||||
call: Call<List<Item>>,
|
||||
response: Response<List<Item>>
|
||||
) {
|
||||
storeItems(response, false, notifyNewItems, notificationManager)
|
||||
}
|
||||
})
|
||||
|
||||
thread {
|
||||
val actions = db.actionsDao().actions()
|
||||
|
||||
@ -131,6 +128,46 @@ class LoadingWorker(val context: Context, params: WorkerParameters) : Worker(con
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
private fun storeItems(response: Response<List<Item>>, newItems: Boolean, notifyNewItems: Boolean, notificationManager: NotificationManager) {
|
||||
thread {
|
||||
if (response.body() != null) {
|
||||
val apiItems = (response.body() as ArrayList<Item>)
|
||||
|
||||
if (newItems) {
|
||||
db.itemsDao().deleteAllItems()
|
||||
}
|
||||
db.itemsDao()
|
||||
.insertAllItems(*(apiItems.map { it.toEntity() }).toTypedArray())
|
||||
|
||||
val newSize = apiItems.filter { it.unread }.size
|
||||
if (newItems && notifyNewItems && newSize > 0) {
|
||||
|
||||
val intent = Intent(context, MainActivity::class.java).apply {
|
||||
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||
}
|
||||
val pendingIntent: PendingIntent = PendingIntent.getActivity(context, 0, intent, 0)
|
||||
|
||||
val newItemsNotification = NotificationCompat.Builder(applicationContext, Config.newItemsChannelId)
|
||||
.setContentTitle(context.getString(R.string.new_items_notification_title))
|
||||
.setContentText(context.getString(R.string.new_items_notification_text, newSize))
|
||||
.setPriority(PRIORITY_DEFAULT)
|
||||
.setChannelId(Config.newItemsChannelId)
|
||||
.setContentIntent(pendingIntent)
|
||||
.setAutoCancel(true)
|
||||
.setSmallIcon(R.drawable.ic_tab_fiber_new_black_24dp)
|
||||
|
||||
Timer("", false).schedule(4000) {
|
||||
notificationManager.notify(2, newItemsNotification.build())
|
||||
}
|
||||
}
|
||||
apiItems.map { it.preloadImages(context) }
|
||||
}
|
||||
Timer("", false).schedule(4000) {
|
||||
notificationManager.cancel(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> doAndReportOnFail(call: Call<T>, action: ActionEntity) {
|
||||
call.enqueue(object : Callback<T> {
|
||||
override fun onResponse(
|
||||
|
@ -29,10 +29,12 @@ import apps.amine.bou.readerforselfoss.api.mercury.ParsedContent
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossApi
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SuccessResponse
|
||||
import apps.amine.bou.readerforselfoss.databinding.FragmentArticleBinding
|
||||
import apps.amine.bou.readerforselfoss.persistence.database.AppDatabase
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.ActionEntity
|
||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_1_2
|
||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_2_3
|
||||
import apps.amine.bou.readerforselfoss.persistence.migrations.MIGRATION_3_4
|
||||
import apps.amine.bou.readerforselfoss.themes.AppColors
|
||||
import apps.amine.bou.readerforselfoss.utils.Config
|
||||
import apps.amine.bou.readerforselfoss.utils.buildCustomTabsIntent
|
||||
@ -49,7 +51,6 @@ import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import com.github.rubensousa.floatingtoolbar.FloatingToolbar
|
||||
import kotlinx.android.synthetic.main.fragment_article.view.*
|
||||
import retrofit2.Call
|
||||
import retrofit2.Callback
|
||||
import retrofit2.Response
|
||||
@ -76,8 +77,8 @@ class ArticleFragment : Fragment() {
|
||||
private lateinit var db: AppDatabase
|
||||
private lateinit var textAlignment: String
|
||||
private lateinit var config: Config
|
||||
|
||||
private var rootView: ViewGroup? = null
|
||||
private var _binding: FragmentArticleBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
|
||||
private lateinit var prefs: SharedPreferences
|
||||
|
||||
@ -93,18 +94,18 @@ class ArticleFragment : Fragment() {
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
appColors = AppColors(activity!!)
|
||||
config = Config(activity!!)
|
||||
appColors = AppColors(requireActivity())
|
||||
config = Config(requireActivity())
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
pageNumber = arguments!!.getInt(ARG_POSITION)
|
||||
allItems = arguments!!.getParcelableArrayList(ARG_ITEMS)
|
||||
pageNumber = requireArguments().getInt(ARG_POSITION)
|
||||
allItems = requireArguments().getParcelableArrayList<Item>(ARG_ITEMS) as ArrayList<Item>
|
||||
|
||||
db = Room.databaseBuilder(
|
||||
context!!,
|
||||
requireContext(),
|
||||
AppDatabase::class.java, "selfoss-database"
|
||||
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).build()
|
||||
).addMigrations(MIGRATION_1_2).addMigrations(MIGRATION_2_3).addMigrations(MIGRATION_3_4).build()
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
@ -113,27 +114,26 @@ class ArticleFragment : Fragment() {
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
try {
|
||||
rootView = inflater
|
||||
.inflate(R.layout.fragment_article, container, false) as ViewGroup
|
||||
_binding = FragmentArticleBinding.inflate(inflater, container, false)
|
||||
|
||||
url = allItems[pageNumber.toInt()].getLinkDecoded()
|
||||
contentText = allItems[pageNumber.toInt()].content
|
||||
contentTitle = allItems[pageNumber.toInt()].getTitleDecoded()
|
||||
contentImage = allItems[pageNumber.toInt()].getThumbnail(activity!!)
|
||||
contentImage = allItems[pageNumber.toInt()].getThumbnail(requireActivity())
|
||||
contentSource = allItems[pageNumber.toInt()].sourceAndDateText()
|
||||
allImages = allItems[pageNumber.toInt()].getImages()
|
||||
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(activity)
|
||||
editor = prefs.edit()
|
||||
fontSize = prefs.getString("reader_font_size", "16").toInt()
|
||||
fontSize = prefs.getString("reader_font_size", "16")!!.toInt()
|
||||
|
||||
font = prefs.getString("reader_font", "")
|
||||
font = prefs.getString("reader_font", "")!!
|
||||
if (font.isNotEmpty()) {
|
||||
resId = context!!.resources.getIdentifier(font, "font", context!!.packageName)
|
||||
resId = requireContext().resources.getIdentifier(font, "font", requireContext().packageName)
|
||||
typeface = try {
|
||||
ResourcesCompat.getFont(context!!, resId)!!
|
||||
ResourcesCompat.getFont(requireContext(), resId)!!
|
||||
} catch (e: java.lang.Exception) {
|
||||
// ACRA.getErrorReporter().maybeHandleSilentException(Throwable("Font loading issue: ${e.message}"), context!!)
|
||||
// ACRA.getErrorReporter().maybeHandleSilentException(Throwable("Font loading issue: ${e.message}"), requireContext())
|
||||
// Just to be sure
|
||||
null
|
||||
}
|
||||
@ -141,27 +141,27 @@ class ArticleFragment : Fragment() {
|
||||
|
||||
refreshAlignment()
|
||||
|
||||
val settings = activity!!.getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||
val settings = requireActivity().getSharedPreferences(Config.settingsName, Context.MODE_PRIVATE)
|
||||
|
||||
val api = SelfossApi(
|
||||
context!!,
|
||||
activity!!,
|
||||
requireContext(),
|
||||
requireActivity(),
|
||||
settings.getBoolean("isSelfSignedCert", false),
|
||||
prefs.getString("api_timeout", "-1").toLong()
|
||||
prefs.getString("api_timeout", "-1")!!.toLong()
|
||||
)
|
||||
|
||||
fab = rootView!!.fab
|
||||
fab = binding.fab
|
||||
|
||||
fab.backgroundTintList = ColorStateList.valueOf(appColors.colorAccent)
|
||||
|
||||
fab.rippleColor = appColors.colorAccentDark
|
||||
|
||||
val floatingToolbar: FloatingToolbar = rootView!!.floatingToolbar
|
||||
val floatingToolbar: FloatingToolbar = binding.floatingToolbar
|
||||
floatingToolbar.attachFab(fab)
|
||||
|
||||
floatingToolbar.background = ColorDrawable(appColors.colorAccent)
|
||||
|
||||
val customTabsIntent = activity!!.buildCustomTabsIntent()
|
||||
val customTabsIntent = requireActivity().buildCustomTabsIntent()
|
||||
mCustomTabActivityHelper = CustomTabActivityHelper()
|
||||
mCustomTabActivityHelper!!.bindCustomTabsService(activity)
|
||||
|
||||
@ -171,17 +171,17 @@ class ArticleFragment : Fragment() {
|
||||
override fun onItemClick(item: MenuItem) {
|
||||
when (item.itemId) {
|
||||
R.id.more_action -> getContentFromMercury(customTabsIntent, prefs)
|
||||
R.id.share_action -> activity!!.shareLink(url, contentTitle)
|
||||
R.id.open_action -> activity!!.openItemUrl(
|
||||
R.id.share_action -> requireActivity().shareLink(url, contentTitle)
|
||||
R.id.open_action -> requireActivity().openItemUrl(
|
||||
allItems,
|
||||
pageNumber.toInt(),
|
||||
url,
|
||||
customTabsIntent,
|
||||
false,
|
||||
false,
|
||||
activity!!
|
||||
requireActivity()
|
||||
)
|
||||
R.id.unread_action -> if ((context != null && context!!.isNetworkAccessible(null)) || context == null) {
|
||||
R.id.unread_action -> if ((context != null && requireContext().isNetworkAccessible(null)) || context == null) {
|
||||
api.unmarkItem(allItems[pageNumber.toInt()].id).enqueue(
|
||||
object : Callback<SuccessResponse> {
|
||||
override fun onResponse(
|
||||
@ -211,35 +211,35 @@ class ArticleFragment : Fragment() {
|
||||
}
|
||||
)
|
||||
|
||||
rootView!!.source.text = contentSource
|
||||
binding.source.text = contentSource
|
||||
if (typeface != null) {
|
||||
rootView!!.source.typeface = typeface
|
||||
binding.source.typeface = typeface
|
||||
}
|
||||
|
||||
if (contentText.isEmptyOrNullOrNullString()) {
|
||||
getContentFromMercury(customTabsIntent, prefs)
|
||||
} else {
|
||||
rootView!!.titleView.text = contentTitle
|
||||
binding.titleView.text = contentTitle
|
||||
if (typeface != null) {
|
||||
rootView!!.titleView.typeface = typeface
|
||||
binding.titleView.typeface = typeface
|
||||
}
|
||||
|
||||
htmlToWebview()
|
||||
|
||||
if (!contentImage.isEmptyOrNullOrNullString() && context != null) {
|
||||
rootView!!.imageView.visibility = View.VISIBLE
|
||||
binding.imageView.visibility = View.VISIBLE
|
||||
Glide
|
||||
.with(context!!)
|
||||
.with(requireContext())
|
||||
.asBitmap()
|
||||
.loadMaybeBasicAuth(config, contentImage)
|
||||
.apply(RequestOptions.fitCenterTransform())
|
||||
.into(rootView!!.imageView)
|
||||
.into(binding.imageView)
|
||||
} else {
|
||||
rootView!!.imageView.visibility = View.GONE
|
||||
binding.imageView.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
rootView!!.nestedScrollView.setOnScrollChangeListener(
|
||||
binding.nestedScrollView.setOnScrollChangeListener(
|
||||
NestedScrollView.OnScrollChangeListener { _, _, scrollY, _, oldScrollY ->
|
||||
if (scrollY > oldScrollY) {
|
||||
fab.hide()
|
||||
@ -250,22 +250,27 @@ class ArticleFragment : Fragment() {
|
||||
)
|
||||
|
||||
} catch (e: InflateException) {
|
||||
AlertDialog.Builder(context!!)
|
||||
.setMessage(context!!.getString(R.string.webview_dialog_issue_message))
|
||||
.setTitle(context!!.getString(R.string.webview_dialog_issue_title))
|
||||
AlertDialog.Builder(requireContext())
|
||||
.setMessage(requireContext().getString(R.string.webview_dialog_issue_message))
|
||||
.setTitle(requireContext().getString(R.string.webview_dialog_issue_title))
|
||||
.setPositiveButton(android.R.string.ok
|
||||
) { dialog, which ->
|
||||
val sharedPref = PreferenceManager.getDefaultSharedPreferences(context!!)
|
||||
val sharedPref = PreferenceManager.getDefaultSharedPreferences(requireContext())
|
||||
val editor = sharedPref.edit()
|
||||
editor.putBoolean("prefer_article_viewer", false)
|
||||
editor.commit()
|
||||
activity!!.finish()
|
||||
requireActivity().finish()
|
||||
}
|
||||
.create()
|
||||
.show()
|
||||
}
|
||||
|
||||
return rootView
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
private fun refreshAlignment() {
|
||||
@ -280,8 +285,8 @@ class ArticleFragment : Fragment() {
|
||||
customTabsIntent: CustomTabsIntent,
|
||||
prefs: SharedPreferences
|
||||
) {
|
||||
if ((context != null && context!!.isNetworkAccessible(null)) || context == null) {
|
||||
rootView!!.progressBar.visibility = View.VISIBLE
|
||||
if ((context != null && requireContext().isNetworkAccessible(null)) || context == null) {
|
||||
binding.progressBar.visibility = View.VISIBLE
|
||||
val parser = MercuryApi()
|
||||
|
||||
parser.parseUrl(url).enqueue(
|
||||
@ -294,9 +299,9 @@ class ArticleFragment : Fragment() {
|
||||
try {
|
||||
if (response.body() != null && response.body()!!.content != null && !response.body()!!.content.isNullOrEmpty()) {
|
||||
try {
|
||||
rootView!!.titleView.text = response.body()!!.title
|
||||
binding.titleView.text = response.body()!!.title
|
||||
if (typeface != null) {
|
||||
rootView!!.titleView.typeface = typeface
|
||||
binding.titleView.typeface = typeface
|
||||
}
|
||||
try {
|
||||
// Note: Mercury may return relative urls... If it does the url val will not be changed.
|
||||
@ -316,18 +321,18 @@ class ArticleFragment : Fragment() {
|
||||
|
||||
try {
|
||||
if (response.body()!!.lead_image_url != null && !response.body()!!.lead_image_url.isNullOrEmpty() && context != null) {
|
||||
rootView!!.imageView.visibility = View.VISIBLE
|
||||
binding.imageView.visibility = View.VISIBLE
|
||||
try {
|
||||
Glide
|
||||
.with(context!!)
|
||||
.with(requireContext())
|
||||
.asBitmap()
|
||||
.loadMaybeBasicAuth(config, response.body()!!.lead_image_url.orEmpty())
|
||||
.apply(RequestOptions.fitCenterTransform())
|
||||
.into(rootView!!.imageView)
|
||||
.into(binding.imageView)
|
||||
} catch (e: Exception) {
|
||||
}
|
||||
} else {
|
||||
rootView!!.imageView.visibility = View.GONE
|
||||
binding.imageView.visibility = View.GONE
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
if (context != null) {
|
||||
@ -335,9 +340,9 @@ class ArticleFragment : Fragment() {
|
||||
}
|
||||
|
||||
try {
|
||||
rootView!!.nestedScrollView.scrollTo(0, 0)
|
||||
binding.nestedScrollView.scrollTo(0, 0)
|
||||
|
||||
rootView!!.progressBar.visibility = View.GONE
|
||||
binding.progressBar.visibility = View.GONE
|
||||
} catch (e: Exception) {
|
||||
if (context != null) {
|
||||
}
|
||||
@ -369,32 +374,32 @@ class ArticleFragment : Fragment() {
|
||||
val stringColor = String.format("#%06X", 0xFFFFFF and appColors.colorAccent)
|
||||
|
||||
val attrs: IntArray = intArrayOf(android.R.attr.fontFamily)
|
||||
val a: TypedArray = context!!.obtainStyledAttributes(resId, attrs)
|
||||
val a: TypedArray = requireContext().obtainStyledAttributes(resId, attrs)
|
||||
|
||||
|
||||
rootView!!.webcontent.settings.standardFontFamily = a.getString(0)
|
||||
rootView!!.webcontent.visibility = View.VISIBLE
|
||||
binding.webcontent.settings.standardFontFamily = a.getString(0)
|
||||
binding.webcontent.visibility = View.VISIBLE
|
||||
val (textColor, backgroundColor) = if (appColors.isDarkTheme) {
|
||||
if (context != null) {
|
||||
rootView!!.webcontent.setBackgroundColor(
|
||||
binding.webcontent.setBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
context!!,
|
||||
requireContext(),
|
||||
R.color.dark_webview
|
||||
)
|
||||
)
|
||||
Pair(ContextCompat.getColor(context!!, R.color.dark_webview_text), ContextCompat.getColor(context!!, R.color.dark_webview))
|
||||
Pair(ContextCompat.getColor(requireContext(), R.color.dark_webview_text), ContextCompat.getColor(requireContext(), R.color.dark_webview))
|
||||
} else {
|
||||
Pair(null, null)
|
||||
}
|
||||
} else {
|
||||
if (context != null) {
|
||||
rootView!!.webcontent.setBackgroundColor(
|
||||
binding.webcontent.setBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
context!!,
|
||||
requireContext(),
|
||||
R.color.light_webview
|
||||
)
|
||||
)
|
||||
Pair(ContextCompat.getColor(context!!, R.color.light_webview_text), ContextCompat.getColor(context!!, R.color.light_webview))
|
||||
Pair(ContextCompat.getColor(requireContext(), R.color.light_webview_text), ContextCompat.getColor(requireContext(), R.color.light_webview))
|
||||
} else {
|
||||
Pair(null, null)
|
||||
}
|
||||
@ -412,14 +417,14 @@ class ArticleFragment : Fragment() {
|
||||
"#FFFFFF"
|
||||
}
|
||||
|
||||
rootView!!.webcontent.settings.useWideViewPort = true
|
||||
rootView!!.webcontent.settings.loadWithOverviewMode = true
|
||||
rootView!!.webcontent.settings.javaScriptEnabled = false
|
||||
binding.webcontent.settings.useWideViewPort = true
|
||||
binding.webcontent.settings.loadWithOverviewMode = true
|
||||
binding.webcontent.settings.javaScriptEnabled = false
|
||||
|
||||
rootView!!.webcontent.webViewClient = object : WebViewClient() {
|
||||
binding.webcontent.webViewClient = object : WebViewClient() {
|
||||
override fun shouldOverrideUrlLoading(view: WebView?, url : String): Boolean {
|
||||
if (rootView!!.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
|
||||
rootView!!.context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
|
||||
if (binding.webcontent.hitTestResult.type != WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
|
||||
requireContext().startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url)))
|
||||
}
|
||||
return true
|
||||
}
|
||||
@ -455,13 +460,13 @@ class ArticleFragment : Fragment() {
|
||||
}
|
||||
})
|
||||
|
||||
rootView!!.webcontent.setOnTouchListener { _, event -> gestureDetector.onTouchEvent(event)}
|
||||
binding.webcontent.setOnTouchListener { _, event -> gestureDetector.onTouchEvent(event)}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
rootView!!.webcontent.settings.layoutAlgorithm =
|
||||
binding.webcontent.settings.layoutAlgorithm =
|
||||
WebSettings.LayoutAlgorithm.TEXT_AUTOSIZING
|
||||
} else {
|
||||
rootView!!.webcontent.settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.SINGLE_COLUMN
|
||||
binding.webcontent.settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.SINGLE_COLUMN
|
||||
}
|
||||
|
||||
var baseUrl: String? = null
|
||||
@ -490,7 +495,7 @@ class ArticleFragment : Fragment() {
|
||||
""
|
||||
}
|
||||
|
||||
rootView!!.webcontent.loadDataWithBaseURL(
|
||||
binding.webcontent.loadDataWithBaseURL(
|
||||
baseUrl,
|
||||
"""<html>
|
||||
|<head>
|
||||
@ -543,15 +548,15 @@ class ArticleFragment : Fragment() {
|
||||
}
|
||||
|
||||
private fun openInBrowserAfterFailing(customTabsIntent: CustomTabsIntent) {
|
||||
rootView!!.progressBar.visibility = View.GONE
|
||||
activity!!.openItemUrl(
|
||||
binding.progressBar.visibility = View.GONE
|
||||
requireActivity().openItemUrl(
|
||||
allItems,
|
||||
pageNumber.toInt(),
|
||||
url,
|
||||
customTabsIntent,
|
||||
true,
|
||||
false,
|
||||
activity!!
|
||||
requireActivity()
|
||||
)
|
||||
}
|
||||
|
||||
@ -573,10 +578,10 @@ class ArticleFragment : Fragment() {
|
||||
}
|
||||
|
||||
fun performClick(): Boolean {
|
||||
if (rootView!!.webcontent.hitTestResult.type == WebView.HitTestResult.IMAGE_TYPE ||
|
||||
rootView!!.webcontent.hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
|
||||
if (binding.webcontent.hitTestResult.type == WebView.HitTestResult.IMAGE_TYPE ||
|
||||
binding.webcontent.hitTestResult.type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) {
|
||||
|
||||
val position : Int = allImages.indexOf(rootView!!.webcontent.hitTestResult.extra)
|
||||
val position : Int = allImages.indexOf(binding.webcontent.hitTestResult.extra)
|
||||
|
||||
val intent = Intent(activity, ImageActivity::class.java)
|
||||
intent.putExtra("allImages", allImages)
|
||||
|
@ -4,35 +4,43 @@ import android.os.Bundle
|
||||
import android.view.*
|
||||
import androidx.fragment.app.Fragment
|
||||
import apps.amine.bou.readerforselfoss.R
|
||||
import apps.amine.bou.readerforselfoss.databinding.FragmentImageBinding
|
||||
import com.bumptech.glide.Glide
|
||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||
import com.bumptech.glide.request.RequestOptions
|
||||
import kotlinx.android.synthetic.main.fragment_image.view.*
|
||||
|
||||
class ImageFragment : Fragment() {
|
||||
|
||||
private lateinit var imageUrl : String
|
||||
private val glideOptions = RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.ALL)
|
||||
private var _binding: FragmentImageBinding? = null
|
||||
private val binding get() = _binding
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
imageUrl = arguments!!.getString("imageUrl")
|
||||
imageUrl = requireArguments().getString("imageUrl")!!
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
val view : View = inflater.inflate(R.layout.fragment_image, container, false)
|
||||
_binding = FragmentImageBinding.inflate(inflater, container, false)
|
||||
val view = binding?.root
|
||||
|
||||
view.photoView.visibility = View.VISIBLE
|
||||
binding!!.photoView.visibility = View.VISIBLE
|
||||
Glide.with(activity)
|
||||
.asBitmap()
|
||||
.apply(glideOptions)
|
||||
.load(imageUrl)
|
||||
.into(view.photoView)
|
||||
.into(binding!!.photoView)
|
||||
|
||||
return view
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val ARG_IMAGE = "imageUrl"
|
||||
|
||||
|
@ -10,7 +10,7 @@ import apps.amine.bou.readerforselfoss.persistence.entities.ItemEntity
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.SourceEntity
|
||||
import apps.amine.bou.readerforselfoss.persistence.entities.TagEntity
|
||||
|
||||
@Database(entities = [TagEntity::class, SourceEntity::class, ItemEntity::class, ActionEntity::class], version = 3)
|
||||
@Database(entities = [TagEntity::class, SourceEntity::class, ItemEntity::class, ActionEntity::class], version = 4)
|
||||
abstract class AppDatabase : RoomDatabase() {
|
||||
abstract fun drawerDataDao(): DrawerDataDao
|
||||
|
||||
|
@ -14,3 +14,21 @@ val MIGRATION_2_3: Migration = object : Migration(2, 3) {
|
||||
database.execSQL("CREATE TABLE IF NOT EXISTS `actions` (`id` INTEGER NOT NULL, `articleid` TEXT NOT NULL, `read` INTEGER NOT NULL, `unread` INTEGER NOT NULL, `starred` INTEGER NOT NULL, `unstarred` INTEGER NOT NULL, PRIMARY KEY(`id`))")
|
||||
}
|
||||
}
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
@ -18,6 +18,7 @@ class AppColors(a: Activity) {
|
||||
@ColorInt val colorAccentDark: Int
|
||||
@ColorInt val cardBackgroundColor: Int
|
||||
@ColorInt val colorBackground: Int
|
||||
@ColorInt val textColor: Int
|
||||
val isDarkTheme: Boolean
|
||||
|
||||
init {
|
||||
@ -57,6 +58,12 @@ class AppColors(a: Activity) {
|
||||
android.R.color.background_light
|
||||
}
|
||||
|
||||
textColor = if (isDarkTheme) {
|
||||
R.color.md_white_1000
|
||||
} else {
|
||||
R.color.md_grey_900
|
||||
}
|
||||
|
||||
val wrapper = Context::class.java
|
||||
val method = wrapper!!.getMethod("getThemeResId")
|
||||
method.isAccessible = true
|
||||
|
@ -11,19 +11,19 @@ class Config(c: Context) {
|
||||
val settings: SharedPreferences = c.getSharedPreferences(settingsName, Context.MODE_PRIVATE)
|
||||
|
||||
val baseUrl: String
|
||||
get() = settings.getString("url", "")
|
||||
get() = settings.getString("url", "")!!
|
||||
|
||||
val userLogin: String
|
||||
get() = settings.getString("login", "")
|
||||
get() = settings.getString("login", "")!!
|
||||
|
||||
val userPassword: String
|
||||
get() = settings.getString("password", "")
|
||||
get() = settings.getString("password", "")!!
|
||||
|
||||
val httpUserLogin: String
|
||||
get() = settings.getString("httpUserName", "")
|
||||
get() = settings.getString("httpUserName", "")!!
|
||||
|
||||
val httpUserPassword: String
|
||||
get() = settings.getString("httpPassword", "")
|
||||
get() = settings.getString("httpPassword", "")!!
|
||||
|
||||
companion object {
|
||||
const val settingsName = "paramsselfoss"
|
||||
|
@ -4,6 +4,7 @@ import android.content.Context
|
||||
import android.text.format.DateUtils
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.Item
|
||||
import apps.amine.bou.readerforselfoss.api.selfoss.SelfossTagType
|
||||
import apps.amine.bou.readerforselfoss.dateTimeFormatter
|
||||
import java.text.ParseException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
@ -22,7 +23,7 @@ fun String.toTextDrawableString(c: Context): String {
|
||||
fun Item.sourceAndDateText(): String {
|
||||
val formattedDate: String = try {
|
||||
" " + DateUtils.getRelativeTimeSpanString(
|
||||
SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(this.datetime).time,
|
||||
SimpleDateFormat(dateTimeFormatter).parse(this.datetime).time,
|
||||
Date().time,
|
||||
DateUtils.MINUTE_IN_MILLIS,
|
||||
DateUtils.FORMAT_ABBREV_RELATIVE
|
||||
@ -32,7 +33,7 @@ fun Item.sourceAndDateText(): String {
|
||||
""
|
||||
}
|
||||
|
||||
return this.sourcetitle + formattedDate
|
||||
return this.getSourceTitle() + formattedDate
|
||||
}
|
||||
|
||||
fun Item.toggleStar(): Item {
|
||||
|
@ -28,7 +28,7 @@ fun SourceEntity.toView(): Source =
|
||||
fun Source.toEntity(): SourceEntity =
|
||||
SourceEntity(
|
||||
this.id,
|
||||
this.title,
|
||||
this.getTitleDecoded(),
|
||||
this.tags.tags,
|
||||
this.spout,
|
||||
this.error,
|
||||
@ -68,6 +68,6 @@ fun Item.toEntity(): ItemEntity =
|
||||
this.thumbnail,
|
||||
this.icon,
|
||||
this.link,
|
||||
this.sourcetitle,
|
||||
this.getSourceTitle(),
|
||||
this.tags.tags
|
||||
)
|
@ -139,6 +139,8 @@
|
||||
<string name="pref_switch_items_caching_off">Els articles no es guardaran a la memòria del dispositiu i l\'aplicació no es podrà utilitzar sense connexió.</string>
|
||||
<string name="pref_switch_items_caching_on">Els articles es guardaran a la memòria del dispositiu i es podran utilitzar sense connexió.</string>
|
||||
<string name="pref_switch_items_caching">Guarda els elements per utilitzar-los sense connexió</string>
|
||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
||||
<string name="no_network_connectivity">Sense connexió!</string>
|
||||
<string name="pref_switch_periodic_refresh">Sincronitza els articles</string>
|
||||
<string name="pref_switch_periodic_refresh_off">Els articles no se sincronitzaran en segon pla</string>
|
||||
|
@ -139,6 +139,8 @@
|
||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
||||
<string name="no_network_connectivity">Nicht verbunden !</string>
|
||||
<string name="pref_switch_periodic_refresh">Synchronisiere Artikel</string>
|
||||
<string name="pref_switch_periodic_refresh_off">Artikel werden nicht im Hintergrund synchronisiert</string>
|
||||
|
@ -139,6 +139,8 @@
|
||||
<string name="pref_switch_items_caching_off">Los artículos no se guardarán en la memoria del dispositivo y la aplicación no se podrá utilizar sin conexión.</string>
|
||||
<string name="pref_switch_items_caching_on">Los artículos se guardarán en la memoria del dispositivo y se utilizarán para el uso sin conexión.</string>
|
||||
<string name="pref_switch_items_caching">Guardar elementos para uso sin conexión</string>
|
||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
||||
<string name="no_network_connectivity">Sin conexión!</string>
|
||||
<string name="pref_switch_periodic_refresh">Sincronizar artículos</string>
|
||||
<string name="pref_switch_periodic_refresh_off">Los artículos no se sincronizarán en segundo plano</string>
|
||||
|
@ -139,6 +139,8 @@
|
||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
||||
<string name="no_network_connectivity">Not connected !</string>
|
||||
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||
|
@ -139,6 +139,8 @@
|
||||
<string name="pref_switch_items_caching_off">Les articles ne seront pas enregistrés et l\'application ne sera pas utilisable hors ligne.</string>
|
||||
<string name="pref_switch_items_caching_on">Les articles seront enregistrés et l\'application sera utilisable hors ligne.</string>
|
||||
<string name="pref_switch_items_caching">Sauvegarder les articles pour une utilisation hors ligne</string>
|
||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
||||
<string name="no_network_connectivity">Hors connexion !</string>
|
||||
<string name="pref_switch_periodic_refresh">Synchroniser les articles</string>
|
||||
<string name="pref_switch_periodic_refresh_off">Les articles ne seront pas synchronisés en arrière plan</string>
|
||||
|
@ -139,6 +139,8 @@
|
||||
<string name="pref_switch_items_caching_off">Os artigos non se gardaran na memoria do dispositivo e non se poderá utilizar a aplicación sen conexión.</string>
|
||||
<string name="pref_switch_items_caching_on">Os artigos gardaranse na memoria do dispositivo e estarán dispoñibles sen conexión.</string>
|
||||
<string name="pref_switch_items_caching">Gardar elementos para uso sen conexión</string>
|
||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
||||
<string name="no_network_connectivity">Non conectado!</string>
|
||||
<string name="pref_switch_periodic_refresh">Sincronizar artigos</string>
|
||||
<string name="pref_switch_periodic_refresh_off">Os artigos non se sincronizarán coa aplicación de fondo</string>
|
||||
|
@ -139,6 +139,8 @@
|
||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
||||
<string name="no_network_connectivity">Not connected !</string>
|
||||
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||
|
@ -139,6 +139,8 @@
|
||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
||||
<string name="no_network_connectivity">Not connected !</string>
|
||||
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||
|
@ -139,6 +139,8 @@
|
||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
||||
<string name="no_network_connectivity">Not connected !</string>
|
||||
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||
|
@ -139,6 +139,8 @@
|
||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
||||
<string name="no_network_connectivity">Not connected !</string>
|
||||
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||
|
@ -139,6 +139,8 @@
|
||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
||||
<string name="no_network_connectivity">Not connected !</string>
|
||||
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||
|
@ -139,6 +139,8 @@
|
||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
||||
<string name="no_network_connectivity">Not connected !</string>
|
||||
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||
|
165
app/src/main/res/values-si-rLK/strings.xml
Normal file
@ -0,0 +1,165 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<string name="app_name">"Reader for Selfoss"</string>
|
||||
<string name="title_activity_login">"පිවිසෙන්න"</string>
|
||||
<string name="prompt_password">"මුර පදය"</string>
|
||||
<string name="prompt_http_password">"HTTP Password"</string>
|
||||
<string name="action_sign_in">"Go"</string>
|
||||
<string name="error_invalid_password">"Password not long enough"</string>
|
||||
<string name="error_field_required">"Field required"</string>
|
||||
<string name="prompt_url">"Url"</string>
|
||||
<string name="withLoginSwitch">"Login required ?"</string>
|
||||
<string name="withHttpLoginSwitch">"HTTP Login required ?"</string>
|
||||
<string name="login_url_problem">"Oops. You may need to add a \"/\" at the end of the url."</string>
|
||||
<string name="prompt_login">"පරිශීලක නාමය"</string>
|
||||
<string name="prompt_http_login">"HTTP Username"</string>
|
||||
<string name="label_share">"Share"</string>
|
||||
<string name="readAll">"Read all"</string>
|
||||
<string name="action_disconnect">"Disconnect"</string>
|
||||
<string name="title_activity_settings">"සැකසුම්"</string>
|
||||
<string name="pref_header_general">"General"</string>
|
||||
<string name="add_source_hint_tags">"Tag1, Tag2, Tag3"</string>
|
||||
<string name="add_source_hint_url">"Link"</string>
|
||||
<string name="add_source_hint_name">"නම"</string>
|
||||
<string name="add_source">"Add a source"</string>
|
||||
<string name="add_source_save">"සුරකින්න"</string>
|
||||
<string name="wrong_infos">"Check your details again."</string>
|
||||
<string name="all_posts_not_read">"All posts weren't read"</string>
|
||||
<string name="all_posts_read">"All posts were read"</string>
|
||||
<string name="cant_get_favs">"Can't get favorites"</string>
|
||||
<string name="cant_get_new_elements">"Can't get new articles"</string>
|
||||
<string name="cant_get_read">"Can't get read articles"</string>
|
||||
<string name="nothing_here">"Nothing here"</string>
|
||||
<string name="tab_new">"New"</string>
|
||||
<string name="tab_read">"සියල්ල"</string>
|
||||
<string name="tab_favs">"Favorites"</string>
|
||||
<string name="action_about">"මේ ගැන"</string>
|
||||
<string name="marked_as_read">"Item read"</string>
|
||||
<string name="marked_as_unread">"Item unread"</string>
|
||||
<string name="undo_string">"Undo"</string>
|
||||
<string name="addStringNoUrl">"Log in to add sources."</string>
|
||||
<string name="cant_get_sources">"Can't get sources list."</string>
|
||||
<string name="cant_create_source">"Can't create source."</string>
|
||||
<string name="cant_get_spouts">"Can't get spouts list."</string>
|
||||
<string name="form_not_complete">"The form is not complete"</string>
|
||||
<string name="pref_header_links">"Links"</string>
|
||||
<string name="issue_tracker_link">"Issue Tracker"</string>
|
||||
<string name="issue_tracker_summary">"Report a bug or ask for a new feature"</string>
|
||||
<string name="warning_wrong_url">"WARNING"</string>
|
||||
<string name="pref_switch_card_view_title">"Card View"</string>
|
||||
<string name="cant_mark_favortie">"Can't mark article as favorite"</string>
|
||||
<string name="cant_unmark_favortie">"Can't remove item from favorite"</string>
|
||||
<string name="share">"Share"</string>
|
||||
<string name="rating_prompt_title">"Enjoying the app ?"</string>
|
||||
<string name="rating_prompt_yes">"Yes !"</string>
|
||||
<string name="rating_prompt_no">"Not really …"</string>
|
||||
<string name="rating_prompt_feedback_title">"Can you tell us why ?"</string>
|
||||
<string name="rating_prompt_feedback_yes">"OK !"</string>
|
||||
<string name="rating_prompt_feedback_no">"Not now."</string>
|
||||
<string name="rating_prompt_rating_title">"Great ! Can you rate us on the Store ?"</string>
|
||||
<string name="rating_prompt_rating_yes">"Sure !"</string>
|
||||
<string name="rating_prompt_rating_no">"Not right now."</string>
|
||||
<string name="rating_prompt_thanks">"Thanks, your feedback help enhance the app !"</string>
|
||||
<string name="switch_unread_count">"Display the unread count as a badge for the bottom bar."</string>
|
||||
<string name="switch_unread_count_title">"Display unread count"</string>
|
||||
<string name="display_all_counts_title">"Display count for favorite and read"</string>
|
||||
<string name="text_wrong_url">"You seem to be trying to use an invalid URL. Make sure it is correct, and if the problem persists, contact me (via the store contact link). Please note that the app needs you to be using Selfoss. You can't access RSS feeds without it."</string>
|
||||
<string name="pref_general_internal_browser_title">"Open links inside the app"</string>
|
||||
<string name="pref_general_internal_browser_on">"Articles will open inside the app"</string>
|
||||
<string name="pref_general_internal_browser_off">"Articles will open with your default browser"</string>
|
||||
<string name="prefer_article_viewer_title">"Use the article viewer"</string>
|
||||
<string name="prefer_article_viewer_on">"Will use the article viewer instead of the internal browser"</string>
|
||||
<string name="prefer_article_viewer_off">"Will use the internal browser instead of the article viewer"</string>
|
||||
<string name="pref_general_category_links">"Link handling"</string>
|
||||
<string name="pref_general_category_displaying">"Displaying"</string>
|
||||
<string name="pref_switch_card_view_on">"The articles will be displayed as cards"</string>
|
||||
<string name="pref_switch_card_view_off">"The articles will be displayed as a list"</string>
|
||||
<string name="menu_home_refresh">"Update remote"</string>
|
||||
<string name="refresh_success_response">"The remote is updated, you can now reload the articles list"</string>
|
||||
<string name="refresh_failer_message">"The update didn't work, try again later, or check your selfoss logs."</string>
|
||||
<string name="refresh_in_progress">"Refresh in progress"</string>
|
||||
<string name="card_height_title">Full height cards</string>
|
||||
<string name="card_height_on">Cards height will adjust to its content</string>
|
||||
<string name="card_height_off">Card height will be fixed</string>
|
||||
<string name="source_code">Source code</string>
|
||||
<string name="cant_mark_read">Can\'t mark article as read</string>
|
||||
<string name="cant_mark_unread">Can\'t mark article as unread</string>
|
||||
<string name="drawer_error_loading_tags">Error loading tags…</string>
|
||||
<string name="drawer_error_loading_sources">Error loading sources…</string>
|
||||
<string name="drawer_item_filters">Filters</string>
|
||||
<string name="drawer_action_clear">clear</string>
|
||||
<string name="drawer_item_tags">Tags</string>
|
||||
<string name="drawer_item_sources">Sources</string>
|
||||
<string name="drawer_action_edit">edit</string>
|
||||
<string name="no_tags_loaded">No tags loaded</string>
|
||||
<string name="no_sources_loaded">No sources loaded</string>
|
||||
<string name="drawer_loading">Loading …</string>
|
||||
<string name="menu_home_search">Search</string>
|
||||
<string name="can_delete_source">Can\'t delete the source…</string>
|
||||
<string name="base_url_error">There was an issue when trying to communicate with your Selfoss Instance. If the issue persists, please get in touch with me.</string>
|
||||
<string name="pref_header_theme">Themes</string>
|
||||
<string name="default_theme">Default</string>
|
||||
<string name="default_dark_theme">Default/Dark</string>
|
||||
<string name="pref_header_debug">Debug</string>
|
||||
<string name="self_hosted_cert_switch">Using a self hosted certificate ?</string>
|
||||
<string name="self_signed_cert_warning">Due to security reasons, self signed certificates are not supported by default. By activating this, I\'ll not be responsible of any security problem you encounter.</string>
|
||||
<string name="pref_selfoss_category">Selfoss Api</string>
|
||||
<string name="pref_api_items_number_title">Loaded items number</string>
|
||||
<string name="pref_hidden_tags">Hidden Tags</string>
|
||||
<string name="summary_debug_identifier">Debug identifier</string>
|
||||
<string name="unique_id_to_clipboard">Identifier copied to your clipboard</string>
|
||||
<string name="display_header_drawer_summary">Display a header with the selfoss instance url on the lateral drawer.</string>
|
||||
<string name="display_header_drawer_title">Account header</string>
|
||||
<string name="pref_general_infinite_loading_title">Load more articles on scroll</string>
|
||||
<string name="translation">Translation</string>
|
||||
<string name="cant_open_invalid_url">The item url is invalid. I\'m looking into solving this issue so the app won\'t crash.</string>
|
||||
<string name="drawer_report_bug">Report a bug</string>
|
||||
<string name="items_number_should_be_number">The items number should be an integer.</string>
|
||||
<string name="reader_action_more">Read more</string>
|
||||
<string name="reader_action_open">Open in browser</string>
|
||||
<string name="reader_action_share">Share</string>
|
||||
<string name="pref_switch_actions_pager_scroll_on">Mark articles as read when swiping between articles.</string>
|
||||
<string name="add_to_favs_reader">Add to favorites</string>
|
||||
<string name="remove_to_favs_reader">Remove from favorites</string>
|
||||
<string name="pref_content_reader_font_size">Article reader content font size</string>
|
||||
<string name="pref_header_viewer">Article viewer</string>
|
||||
<string name="refresh_dialog_message">This will refresh your Selfoss instance.</string>
|
||||
<string name="markall_dialog_message">This will mark all the items as read.</string>
|
||||
<string name="pref_switch_actions_pager_scroll">Mark as read on swipe</string>
|
||||
<string name="pref_switch_actions_pager_scroll_off">Don\'t mark articles as read when swiping.</string>
|
||||
<string name="pref_acra_alwaysaccept">Automatically send crash reports</string>
|
||||
<string name="pref_acra_alwaysaccept_enabled">Will send crash reports automatically</string>
|
||||
<string name="pref_acra_alwaysaccept_disabled">Will ask everytime when sending crash reports.</string>
|
||||
<string name="pref_debug_crash_reports">Crash reports</string>
|
||||
<string name="pref_debug_debug_logs">Debug logging (these will be sent without a dialog)</string>
|
||||
<string name="acra_login">Enable logging</string>
|
||||
<string name="drawer_item_hidden_tags">Hidden Tags</string>
|
||||
<string name="unmark">Mark item as unread</string>
|
||||
<string name="pref_header_offline">Offline and cache</string>
|
||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
||||
<string name="no_network_connectivity">Not connected !</string>
|
||||
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||
<string name="pref_switch_periodic_refresh_on">Articles will periodically be synced</string>
|
||||
<string name="pref_periodic_refresh_minutes_title"><![CDATA[Sync interval ( >= 15 minutes)]]></string>
|
||||
<string name="pref_switch_refresh_when_charging">Only refresh when phone is charging</string>
|
||||
<string name="loading_notification_title">Loading ...</string>
|
||||
<string name="loading_notification_text">Selfoss is syncing your articles</string>
|
||||
<string name="notification_channel_sync">Sync notification</string>
|
||||
<string name="new_items_channel_sync">New items notification</string>
|
||||
<string name="new_items_notification_title">New items !</string>
|
||||
<string name="new_items_notification_text">%1$d new items loaded.</string>
|
||||
<string name="pref_switch_notify_new_items">Notify on new items synced.</string>
|
||||
<string name="shortcut_offline">Offline</string>
|
||||
<string name="pref_api_timeout">Api Timeout</string>
|
||||
<string name="pref_header_experimental">Experimental</string>
|
||||
<string name="webview_dialog_issue_message">Webview not available. Disabling the article viewer to avoid any future crashes. Will load articles inside of your browser from now on.</string>
|
||||
<string name="webview_dialog_issue_title">Webview issue</string>
|
||||
<string name="reader_text_align_left">Align left</string>
|
||||
<string name="reader_text_align_justify">Justify</string>
|
||||
<string name="settings_reader_font">Reader font</string>
|
||||
</resources>
|
@ -139,6 +139,8 @@
|
||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
||||
<string name="no_network_connectivity">Not connected !</string>
|
||||
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||
|
@ -139,6 +139,8 @@
|
||||
<string name="pref_switch_items_caching_off">文章不会被保存到设备内存,应用程序在离线时将无法阅读它们</string>
|
||||
<string name="pref_switch_items_caching_on">文章将被保存到设备内存并可在离线时使用</string>
|
||||
<string name="pref_switch_items_caching">保存项目以便离线使用</string>
|
||||
<string name="pref_switch_update_sources">检查新来源和标签</string>
|
||||
<string name="pref_switch_update_sources_summary">如果你的服务器接收过多的数据库查询,请禁用此功能。</string>
|
||||
<string name="no_network_connectivity">未连接!</string>
|
||||
<string name="pref_switch_periodic_refresh">同步文章</string>
|
||||
<string name="pref_switch_periodic_refresh_off">文章将不会在后台同步</string>
|
||||
|
@ -139,6 +139,8 @@
|
||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
||||
<string name="no_network_connectivity">Not connected !</string>
|
||||
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||
|
@ -140,6 +140,8 @@
|
||||
<string name="pref_switch_items_caching_off">Articles won\'t be saved to the device memory, and the app won\'t be usable offline.</string>
|
||||
<string name="pref_switch_items_caching_on">Articles will be saved to the device memory and will be used for offline use.</string>
|
||||
<string name="pref_switch_items_caching">Save items for offline use</string>
|
||||
<string name="pref_switch_update_sources">Check for new sources and tags</string>
|
||||
<string name="pref_switch_update_sources_summary">Disable this if your server is receiving excessive amounts of database queries.</string>
|
||||
<string name="no_network_connectivity">Not connected !</string>
|
||||
<string name="pref_switch_periodic_refresh">Sync articles</string>
|
||||
<string name="pref_switch_periodic_refresh_off">Articles will not be synced in the background</string>
|
||||
|
@ -34,4 +34,10 @@
|
||||
android:key="notify_new_items"
|
||||
android:dependency="periodic_refresh"
|
||||
android:title="@string/pref_switch_notify_new_items" />
|
||||
|
||||
<SwitchPreference
|
||||
android:defaultValue="true"
|
||||
android:key="update_sources"
|
||||
android:summary="@string/pref_switch_update_sources_summary"
|
||||
android:title="@string/pref_switch_update_sources" />
|
||||
</PreferenceScreen>
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
buildscript {
|
||||
ext {
|
||||
kotlin_version = '1.3.31'
|
||||
kotlin_version = '1.4.21'
|
||||
android_version = '1.0.0'
|
||||
androidx_version = '1.1.0-alpha05'
|
||||
lifecycle_version = '2.2.0-alpha01'
|
||||
@ -17,7 +17,7 @@ buildscript {
|
||||
}
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:3.5.3'
|
||||
classpath 'com.android.tools.build:gradle:4.1.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
}
|
||||
}
|
||||
|
13
fastlane/metadata/android/en-US/full_description.txt
Normal file
@ -0,0 +1,13 @@
|
||||
A new RSS reader for <a href="http://selfoss.aditu.de/">selfoss</a>.
|
||||
|
||||
What it does:
|
||||
|
||||
<ul>
|
||||
<li>Fetches read, unread, and favorite feeds.</li>
|
||||
<li>Marking as read, marking as favorite.</li>
|
||||
<li>Manage selfoss sources from the app.</li>
|
||||
<li>Add an RSS feed from within the app, or by sharing a link from your browser.</li>
|
||||
<li>Choose between multiple light and dark themes.</li>
|
||||
</ul>
|
||||
|
||||
PS: It only works with Selfoss
|
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/1.png
Normal file
After Width: | Height: | Size: 266 KiB |
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/2.png
Normal file
After Width: | Height: | Size: 973 KiB |
BIN
fastlane/metadata/android/en-US/images/phoneScreenshots/3.png
Normal file
After Width: | Height: | Size: 457 KiB |
Before Width: | Height: | Size: 1.2 MiB After Width: | Height: | Size: 1.2 MiB |
Before Width: | Height: | Size: 422 KiB After Width: | Height: | Size: 422 KiB |
3
fastlane/metadata/android/en-US/short_description.txt
Normal file
@ -0,0 +1,3 @@
|
||||
A new RSS reader for <a href="http://selfoss.aditu.de/">selfoss</a>.
|
||||
|
||||
It connects to your selfoss instance (works only with selfoss, and can't work without it), and you'll be able to read and manage all your RSS feeds.
|
1
fastlane/metadata/android/en-US/title.txt
Normal file
@ -0,0 +1 @@
|
||||
Reader for Selfoss
|
4
gradle/wrapper/gradle-wrapper.properties
vendored
@ -1,6 +1,6 @@
|
||||
#Sat Feb 01 12:14:14 CET 2020
|
||||
#Sat Dec 12 19:38:31 CET 2020
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip
|
||||
|